dolibarr  16.0.1
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  *
22  * This program is free software; you can redistribute it and/or modify
23  * it under the terms of the GNU General Public License as published by
24  * the Free Software Foundation; either version 3 of the License, or
25  * (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program. If not, see <https://www.gnu.org/licenses/>.
34  * or see https://www.gnu.org/
35  */
36 
43 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
44 
45 
46 if (!function_exists('utf8_encode')) {
53  function utf8_encode($elements)
54  {
55  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
56  }
57 }
58 
59 if (!function_exists('utf8_decode')) {
66  function utf8_decode($elements)
67  {
68  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
69  }
70 }
71 
72 
79 function getDolGlobalString($key, $default = '')
80 {
81  global $conf;
82  // return $conf->global->$key ?? $default;
83  return (string) (empty($conf->global->$key) ? $default : $conf->global->$key);
84 }
85 
92 function getDolGlobalInt($key, $default = 0)
93 {
94  global $conf;
95  // return $conf->global->$key ?? $default;
96  return (int) (empty($conf->global->$key) ? $default : $conf->global->$key);
97 }
98 
104 function isModEnabled($module)
105 {
106  global $conf;
107  return !empty($conf->$module->enabled);
108 }
109 
121 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
122 {
123  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
124 
125  $class = 'DoliDB'.ucfirst($type);
126  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
127  return $dolidb;
128 }
129 
147 function getEntity($element, $shared = 1, $currentobject = null)
148 {
149  global $conf, $mc, $hookmanager, $object, $action, $db;
150 
151  if (! is_object($hookmanager)) {
152  $hookmanager = new HookManager($db);
153  }
154 
155  // fix different element names (France to English)
156  switch ($element) {
157  case 'contrat':
158  $element = 'contract';
159  break; // "/contrat/class/contrat.class.php"
160  case 'order_supplier':
161  $element = 'supplier_order';
162  break; // "/fourn/class/fournisseur.commande.class.php"
163  case 'invoice_supplier':
164  $element = 'supplier_invoice';
165  break; // "/fourn/class/fournisseur.facture.class.php"
166  }
167 
168  if (is_object($mc)) {
169  $out = $mc->getEntity($element, $shared, $currentobject);
170  } else {
171  $out = '';
172  $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
173  if (in_array($element, $addzero)) {
174  $out .= '0,';
175  }
176  $out .= ((int) $conf->entity);
177  }
178 
179  // Manipulate entities to query on the fly
180  $parameters = array(
181  'element' => $element,
182  'shared' => $shared,
183  'object' => $object,
184  'currentobject' => $currentobject,
185  'out' => $out
186  );
187  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
188 
189  if (is_numeric($reshook)) {
190  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
191  $out .= ','.$hookmanager->resPrint; // add
192  } elseif ($reshook == 1) {
193  $out = $hookmanager->resPrint; // replace
194  }
195  }
196 
197  return $out;
198 }
199 
206 function setEntity($currentobject)
207 {
208  global $conf, $mc;
209 
210  if (is_object($mc) && method_exists($mc, 'setEntity')) {
211  return $mc->setEntity($currentobject);
212  } else {
213  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
214  }
215 }
216 
223 function isASecretKey($keyname)
224 {
225  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
226 }
227 
228 
235 function num2Alpha($n)
236 {
237  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
238  $r = chr($n % 26 + 0x41) . $r;
239  return $r;
240 }
241 
242 
259 function getBrowserInfo($user_agent)
260 {
261  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
262 
263  $name = 'unknown';
264  $version = '';
265  $os = 'unknown';
266  $phone = '';
267 
268  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
269 
270  $detectmobile = new Mobile_Detect(null, $user_agent);
271  $tablet = $detectmobile->isTablet();
272 
273  if ($detectmobile->isMobile()) {
274  $phone = 'unknown';
275 
276  // If phone/smartphone, we set phone os name.
277  if ($detectmobile->is('AndroidOS')) {
278  $os = $phone = 'android';
279  } elseif ($detectmobile->is('BlackBerryOS')) {
280  $os = $phone = 'blackberry';
281  } elseif ($detectmobile->is('iOS')) {
282  $os = 'ios';
283  $phone = 'iphone';
284  } elseif ($detectmobile->is('PalmOS')) {
285  $os = $phone = 'palm';
286  } elseif ($detectmobile->is('SymbianOS')) {
287  $os = 'symbian';
288  } elseif ($detectmobile->is('webOS')) {
289  $os = 'webos';
290  } elseif ($detectmobile->is('MaemoOS')) {
291  $os = 'maemo';
292  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
293  $os = 'windows';
294  }
295  }
296 
297  // OS
298  if (preg_match('/linux/i', $user_agent)) {
299  $os = 'linux';
300  } elseif (preg_match('/macintosh/i', $user_agent)) {
301  $os = 'macintosh';
302  } elseif (preg_match('/windows/i', $user_agent)) {
303  $os = 'windows';
304  }
305 
306  // Name
307  $reg = array();
308  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
309  $name = 'firefox';
310  $version = $reg[2];
311  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
312  $name = 'edge';
313  $version = $reg[2];
314  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
315  $name = 'chrome';
316  $version = $reg[2];
317  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
318  // we can have 'chrome (Mozilla...) chrome x.y' in one string
319  $name = 'chrome';
320  } elseif (preg_match('/iceweasel/i', $user_agent)) {
321  $name = 'iceweasel';
322  } elseif (preg_match('/epiphany/i', $user_agent)) {
323  $name = 'epiphany';
324  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
325  $name = 'safari';
326  $version = $reg[2];
327  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
328  // Safari is often present in string for mobile but its not.
329  $name = 'opera';
330  $version = $reg[2];
331  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
332  $name = 'ie';
333  $version = end($reg);
334  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
335  // MS products at end
336  $name = 'ie';
337  $version = end($reg);
338  } elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
339  // MS products at end
340  $name = 'lynxlinks';
341  $version = $reg[4];
342  }
343 
344  if ($tablet) {
345  $layout = 'tablet';
346  } elseif ($phone) {
347  $layout = 'phone';
348  } else {
349  $layout = 'classic';
350  }
351 
352  return array(
353  'browsername' => $name,
354  'browserversion' => $version,
355  'browseros' => $os,
356  'layout' => $layout,
357  'phone' => $phone,
358  'tablet' => $tablet
359  );
360 }
361 
367 function dol_shutdown()
368 {
369  global $conf, $user, $langs, $db;
370  $disconnectdone = false;
371  $depth = 0;
372  if (is_object($db) && !empty($db->connected)) {
373  $depth = $db->transaction_opened;
374  $disconnectdone = $db->close();
375  }
376  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
377 }
378 
385 function GETPOSTISSET($paramname)
386 {
387  $isset = false;
388 
389  $relativepathstring = $_SERVER["PHP_SELF"];
390  // Clean $relativepathstring
391  if (constant('DOL_URL_ROOT')) {
392  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
393  }
394  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
395  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
396  //var_dump($relativepathstring);
397  //var_dump($user->default_values);
398 
399  // Code for search criteria persistence.
400  // Retrieve values if restore_lastsearch_values
401  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
402  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
403  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
404  if (is_array($tmp)) {
405  foreach ($tmp as $key => $val) {
406  if ($key == $paramname) { // We are on the requested parameter
407  $isset = true;
408  break;
409  }
410  }
411  }
412  }
413  // If there is saved contextpage, limit, page or mode
414  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
415  $isset = true;
416  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
417  $isset = true;
418  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
419  $isset = true;
420  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
421  $isset = true;
422  }
423  } else {
424  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
425  }
426 
427  return $isset;
428 }
429 
438 function GETPOSTISARRAY($paramname, $method = 0)
439 {
440  // for $method test need return the same $val as GETPOST
441  if (empty($method)) {
442  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
443  } elseif ($method == 1) {
444  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
445  } elseif ($method == 2) {
446  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
447  } elseif ($method == 3) {
448  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
449  } else {
450  $val = 'BadFirstParameterForGETPOST';
451  }
452 
453  return is_array($val);
454 }
455 
483 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
484 {
485  global $mysoc, $user, $conf;
486 
487  if (empty($paramname)) {
488  return 'BadFirstParameterForGETPOST';
489  }
490  if (empty($check)) {
491  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
492  // Enable this line to know who call the GETPOST with '' $check parameter.
493  //var_dump(debug_backtrace()[0]);
494  }
495 
496  if (empty($method)) {
497  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
498  } elseif ($method == 1) {
499  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
500  } elseif ($method == 2) {
501  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
502  } elseif ($method == 3) {
503  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
504  } else {
505  return 'BadThirdParameterForGETPOST';
506  }
507 
508  if (empty($method) || $method == 3 || $method == 4) {
509  $relativepathstring = $_SERVER["PHP_SELF"];
510  // Clean $relativepathstring
511  if (constant('DOL_URL_ROOT')) {
512  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
513  }
514  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
515  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
516  //var_dump($relativepathstring);
517  //var_dump($user->default_values);
518 
519  // Code for search criteria persistence.
520  // Retrieve values if restore_lastsearch_values
521  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
522  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
523  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
524  if (is_array($tmp)) {
525  foreach ($tmp as $key => $val) {
526  if ($key == $paramname) { // We are on the requested parameter
527  $out = $val;
528  break;
529  }
530  }
531  }
532  }
533  // If there is saved contextpage, page or limit
534  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
535  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
536  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
537  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
538  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
539  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
540  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
541  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
542  }
543  } elseif (!isset($_GET['sortfield'])) {
544  // Else, retrieve default values if we are not doing a sort
545  // 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
546  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
547  // Search default value from $object->field
548  global $object;
549  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
550  $out = $object->fields[$paramname]['default'];
551  }
552  }
553  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
554  if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
555  // Now search in setup to overwrite default values
556  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
557  if (isset($user->default_values[$relativepathstring]['createform'])) {
558  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
559  $qualified = 0;
560  if ($defkey != '_noquery_') {
561  $tmpqueryarraytohave = explode('&', $defkey);
562  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
563  $foundintru = 0;
564  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
565  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
566  $foundintru = 1;
567  }
568  }
569  if (!$foundintru) {
570  $qualified = 1;
571  }
572  //var_dump($defkey.'-'.$qualified);
573  } else {
574  $qualified = 1;
575  }
576 
577  if ($qualified) {
578  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
579  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
580  break;
581  }
582  }
583  }
584  }
585  }
586  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
587  // Management of default search_filters and sort order
588  if (!empty($user->default_values)) {
589  // $user->default_values defined from menu 'Setup - Default values'
590  //var_dump($user->default_values[$relativepathstring]);
591  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
592  // Sorted on which fields ? ASC or DESC ?
593  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
594  // Even if paramname is sortfield, data are stored into ['sortorder...']
595  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
596  $qualified = 0;
597  if ($defkey != '_noquery_') {
598  $tmpqueryarraytohave = explode('&', $defkey);
599  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
600  $foundintru = 0;
601  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
602  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
603  $foundintru = 1;
604  }
605  }
606  if (!$foundintru) {
607  $qualified = 1;
608  }
609  //var_dump($defkey.'-'.$qualified);
610  } else {
611  $qualified = 1;
612  }
613 
614  if ($qualified) {
615  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
616  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
617  if ($out) {
618  $out .= ', ';
619  }
620  if ($paramname == 'sortfield') {
621  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
622  }
623  if ($paramname == 'sortorder') {
624  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
625  }
626  }
627  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
628  }
629  }
630  }
631  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
632  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
633  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
634  continue;
635  }
636  $qualified = 0;
637  if ($defkey != '_noquery_') {
638  $tmpqueryarraytohave = explode('&', $defkey);
639  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
640  $foundintru = 0;
641  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
642  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
643  $foundintru = 1;
644  }
645  }
646  if (!$foundintru) {
647  $qualified = 1;
648  }
649  //var_dump($defkey.'-'.$qualified);
650  } else {
651  $qualified = 1;
652  }
653 
654  if ($qualified) {
655  // We must keep $_POST and $_GET here
656  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
657  // We made a search from quick search menu, do we still use default filter ?
658  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
659  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
660  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
661  }
662  } else {
663  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
664  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
665  }
666  break;
667  }
668  }
669  }
670  }
671  }
672  }
673  }
674  }
675 
676  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
677  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
678  // 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.
679  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
680  $reg = array();
681  $maxloop = 20;
682  $loopnb = 0; // Protection against infinite loop
683  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.
684  $loopnb++;
685  $newout = '';
686 
687  if ($reg[1] == 'DAY') {
688  $tmp = dol_getdate(dol_now(), true);
689  $newout = $tmp['mday'];
690  } elseif ($reg[1] == 'MONTH') {
691  $tmp = dol_getdate(dol_now(), true);
692  $newout = $tmp['mon'];
693  } elseif ($reg[1] == 'YEAR') {
694  $tmp = dol_getdate(dol_now(), true);
695  $newout = $tmp['year'];
696  } elseif ($reg[1] == 'PREVIOUS_DAY') {
697  $tmp = dol_getdate(dol_now(), true);
698  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
699  $newout = $tmp2['day'];
700  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
701  $tmp = dol_getdate(dol_now(), true);
702  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
703  $newout = $tmp2['month'];
704  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
705  $tmp = dol_getdate(dol_now(), true);
706  $newout = ($tmp['year'] - 1);
707  } elseif ($reg[1] == 'NEXT_DAY') {
708  $tmp = dol_getdate(dol_now(), true);
709  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
710  $newout = $tmp2['day'];
711  } elseif ($reg[1] == 'NEXT_MONTH') {
712  $tmp = dol_getdate(dol_now(), true);
713  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
714  $newout = $tmp2['month'];
715  } elseif ($reg[1] == 'NEXT_YEAR') {
716  $tmp = dol_getdate(dol_now(), true);
717  $newout = ($tmp['year'] + 1);
718  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
719  $newout = $mysoc->country_id;
720  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
721  $newout = $user->id;
722  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
723  $newout = $user->fk_user;
724  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
725  $newout = $conf->entity;
726  } else {
727  $newout = ''; // Key not found, we replace with empty string
728  }
729  //var_dump('__'.$reg[1].'__ -> '.$newout);
730  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
731  }
732  }
733 
734  // Check rule
735  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
736  if (!is_array($out) || empty($out)) {
737  $out = array();
738  } else {
739  $tmparray = explode(':', $check);
740  if (!empty($tmparray[1])) {
741  $tmpcheck = $tmparray[1];
742  } else {
743  $tmpcheck = 'alphanohtml';
744  }
745  foreach ($out as $outkey => $outval) {
746  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
747  }
748  }
749  } else {
750  $out = sanitizeVal($out, $check, $filter, $options);
751  }
752 
753  // Sanitizing for special parameters.
754  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
755  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
756  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
757  $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.
758  do {
759  $oldstringtoclean = $out;
760  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
761  $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'
762  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
763  } while ($oldstringtoclean != $out);
764  }
765 
766  // Code for search criteria persistence.
767  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
768  if (empty($method) || $method == 3 || $method == 4) {
769  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
770  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
771 
772  // We save search key only if $out not empty that means:
773  // - posted value not empty, or
774  // - 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).
775 
776  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
777  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
778  }
779  }
780  }
781 
782  return $out;
783 }
784 
794 function GETPOSTINT($paramname, $method = 0)
795 {
796  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
797 }
798 
799 
810 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
811 {
812  return sanitizeVal($out, $check, $filter, $options);
813 }
814 
824 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
825 {
826  global $conf;
827 
828  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
829  // Check is done after replacement
830  switch ($check) {
831  case 'none':
832  break;
833  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
834  if (!is_numeric($out)) {
835  $out = '';
836  }
837  break;
838  case 'intcomma':
839  if (preg_match('/[^0-9,-]+/i', $out)) {
840  $out = '';
841  }
842  break;
843  case 'san_alpha':
844  $out = filter_var($out, FILTER_SANITIZE_STRING);
845  break;
846  case 'email':
847  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
848  break;
849  case 'aZ':
850  if (!is_array($out)) {
851  $out = trim($out);
852  if (preg_match('/[^a-z]+/i', $out)) {
853  $out = '';
854  }
855  }
856  break;
857  case 'aZ09':
858  if (!is_array($out)) {
859  $out = trim($out);
860  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
861  $out = '';
862  }
863  }
864  break;
865  case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
866  if (!is_array($out)) {
867  $out = trim($out);
868  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
869  $out = '';
870  }
871  }
872  break;
873  case 'nohtml': // No html
874  $out = dol_string_nohtmltag($out, 0);
875  break;
876  case 'alpha': // No html and no ../ and "
877  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
878  if (!is_array($out)) {
879  $out = trim($out);
880  do {
881  $oldstringtoclean = $out;
882  // Remove html tags
883  $out = dol_string_nohtmltag($out, 0);
884  // Remove also other dangerous string sequences
885  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
886  // '../' or '..\' is dangerous because it allows dir transversals
887  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
888  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
889  } while ($oldstringtoclean != $out);
890  // keep lines feed
891  }
892  break;
893  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'
894  if (!is_array($out)) {
895  $out = trim($out);
896  do {
897  $oldstringtoclean = $out;
898  // Remove html tags
899  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
900  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
901  // '../' or '..\' is dangerous because it allows dir transversals
902  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
903  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
904  } while ($oldstringtoclean != $out);
905  }
906  break;
907  case 'restricthtml': // Recommended for most html textarea
908  case 'restricthtmlallowunvalid':
909  do {
910  $oldstringtoclean = $out;
911 
912  if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') {
913  try {
914  $dom = new DOMDocument;
915  // Add a trick to solve pb with text without parent tag
916  // like '<h1>Foo</h1><p>bar</p>' that ends up with '<h1>Foo<p>bar</p></h1>'
917  // like 'abc' that ends up with '<p>abc</p>'
918  $out = '<div class="tricktoremove">'.$out.'</div>';
919 
920  $dom->loadHTML($out, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
921  $out = trim($dom->saveHTML());
922 
923  // Remove the trick added to solve pb with text without parent tag
924  $out = preg_replace('/^<div class="tricktoremove">/', '', $out);
925  $out = preg_replace('/<\/div>$/', '', $out);
926  } catch (Exception $e) {
927  //print $e->getMessage();
928  return 'InvalidHTMLString';
929  }
930  }
931 
932  // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are
933  // encoded using text entities) so we can then exclude all numeric entities.
934  $out = preg_replace('/&#39;/i', '&apos;', $out);
935 
936  // 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).
937  // 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
938  // using a non coventionnel way to be encoded, to not have them sanitized just after)
939  //$out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', 'realCharForNumericEntities', $out);
940  $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', function ($m) {
941  return realCharForNumericEntities($m); }, $out);
942 
943 
944  // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
945  $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'.
946 
947  $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
948 
949  // We should also exclude non expected HTML attributes and clean content of some attributes.
950  if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) {
951  // 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.
953  }
954 
955  // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
956  $out = preg_replace('/&apos;/i', "&#39;", $out);
957  } while ($oldstringtoclean != $out);
958  break;
959  case 'custom':
960  if (empty($filter)) {
961  return 'BadFourthParameterForGETPOST';
962  }
963  $out = filter_var($out, $filter, $options);
964  break;
965  }
966 
967  return $out;
968 }
969 
970 
971 if (!function_exists('dol_getprefix')) {
981  function dol_getprefix($mode = '')
982  {
983  // If prefix is for email (we need to have $conf already loaded for this case)
984  if ($mode == 'email') {
985  global $conf;
986 
987  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
988  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
989  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
990  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
991  return $_SERVER["SERVER_NAME"];
992  }
993  }
994 
995  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
996  if (!empty($conf->file->instance_unique_id)) {
997  return sha1('dolibarr'.$conf->file->instance_unique_id);
998  }
999 
1000  // For backward compatibility when instance_unique_id is not set
1001  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1002  }
1003 
1004  // If prefix is for session (no need to have $conf loaded)
1005  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1006  $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
1007 
1008  // The recommended value (may be not defined for old versions)
1009  if (!empty($tmp_instance_unique_id)) {
1010  return sha1('dolibarr'.$tmp_instance_unique_id);
1011  }
1012 
1013  // For backward compatibility when instance_unique_id is not set
1014  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1015  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1016  } else {
1017  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1018  }
1019  }
1020 }
1021 
1032 function dol_include_once($relpath, $classname = '')
1033 {
1034  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']
1035 
1036  $fullpath = dol_buildpath($relpath);
1037 
1038  if (!file_exists($fullpath)) {
1039  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1040  return false;
1041  }
1042 
1043  if (!empty($classname) && !class_exists($classname)) {
1044  return include $fullpath;
1045  } else {
1046  return include_once $fullpath;
1047  }
1048 }
1049 
1050 
1061 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1062 {
1063  global $conf;
1064 
1065  $path = preg_replace('/^\//', '', $path);
1066 
1067  if (empty($type)) { // For a filesystem path
1068  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1069  if (is_array($conf->file->dol_document_root)) {
1070  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1071  if ($key == 'main') {
1072  continue;
1073  }
1074  if (file_exists($dirroot.'/'.$path)) {
1075  $res = $dirroot.'/'.$path;
1076  return $res;
1077  }
1078  }
1079  }
1080  if ($returnemptyifnotfound) {
1081  // Not found into alternate dir
1082  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1083  return '';
1084  }
1085  }
1086  } else {
1087  // For an url path
1088  // We try to get local path of file on filesystem from url
1089  // Note that trying to know if a file on disk exist by forging path on disk from url
1090  // works only for some web server and some setup. This is bugged when
1091  // using proxy, rewriting, virtual path, etc...
1092  $res = '';
1093  if ($type == 1) {
1094  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1095  }
1096  if ($type == 2) {
1097  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1098  }
1099  if ($type == 3) {
1100  $res = DOL_URL_ROOT.'/'.$path;
1101  }
1102 
1103  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1104  if ($key == 'main') {
1105  if ($type == 3) {
1106  global $dolibarr_main_url_root;
1107 
1108  // Define $urlwithroot
1109  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1110  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1111  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1112 
1113  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1114  }
1115  continue;
1116  }
1117  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1118  if (!empty($regs[1])) {
1119  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1120  if (file_exists($dirroot.'/'.$regs[1])) {
1121  if ($type == 1) {
1122  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1123  }
1124  if ($type == 2) {
1125  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1126  }
1127  if ($type == 3) {
1128  global $dolibarr_main_url_root;
1129 
1130  // Define $urlwithroot
1131  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1132  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1133  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1134 
1135  $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
1136  }
1137  break;
1138  }
1139  }
1140  }
1141  }
1142 
1143  return $res;
1144 }
1145 
1156 function dol_clone($object, $native = 0)
1157 {
1158  if (empty($native)) {
1159  $tmpsavdb = null;
1160  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1161  $tmpsavdb = $object->db;
1162  unset($object->db); // Such property can not be serialized when PgSql/Connection
1163  }
1164 
1165  $myclone = unserialize(serialize($object)); // serialize then unserialize is hack to be sure to have a new object for all fields
1166 
1167  if ($tmpsavdb) {
1168  $object->db = $tmpsavdb;
1169  }
1170  } else {
1171  $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)
1172  }
1173 
1174  return $myclone;
1175 }
1176 
1186 function dol_size($size, $type = '')
1187 {
1188  global $conf;
1189  if (empty($conf->dol_optimize_smallscreen)) {
1190  return $size;
1191  }
1192  if ($type == 'width' && $size > 250) {
1193  return 250;
1194  } else {
1195  return 10;
1196  }
1197 }
1198 
1199 
1211 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1212 {
1213  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1214  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1215  // Char '/' and '\' are file delimiters.
1216  // 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
1217  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';');
1218  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1219  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1220  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1221  $tmp = str_replace('..', '', $tmp);
1222  return $tmp;
1223 }
1224 
1236 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1237 {
1238  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1239  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1240  // 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
1241  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';');
1242  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1243  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1244  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1245  $tmp = str_replace('..', '', $tmp);
1246  return $tmp;
1247 }
1248 
1256 function dol_sanitizeUrl($stringtoclean, $type = 1)
1257 {
1258  // 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)
1259  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1260  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1261  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1262  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1263 
1264  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1265  if ($type == 1) {
1266  // removing : should disable links to external url like http:aaa)
1267  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1268  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1269  }
1270 
1271  do {
1272  $oldstringtoclean = $stringtoclean;
1273  // removing '&colon' should disable links to external url like http:aaa)
1274  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1275  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1276  } while ($oldstringtoclean != $stringtoclean);
1277 
1278  if ($type == 1) {
1279  // removing '//' should disable links to external url like //aaa or http//)
1280  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1281  }
1282 
1283  return $stringtoclean;
1284 }
1285 
1294 function dol_string_unaccent($str)
1295 {
1296  global $conf;
1297 
1298  if (utf8_check($str)) {
1299  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1300  $transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
1301  return $transliterator->transliterate($str);
1302  }
1303  // See http://www.utf8-chartable.de/
1304  $string = rawurlencode($str);
1305  $replacements = array(
1306  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1307  '%C3%87' => 'C',
1308  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1309  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1310  '%C3%91' => 'N',
1311  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1312  '%C5%A0' => 'S',
1313  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1314  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1315  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1316  '%C3%A7' => 'c',
1317  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1318  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1319  '%C3%B1' => 'n',
1320  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1321  '%C5%A1' => 's',
1322  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1323  '%C3%BD' => 'y', '%C3%BF' => 'y'
1324  );
1325  $string = strtr($string, $replacements);
1326  return rawurldecode($string);
1327  } else {
1328  // See http://www.ascii-code.com/
1329  $string = strtr(
1330  $str,
1331  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1332  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1333  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1334  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1335  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1336  \xF9\xFA\xFB\xFC\xFD\xFF",
1337  "AAAAAAC
1338  EEEEIIIIDN
1339  OOOOOUUUY
1340  aaaaaaceeee
1341  iiiidnooooo
1342  uuuuyy"
1343  );
1344  $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"));
1345  return $string;
1346  }
1347 }
1348 
1361 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '')
1362 {
1363  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName
1364  $forbidden_chars_to_remove = array();
1365  //$forbidden_chars_to_remove=array("(",")");
1366 
1367  if (is_array($badcharstoreplace)) {
1368  $forbidden_chars_to_replace = $badcharstoreplace;
1369  }
1370  if (is_array($badcharstoremove)) {
1371  $forbidden_chars_to_remove = $badcharstoremove;
1372  }
1373 
1374  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1375 }
1376 
1377 
1391 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1392 {
1393  if ($removetabcrlf) {
1394  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1395  } else {
1396  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
1397  }
1398 }
1399 
1408 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1409 {
1410  // escape quotes and backslashes, newlines, etc.
1411  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1412  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1413  if (empty($noescapebackslashn)) {
1414  $substitjs["\n"] = '\\n';
1415  $substitjs['\\'] = '\\\\';
1416  }
1417  if (empty($mode)) {
1418  $substitjs["'"] = "\\'";
1419  $substitjs['"'] = "\\'";
1420  } elseif ($mode == 1) {
1421  $substitjs["'"] = "\\'";
1422  } elseif ($mode == 2) {
1423  $substitjs['"'] = '\\"';
1424  } elseif ($mode == 3) {
1425  $substitjs["'"] = "\\'";
1426  $substitjs['"'] = "\\\"";
1427  }
1428  return strtr($stringtoescape, $substitjs);
1429 }
1430 
1437 function dol_escape_json($stringtoescape)
1438 {
1439  return str_replace('"', '\"', $stringtoescape);
1440 }
1441 
1453 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0)
1454 {
1455  if ($noescapetags == 'common') {
1456  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1457  }
1458 
1459  // escape quotes and backslashes, newlines, etc.
1460  if ($escapeonlyhtmltags) {
1461  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1462  } else {
1463  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1464  }
1465  if (!$keepb) {
1466  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1467  }
1468  if (!$keepn) {
1469  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1470  }
1471 
1472  if ($escapeonlyhtmltags) {
1473  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1474  } else {
1475  // Escape tags to keep
1476  // TODO Does not works yet when there is attributes to tag
1477  $tmparrayoftags = array();
1478  if ($noescapetags) {
1479  $tmparrayoftags = explode(',', $noescapetags);
1480  }
1481  if (count($tmparrayoftags)) {
1482  foreach ($tmparrayoftags as $tagtoreplace) {
1483  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1484  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1485  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1486  }
1487  }
1488 
1489  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1490 
1491  if (count($tmparrayoftags)) {
1492  foreach ($tmparrayoftags as $tagtoreplace) {
1493  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1494  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1495  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1496  }
1497  }
1498 
1499  return $result;
1500  }
1501 }
1502 
1510 function dol_strtolower($string, $encoding = "UTF-8")
1511 {
1512  if (function_exists('mb_strtolower')) {
1513  return mb_strtolower($string, $encoding);
1514  } else {
1515  return strtolower($string);
1516  }
1517 }
1518 
1526 function dol_strtoupper($string, $encoding = "UTF-8")
1527 {
1528  if (function_exists('mb_strtoupper')) {
1529  return mb_strtoupper($string, $encoding);
1530  } else {
1531  return strtoupper($string);
1532  }
1533 }
1534 
1542 function dol_ucfirst($string, $encoding = "UTF-8")
1543 {
1544  if (function_exists('mb_substr')) {
1545  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1546  } else {
1547  return ucfirst($string);
1548  }
1549 }
1550 
1558 function dol_ucwords($string, $encoding = "UTF-8")
1559 {
1560  if (function_exists('mb_convert_case')) {
1561  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1562  } else {
1563  return ucwords($string);
1564  }
1565 }
1566 
1588 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1589 {
1590  global $conf, $user, $debugbar;
1591 
1592  // If syslog module enabled
1593  if (empty($conf->syslog->enabled)) {
1594  return;
1595  }
1596 
1597  // Check if we are into execution of code of a website
1598  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1599  global $website, $websitekey;
1600  if (is_object($website) && !empty($website->ref)) {
1601  $suffixinfilename .= '_website_'.$website->ref;
1602  } elseif (!empty($websitekey)) {
1603  $suffixinfilename .= '_website_'.$websitekey;
1604  }
1605  }
1606 
1607  // Check if we have a forced suffix
1608  if (defined('USESUFFIXINLOG')) {
1609  $suffixinfilename .= constant('USESUFFIXINLOG');
1610  }
1611 
1612  if ($ident < 0) {
1613  foreach ($conf->loghandlers as $loghandlerinstance) {
1614  $loghandlerinstance->setIdent($ident);
1615  }
1616  }
1617 
1618  if (!empty($message)) {
1619  // Test log level
1620  $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');
1621  if (!array_key_exists($level, $logLevels)) {
1622  throw new Exception('Incorrect log level');
1623  }
1624  if ($level > $conf->global->SYSLOG_LEVEL) {
1625  return;
1626  }
1627 
1628  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1629  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1630  }
1631 
1632  // If adding log inside HTML page is required
1633  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1634  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1635  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1636  }
1637 
1638  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1639  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1640  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1641  print "\n\n<!-- Log start\n";
1642  print dol_escape_htmltag($message)."\n";
1643  print "Log end -->\n";
1644  }
1645 
1646  $data = array(
1647  'message' => $message,
1648  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1649  'level' => $level,
1650  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1651  'ip' => false
1652  );
1653 
1654  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1655  if (!empty($remoteip)) {
1656  $data['ip'] = $remoteip;
1657  // This is when server run behind a reverse proxy
1658  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1659  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1660  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1661  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1662  }
1663  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1664  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1665  $data['ip'] = $_SERVER['SERVER_ADDR'];
1666  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1667  // 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).
1668  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1669  } elseif (!empty($_SERVER['LOGNAME'])) {
1670  // 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).
1671  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1672  }
1673 
1674  // Loop on each log handler and send output
1675  foreach ($conf->loghandlers as $loghandlerinstance) {
1676  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1677  continue;
1678  }
1679  $loghandlerinstance->export($data, $suffixinfilename);
1680  }
1681  unset($data);
1682  }
1683 
1684  if ($ident > 0) {
1685  foreach ($conf->loghandlers as $loghandlerinstance) {
1686  $loghandlerinstance->setIdent($ident);
1687  }
1688  }
1689 }
1690 
1705 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'button bordertransp', $backtopagejsfields = '')
1706 {
1707  if (strpos($url, '?') > 0) {
1708  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1709  } else {
1710  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1711  }
1712 
1713  $out = '';
1714 
1715  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1716  if ($backtopagejsfields) {
1717  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1718  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1719  $backtopagejsfields = $name.":".$backtopagejsfields;
1720  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1721  } else {
1722  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1723  }
1724  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1725  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1726  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1727  }
1728 
1729  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1730  $out .= '<!-- a link for button to open url into a dialog popup backtopagejsfields = '.$backtopagejsfields.' -->'."\n";
1731  $out .= '<a class="cursorpointer button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'">'.$buttonstring.'</a>';
1732  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1733  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1734  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1735  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1736  $out .= '<script type="text/javascript">
1737  jQuery(document).ready(function () {
1738  jQuery(".button_'.$name.'").click(function () {
1739  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1740  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1741  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1742  $tmpdialog.dialog({
1743  autoOpen: false,
1744  modal: true,
1745  height: (window.innerHeight - 150),
1746  width: \'80%\',
1747  title: \''.dol_escape_js($label).'\',
1748  open: function (event, ui) {
1749  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1750  },
1751  close: function (event, ui) {
1752  returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1753  returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1754  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1755  if (returnedid != "" && returnedid != "div for returned id") {
1756  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1757  }
1758  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1759  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1760  }
1761  }
1762  });
1763 
1764  $tmpdialog.dialog(\'open\');
1765  });
1766  });
1767  </script>';
1768  return $out;
1769 }
1770 
1787 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1788 {
1789  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1790 }
1791 
1807 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1808 {
1809  global $conf, $langs, $hookmanager;
1810 
1811  // Show title
1812  $showtitle = 1;
1813  if (!empty($conf->dol_optimize_smallscreen)) {
1814  $showtitle = 0;
1815  }
1816 
1817  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1818 
1819  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1820  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1821  }
1822 
1823  // Show right part
1824  if ($morehtmlright) {
1825  $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.
1826  }
1827 
1828  // Show title
1829  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1830  $limittitle = 30;
1831  $out .= '<a class="tabTitle">';
1832  if ($picto) {
1833  $noprefix = $pictoisfullpath;
1834  if (strpos($picto, 'fontawesome_') !== false) {
1835  $noprefix = 1;
1836  }
1837  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
1838  }
1839  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
1840  $out .= '</a>';
1841  }
1842 
1843  // Show tabs
1844 
1845  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1846  $maxkey = -1;
1847  if (is_array($links) && !empty($links)) {
1848  $keys = array_keys($links);
1849  if (count($keys)) {
1850  $maxkey = max($keys);
1851  }
1852  }
1853 
1854  // Show tabs
1855  // if =0 we don't use the feature
1856  if (empty($limittoshow)) {
1857  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
1858  }
1859  if (!empty($conf->dol_optimize_smallscreen)) {
1860  $limittoshow = 2;
1861  }
1862 
1863  $displaytab = 0;
1864  $nbintab = 0;
1865  $popuptab = 0;
1866  $outmore = '';
1867  for ($i = 0; $i <= $maxkey; $i++) {
1868  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1869  // If active tab is already present
1870  if ($i >= $limittoshow) {
1871  $limittoshow--;
1872  }
1873  }
1874  }
1875 
1876  for ($i = 0; $i <= $maxkey; $i++) {
1877  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1878  $isactive = true;
1879  } else {
1880  $isactive = false;
1881  }
1882 
1883  if ($i < $limittoshow || $isactive) {
1884  // Output entry with a visible tab
1885  $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])).' -->';
1886 
1887  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1888  if (!empty($links[$i][0])) {
1889  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1890  } else {
1891  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1892  }
1893  } elseif (!empty($links[$i][1])) {
1894  //print "x $i $active ".$links[$i][2]." z";
1895  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
1896  if (!empty($links[$i][0])) {
1897  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
1898  $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).'">';
1899  }
1900  $out .= $links[$i][1];
1901  if (!empty($links[$i][0])) {
1902  $out .= '</a>'."\n";
1903  }
1904  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
1905  $out .= '</div>';
1906  }
1907 
1908  $out .= '</div>';
1909  } else {
1910  // Add entry into the combo popup with the other tabs
1911  if (!$popuptab) {
1912  $popuptab = 1;
1913  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1914  }
1915  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
1916  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1917  if (!empty($links[$i][0])) {
1918  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1919  } else {
1920  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1921  }
1922  } elseif (!empty($links[$i][1])) {
1923  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1924  $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.
1925  $outmore .= '</a>'."\n";
1926  }
1927  $outmore .= '</div>';
1928 
1929  $nbintab++;
1930  }
1931  $displaytab = $i;
1932  }
1933  if ($popuptab) {
1934  $outmore .= '</div>';
1935  }
1936 
1937  if ($popuptab) { // If there is some tabs not shown
1938  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
1939  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
1940  $widthofpopup = 200;
1941 
1942  $tabsname = $moretabssuffix;
1943  if (empty($tabsname)) {
1944  $tabsname = str_replace("@", "", $picto);
1945  }
1946  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
1947  $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".
1948  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
1949  $out .= $outmore;
1950  $out .= '</div>';
1951  $out .= '<div></div>';
1952  $out .= "</div>\n";
1953 
1954  $out .= "<script>";
1955  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
1956  var x = this.offsetLeft, y = this.offsetTop;
1957  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
1958  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
1959  $('#moretabsList".$tabsname."').css('".$right."','8px');
1960  }
1961  $('#moretabsList".$tabsname."').css('".$left."','auto');
1962  });
1963  ";
1964  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
1965  $out .= "</script>";
1966  }
1967 
1968  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1969  $out .= "</div>\n";
1970  }
1971 
1972  if (!$notab || $notab == -1 || $notab == -2) {
1973  $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ' tabBarWithBottom')).'">'."\n";
1974  }
1975 
1976  $parameters = array('tabname' => $active, 'out' => $out);
1977  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
1978  if ($reshook > 0) {
1979  $out = $hookmanager->resPrint;
1980  }
1981 
1982  return $out;
1983 }
1984 
1992 function dol_fiche_end($notab = 0)
1993 {
1994  print dol_get_fiche_end($notab);
1995 }
1996 
2003 function dol_get_fiche_end($notab = 0)
2004 {
2005  if (!$notab || $notab == -1) {
2006  return "\n</div>\n";
2007  } else {
2008  return '';
2009  }
2010 }
2011 
2031 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2032 {
2033  global $conf, $form, $user, $langs, $hookmanager, $action;
2034 
2035  $error = 0;
2036 
2037  $maxvisiblephotos = 1;
2038  $showimage = 1;
2039  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2040  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2041  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2042  $showbarcode = 0;
2043  }
2044  $modulepart = 'unknown';
2045 
2046  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2047  $modulepart = $object->element;
2048  } elseif ($object->element == 'member') {
2049  $modulepart = 'memberphoto';
2050  } elseif ($object->element == 'user') {
2051  $modulepart = 'userphoto';
2052  }
2053 
2054  if (class_exists("Imagick")) {
2055  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2056  $modulepart = $object->element;
2057  } elseif ($object->element == 'fichinter') {
2058  $modulepart = 'ficheinter';
2059  } elseif ($object->element == 'contrat') {
2060  $modulepart = 'contract';
2061  } elseif ($object->element == 'order_supplier') {
2062  $modulepart = 'supplier_order';
2063  } elseif ($object->element == 'invoice_supplier') {
2064  $modulepart = 'supplier_invoice';
2065  }
2066  }
2067 
2068  if ($object->element == 'product') {
2069  $width = 80;
2070  $cssclass = 'photowithmargin photoref';
2071  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2072  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2073  if ($conf->browser->layout == 'phone') {
2074  $maxvisiblephotos = 1;
2075  }
2076  if ($showimage) {
2077  $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>';
2078  } else {
2079  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2080  $nophoto = '';
2081  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2082  } else { // Show no photo link
2083  $nophoto = '/public/theme/common/nophoto.png';
2084  $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>';
2085  }
2086  }
2087  } elseif ($object->element == 'ticket') {
2088  $width = 80;
2089  $cssclass = 'photoref';
2090  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2091  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2092  if ($conf->browser->layout == 'phone') {
2093  $maxvisiblephotos = 1;
2094  }
2095 
2096  if ($showimage) {
2097  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2098  if ($object->nbphoto > 0) {
2099  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2100  } else {
2101  $showimage = 0;
2102  }
2103  }
2104  if (!$showimage) {
2105  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2106  $nophoto = '';
2107  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2108  } else { // Show no photo link
2109  $nophoto = img_picto('No photo', 'object_ticket');
2110  $morehtmlleft .= '<!-- No photo to show -->';
2111  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2112  $morehtmlleft .= $nophoto;
2113  $morehtmlleft .= '</div></div>';
2114  }
2115  }
2116  } else {
2117  if ($showimage) {
2118  if ($modulepart != 'unknown') {
2119  $phototoshow = '';
2120  // Check if a preview file is available
2121  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2122  $objectref = dol_sanitizeFileName($object->ref);
2123  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2124  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2125  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2126  $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
2127  } else {
2128  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2129  }
2130  if (empty($subdir)) {
2131  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2132  }
2133 
2134  $filepath = $dir_output.$subdir."/";
2135 
2136  $filepdf = $filepath.$objectref.".pdf";
2137  $relativepath = $subdir.'/'.$objectref.'.pdf';
2138 
2139  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2140  $fileimage = $filepdf.'_preview.png';
2141  $relativepathimage = $relativepath.'_preview.png';
2142 
2143  $pdfexists = file_exists($filepdf);
2144 
2145  // If PDF file exists
2146  if ($pdfexists) {
2147  // Conversion du PDF en image png si fichier png non existant
2148  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2149  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2150  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2151  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2152  if ($ret < 0) {
2153  $error++;
2154  }
2155  }
2156  }
2157  }
2158 
2159  if ($pdfexists && !$error) {
2160  $heightforphotref = 80;
2161  if (!empty($conf->dol_optimize_smallscreen)) {
2162  $heightforphotref = 60;
2163  }
2164  // If the preview file is found
2165  if (file_exists($fileimage)) {
2166  $phototoshow = '<div class="photoref">';
2167  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2168  $phototoshow .= '</div>';
2169  }
2170  }
2171  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2172  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2173  }
2174 
2175  if ($phototoshow) {
2176  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2177  $morehtmlleft .= $phototoshow;
2178  $morehtmlleft .= '</div>';
2179  }
2180  }
2181 
2182  if (empty($phototoshow)) { // Show No photo link (picto of object)
2183  if ($object->element == 'action') {
2184  $width = 80;
2185  $cssclass = 'photorefcenter';
2186  $nophoto = img_picto('No photo', 'title_agenda');
2187  } else {
2188  $width = 14;
2189  $cssclass = 'photorefcenter';
2190  $picto = $object->picto;
2191  $prefix = 'object_';
2192  if ($object->element == 'project' && !$object->public) {
2193  $picto = 'project'; // instead of projectpub
2194  }
2195  if (strpos($picto, 'fontawesome_') !== false) {
2196  $prefix = '';
2197  }
2198  $nophoto = img_picto('No photo', $prefix.$picto);
2199  }
2200  $morehtmlleft .= '<!-- No photo to show -->';
2201  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2202  $morehtmlleft .= $nophoto;
2203  $morehtmlleft .= '</div></div>';
2204  }
2205  }
2206  }
2207 
2208  if ($showbarcode) {
2209  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2210  }
2211 
2212  if ($object->element == 'societe') {
2213  if (!empty($conf->use_javascript_ajax) && $user->rights->societe->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2214  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2215  } else {
2216  $morehtmlstatus .= $object->getLibStatut(6);
2217  }
2218  } elseif ($object->element == 'product') {
2219  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2220  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2221  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2222  } else {
2223  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2224  }
2225  $morehtmlstatus .= ' &nbsp; ';
2226  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2227  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2228  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2229  } else {
2230  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2231  }
2232  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2233  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2234  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2235  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2236  }
2237  $morehtmlstatus .= $tmptxt;
2238  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2239  if ($object->statut == 0) {
2240  $morehtmlstatus .= $object->getLibStatut(5);
2241  } else {
2242  $morehtmlstatus .= $object->getLibStatut(4);
2243  }
2244  } elseif ($object->element == 'facturerec') {
2245  if ($object->frequency == 0) {
2246  $morehtmlstatus .= $object->getLibStatut(2);
2247  } else {
2248  $morehtmlstatus .= $object->getLibStatut(5);
2249  }
2250  } elseif ($object->element == 'project_task') {
2251  $object->fk_statut = 1;
2252  if ($object->progress > 0) {
2253  $object->fk_statut = 2;
2254  }
2255  if ($object->progress >= 100) {
2256  $object->fk_statut = 3;
2257  }
2258  $tmptxt = $object->getLibStatut(5);
2259  $morehtmlstatus .= $tmptxt; // No status on task
2260  } else { // Generic case
2261  $tmptxt = $object->getLibStatut(6);
2262  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2263  $tmptxt = $object->getLibStatut(5);
2264  }
2265  $morehtmlstatus .= $tmptxt;
2266  }
2267 
2268  // Add if object was dispatched "into accountancy"
2269  if (!empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2270  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2271  if (method_exists($object, 'getVentilExportCompta')) {
2272  $accounted = $object->getVentilExportCompta();
2273  $langs->load("accountancy");
2274  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2275  }
2276  }
2277 
2278  // Add alias for thirdparty
2279  if (!empty($object->name_alias)) {
2280  $morehtmlref .= '<div class="refidno">'.$object->name_alias.'</div>';
2281  }
2282 
2283  // Add label
2284  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2285  if (!empty($object->label)) {
2286  $morehtmlref .= '<div class="refidno">'.$object->label.'</div>';
2287  }
2288  }
2289 
2290  // Show address and email
2291  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2292  $moreaddress = $object->getBannerAddress('refaddress', $object);
2293  if ($moreaddress) {
2294  $morehtmlref .= '<div class="refidno">';
2295  $morehtmlref .= $moreaddress;
2296  $morehtmlref .= '</div>';
2297  }
2298  }
2299  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)) {
2300  $morehtmlref .= '<div style="clear: both;"></div>';
2301  $morehtmlref .= '<div class="refidno">';
2302  $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id;
2303  $morehtmlref .= '</div>';
2304  }
2305 
2306  $parameters=array('morehtmlref'=>$morehtmlref);
2307  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2308  if ($reshook < 0) {
2309  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2310  } elseif (empty($reshook)) {
2311  $morehtmlref .= $hookmanager->resPrint;
2312  } elseif ($reshook > 0) {
2313  $morehtmlref = $hookmanager->resPrint;
2314  }
2315 
2316 
2317  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2318  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2319  print '</div>';
2320  print '<div class="underrefbanner clearboth"></div>';
2321 }
2322 
2332 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2333 {
2334  global $langs;
2335  $ret = '';
2336  if ($fieldrequired) {
2337  $ret .= '<span class="fieldrequired">';
2338  }
2339  $ret .= '<label for="'.$fieldkey.'">';
2340  $ret .= $langs->trans($langkey);
2341  $ret .= '</label>';
2342  if ($fieldrequired) {
2343  $ret .= '</span>';
2344  }
2345  return $ret;
2346 }
2347 
2355 function dol_bc($var, $moreclass = '')
2356 {
2357  global $bc;
2358  $ret = ' '.$bc[$var];
2359  if ($moreclass) {
2360  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2361  }
2362  return $ret;
2363 }
2364 
2378 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2379 {
2380  global $conf, $langs, $hookmanager;
2381 
2382  $ret = '';
2383  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2384 
2385  // See format of addresses on https://en.wikipedia.org/wiki/Address
2386  // Address
2387  if (empty($mode)) {
2388  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address));
2389  }
2390  // Zip/Town/State
2391  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2392  // US: title firstname name \n address lines \n town, state, zip \n country
2393  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2394  $ret .= (($ret && $town) ? $sep : '').$town;
2395 
2396  if (!empty($object->state)) {
2397  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2398  }
2399  if (!empty($object->zip)) {
2400  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2401  }
2402  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2403  // UK: title firstname name \n address lines \n town state \n zip \n country
2404  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2405  $ret .= ($ret ? $sep : '').$town;
2406  if (!empty($object->state)) {
2407  $ret .= ($ret ? ", " : '').$object->state;
2408  }
2409  if (!empty($object->zip)) {
2410  $ret .= ($ret ? $sep : '').$object->zip;
2411  }
2412  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2413  // ES: title firstname name \n address lines \n zip town \n state \n country
2414  $ret .= ($ret ? $sep : '').$object->zip;
2415  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2416  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2417  if (!empty($object->state)) {
2418  $ret .= "\n".$object->state;
2419  }
2420  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2421  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2422  // See https://www.sljfaq.org/afaq/addresses.html
2423  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2424  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2425  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2426  // IT: title firstname name\n address lines \n zip town state_code \n country
2427  $ret .= ($ret ? $sep : '').$object->zip;
2428  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2429  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2430  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2431  } else {
2432  // Other: title firstname name \n address lines \n zip town[, state] \n country
2433  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2434  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2435  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2436  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2437  $ret .= ($ret ? ", " : '').$object->state;
2438  }
2439  }
2440  if (!is_object($outputlangs)) {
2441  $outputlangs = $langs;
2442  }
2443  if ($withcountry) {
2444  $langs->load("dict");
2445  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2446  }
2447  if ($hookmanager) {
2448  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2449  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2450  if ($reshook > 0) {
2451  $ret = '';
2452  }
2453  $ret .= $hookmanager->resPrint;
2454  }
2455 
2456  return $ret;
2457 }
2458 
2459 
2460 
2469 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2470 {
2471  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2472  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2473  } else {
2474  return 'Error date into a not supported range';
2475  }
2476 }
2477 
2499 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2500 {
2501  global $conf, $langs;
2502 
2503  // If date undefined or "", we return ""
2504  if (dol_strlen($time) == 0) {
2505  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2506  }
2507 
2508  if ($tzoutput === 'auto') {
2509  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2510  }
2511 
2512  // Clean parameters
2513  $to_gmt = false;
2514  $offsettz = $offsetdst = 0;
2515  if ($tzoutput) {
2516  $to_gmt = true; // For backward compatibility
2517  if (is_string($tzoutput)) {
2518  if ($tzoutput == 'tzserver') {
2519  $to_gmt = false;
2520  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2521  $offsettz = 0; // Timezone offset with server timezone, so 0
2522  $offsetdst = 0; // Dst offset with server timezone, so 0
2523  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2524  $to_gmt = true;
2525  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2526 
2527  if (class_exists('DateTimeZone')) {
2528  $user_date_tz = new DateTimeZone($offsettzstring);
2529  $user_dt = new DateTime();
2530  $user_dt->setTimezone($user_date_tz);
2531  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2532  $offsettz = $user_dt->getOffset();
2533  } else { // old method (The 'tzuser' was processed like the 'tzuserrel')
2534  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2535  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2536  }
2537  }
2538  }
2539  }
2540  if (!is_object($outputlangs)) {
2541  $outputlangs = $langs;
2542  }
2543  if (!$format) {
2544  $format = 'daytextshort';
2545  }
2546 
2547  // Do we have to reduce the length of date (year on 2 chars) to save space.
2548  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2549  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2550  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2551  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2552  if ($formatwithoutreduce != $format) {
2553  $format = $formatwithoutreduce;
2554  $reduceformat = 1;
2555  } // so format 'dayreduceformat' is processed like day
2556 
2557  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2558  // TODO Add format daysmallyear and dayhoursmallyear
2559  if ($format == 'day') {
2560  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2561  } elseif ($format == 'hour') {
2562  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2563  } elseif ($format == 'hourduration') {
2564  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2565  } elseif ($format == 'daytext') {
2566  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2567  } elseif ($format == 'daytextshort') {
2568  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2569  } elseif ($format == 'dayhour') {
2570  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2571  } elseif ($format == 'dayhoursec') {
2572  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2573  } elseif ($format == 'dayhourtext') {
2574  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2575  } elseif ($format == 'dayhourtextshort') {
2576  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2577  } elseif ($format == 'dayhourlog') {
2578  // Format not sensitive to language
2579  $format = '%Y%m%d%H%M%S';
2580  } elseif ($format == 'dayhourlogsmall') {
2581  // Format not sensitive to language
2582  $format = '%Y%m%d%H%M';
2583  } elseif ($format == 'dayhourldap') {
2584  $format = '%Y%m%d%H%M%SZ';
2585  } elseif ($format == 'dayhourxcard') {
2586  $format = '%Y%m%dT%H%M%SZ';
2587  } elseif ($format == 'dayxcard') {
2588  $format = '%Y%m%d';
2589  } elseif ($format == 'dayrfc') {
2590  $format = '%Y-%m-%d'; // DATE_RFC3339
2591  } elseif ($format == 'dayhourrfc') {
2592  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2593  } elseif ($format == 'standard') {
2594  $format = '%Y-%m-%d %H:%M:%S';
2595  }
2596 
2597  if ($reduceformat) {
2598  $format = str_replace('%Y', '%y', $format);
2599  $format = str_replace('yyyy', 'yy', $format);
2600  }
2601 
2602  // Clean format
2603  if (preg_match('/%b/i', $format)) { // There is some text to translate
2604  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2605  $format = str_replace('%b', '__b__', $format);
2606  $format = str_replace('%B', '__B__', $format);
2607  }
2608  if (preg_match('/%a/i', $format)) { // There is some text to translate
2609  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2610  $format = str_replace('%a', '__a__', $format);
2611  $format = str_replace('%A', '__A__', $format);
2612  }
2613 
2614 
2615  // Analyze date
2616  $reg = array();
2617  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
2618  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2619  return '';
2620  } 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
2621  // This part of code should not be used anymore.
2622  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2623  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2624  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2625  $syear = (!empty($reg[1]) ? $reg[1] : '');
2626  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2627  $sday = (!empty($reg[3]) ? $reg[3] : '');
2628  $shour = (!empty($reg[4]) ? $reg[4] : '');
2629  $smin = (!empty($reg[5]) ? $reg[5] : '');
2630  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2631 
2632  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2633  $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt);
2634  } else {
2635  // Date is a timestamps
2636  if ($time < 100000000000) { // Protection against bad date values
2637  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2638 
2639  $ret = adodb_strftime($format, $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2640  } else {
2641  $ret = 'Bad value '.$time.' for date';
2642  }
2643  }
2644 
2645  if (preg_match('/__b__/i', $format)) {
2646  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2647 
2648  // Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
2649  $month = adodb_strftime('%m', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2650  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2651  if ($encodetooutput) {
2652  $monthtext = $outputlangs->transnoentities('Month'.$month);
2653  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2654  } else {
2655  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2656  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2657  }
2658  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2659  $ret = str_replace('__b__', $monthtextshort, $ret);
2660  $ret = str_replace('__B__', $monthtext, $ret);
2661  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2662  //return $ret;
2663  }
2664  if (preg_match('/__a__/i', $format)) {
2665  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2666  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2667 
2668  $w = adodb_strftime('%w', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2669  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2670  $ret = str_replace('__A__', $dayweek, $ret);
2671  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2672  }
2673 
2674  return $ret;
2675 }
2676 
2677 
2698 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2699 {
2700  //$datetimeobj = new DateTime('@'.$timestamp);
2701  $datetimeobj = new DateTime();
2702  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2703  if ($forcetimezone) {
2704  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2705  }
2706  $arrayinfo = array(
2707  'year'=>((int) date_format($datetimeobj, 'Y')),
2708  'mon'=>((int) date_format($datetimeobj, 'm')),
2709  'mday'=>((int) date_format($datetimeobj, 'd')),
2710  'wday'=>((int) date_format($datetimeobj, 'w')),
2711  'yday'=>((int) date_format($datetimeobj, 'z')),
2712  'hours'=>((int) date_format($datetimeobj, 'H')),
2713  'minutes'=>((int) date_format($datetimeobj, 'i')),
2714  'seconds'=>((int) date_format($datetimeobj, 's')),
2715  '0'=>$timestamp
2716  );
2717 
2718  return $arrayinfo;
2719 }
2720 
2742 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2743 {
2744  global $conf;
2745  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2746 
2747  if ($gm === 'auto') {
2748  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2749  }
2750  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2751 
2752  // Clean parameters
2753  if ($hour == -1 || empty($hour)) {
2754  $hour = 0;
2755  }
2756  if ($minute == -1 || empty($minute)) {
2757  $minute = 0;
2758  }
2759  if ($second == -1 || empty($second)) {
2760  $second = 0;
2761  }
2762 
2763  // Check parameters
2764  if ($check) {
2765  if (!$month || !$day) {
2766  return '';
2767  }
2768  if ($day > 31) {
2769  return '';
2770  }
2771  if ($month > 12) {
2772  return '';
2773  }
2774  if ($hour < 0 || $hour > 24) {
2775  return '';
2776  }
2777  if ($minute < 0 || $minute > 60) {
2778  return '';
2779  }
2780  if ($second < 0 || $second > 60) {
2781  return '';
2782  }
2783  }
2784 
2785  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
2786  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
2787  $localtz = new DateTimeZone($default_timezone);
2788  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
2789  // We use dol_tz_string first because it is more reliable.
2790  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2791  try {
2792  $localtz = new DateTimeZone($default_timezone);
2793  } catch (Exception $e) {
2794  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2795  $default_timezone = @date_default_timezone_get();
2796  }
2797  } elseif (strrpos($gm, "tz,") !== false) {
2798  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2799  try {
2800  $localtz = new DateTimeZone($timezone);
2801  } catch (Exception $e) {
2802  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2803  }
2804  }
2805 
2806  if (empty($localtz)) {
2807  $localtz = new DateTimeZone('UTC');
2808  }
2809  //var_dump($localtz);
2810  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2811  $dt = new DateTime('now', $localtz);
2812  $dt->setDate((int) $year, (int) $month, (int) $day);
2813  $dt->setTime((int) $hour, (int) $minute, (int) $second);
2814  $date = $dt->getTimestamp(); // should include daylight saving time
2815  //var_dump($date);
2816  return $date;
2817 }
2818 
2819 
2830 function dol_now($mode = 'auto')
2831 {
2832  $ret = 0;
2833 
2834  if ($mode === 'auto') {
2835  $mode = 'gmt';
2836  }
2837 
2838  if ($mode == 'gmt') {
2839  $ret = time(); // Time for now at greenwich.
2840  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
2841  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2842  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2843  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2844  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
2845  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2846  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
2847  // $ret=dol_now('gmt')+($tzsecond*3600);
2848  //}
2849  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
2850  // Time for now with user timezone added
2851  //print 'time: '.time();
2852  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
2853  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
2854  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
2855  }
2856 
2857  return $ret;
2858 }
2859 
2860 
2869 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2870 {
2871  global $conf, $langs;
2872  $level = 1024;
2873 
2874  if (!empty($conf->dol_optimize_smallscreen)) {
2875  $shortunit = 1;
2876  }
2877 
2878  // Set value text
2879  if (empty($shortvalue) || $size < ($level * 10)) {
2880  $ret = $size;
2881  $textunitshort = $langs->trans("b");
2882  $textunitlong = $langs->trans("Bytes");
2883  } else {
2884  $ret = round($size / $level, 0);
2885  $textunitshort = $langs->trans("Kb");
2886  $textunitlong = $langs->trans("KiloBytes");
2887  }
2888  // Use long or short text unit
2889  if (empty($shortunit)) {
2890  $ret .= ' '.$textunitlong;
2891  } else {
2892  $ret .= ' '.$textunitshort;
2893  }
2894 
2895  return $ret;
2896 }
2897 
2907 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
2908 {
2909  global $langs;
2910 
2911  if (empty($url)) {
2912  return '';
2913  }
2914 
2915  $link = '<a href="';
2916  if (!preg_match('/^http/i', $url)) {
2917  $link .= 'http://';
2918  }
2919  $link .= $url;
2920  $link .= '"';
2921  if ($target) {
2922  $link .= ' target="'.$target.'"';
2923  }
2924  $link .= '>';
2925  if (!preg_match('/^http/i', $url)) {
2926  $link .= 'http://';
2927  }
2928  $link .= dol_trunc($url, $max);
2929  $link .= '</a>';
2930  return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
2931 }
2932 
2945 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
2946 {
2947  global $conf, $user, $langs, $hookmanager;
2948 
2949  $newemail = dol_escape_htmltag($email);
2950 
2951  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
2952  $withpicto = 0;
2953  }
2954 
2955  if (empty($email)) {
2956  return '&nbsp;';
2957  }
2958 
2959  if (!empty($addlink)) {
2960  $newemail = '<a style="text-overflow: ellipsis;" href="';
2961  if (!preg_match('/^mailto:/i', $email)) {
2962  $newemail .= 'mailto:';
2963  }
2964  $newemail .= $email;
2965  $newemail .= '">';
2966  $newemail .= dol_trunc($email, $max);
2967  $newemail .= '</a>';
2968  if ($showinvalid && !isValidEmail($email)) {
2969  $langs->load("errors");
2970  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
2971  }
2972 
2973  if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) {
2974  $type = 'AC_EMAIL';
2975  $link = '';
2976  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
2977  $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>';
2978  }
2979  if ($link) {
2980  $newemail = '<div>'.$newemail.' '.$link.'</div>';
2981  }
2982  }
2983  } else {
2984  if ($showinvalid && !isValidEmail($email)) {
2985  $langs->load("errors");
2986  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
2987  }
2988  }
2989 
2990  //$rep = '<div class="nospan" style="margin-right: 10px">';
2991  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
2992  //$rep .= '</div>';
2993  if ($hookmanager) {
2994  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
2995 
2996  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
2997  if ($reshook > 0) {
2998  $rep = '';
2999  }
3000  $rep .= $hookmanager->resPrint;
3001  }
3002 
3003  return $rep;
3004 }
3005 
3012 {
3013  global $conf, $db;
3014 
3015  $socialnetworks = array();
3016  // Enable caching of array
3017  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3018  $cachekey = 'socialnetworks_' . $conf->entity;
3019  $dataretrieved = dol_getcache($cachekey);
3020  if (!is_null($dataretrieved)) {
3021  $socialnetworks = $dataretrieved;
3022  } else {
3023  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3024  $sql .= " WHERE entity=".$conf->entity;
3025  $resql = $db->query($sql);
3026  if ($resql) {
3027  while ($obj = $db->fetch_object($resql)) {
3028  $socialnetworks[$obj->code] = array(
3029  'rowid' => $obj->rowid,
3030  'label' => $obj->label,
3031  'url' => $obj->url,
3032  'icon' => $obj->icon,
3033  'active' => $obj->active,
3034  );
3035  }
3036  }
3037  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3038  }
3039 
3040  return $socialnetworks;
3041 }
3042 
3053 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3054 {
3055  global $conf, $user, $langs;
3056 
3057  $htmllink = $value;
3058 
3059  if (empty($value)) {
3060  return '&nbsp;';
3061  }
3062 
3063  if (!empty($type)) {
3064  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3065  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3066  $htmllink .= '<span class="fa paddingright '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3067  if ($type == 'skype') {
3068  $htmllink .= dol_escape_htmltag($value);
3069  $htmllink .= '&nbsp;';
3070  $htmllink .= '<a href="skype:';
3071  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3072  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3073  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3074  $htmllink .= '</a><a href="skype:';
3075  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3076  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3077  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3078  $htmllink .= '</a>';
3079  if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) {
3080  $addlink = 'AC_SKYPE';
3081  $link = '';
3082  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3083  $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>';
3084  }
3085  $htmllink .= ($link ? ' '.$link : '');
3086  }
3087  } else {
3088  if (!empty($dictsocialnetworks[$type]['url'])) {
3089  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3090  if ($tmpvirginurl) {
3091  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3092  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3093 
3094  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3095  if ($tmpvirginurl3) {
3096  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3097  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3098  }
3099 
3100  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3101  if ($tmpvirginurl2) {
3102  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3103  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3104  }
3105  }
3106  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3107  if (preg_match('/^https?:\/\//i', $link)) {
3108  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3109  } else {
3110  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3111  }
3112  } else {
3113  $htmllink .= dol_escape_htmltag($value);
3114  }
3115  }
3116  $htmllink .= '</div>';
3117  } else {
3118  $langs->load("errors");
3119  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3120  }
3121  return $htmllink;
3122 }
3123 
3134 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3135 {
3136  global $mysoc;
3137 
3138  if (empty($profID) || empty($profIDtype)) {
3139  return '';
3140  }
3141  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3142  $newProfID = $profID;
3143  $id = substr($profIDtype, -1);
3144  $ret = '';
3145  if (strtoupper($countrycode) == 'FR') {
3146  // France
3147  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3148  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);
3149  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);
3150  }
3151  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3152  else $ret = $newProfID;
3153  return $ret;
3154 }
3155 
3170 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3171 {
3172  global $conf, $user, $langs, $mysoc, $hookmanager;
3173 
3174  // Clean phone parameter
3175  $phone = preg_replace("/[\s.-]/", "", trim($phone));
3176  if (empty($phone)) {
3177  return '';
3178  }
3179  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3180  $separ = $conf->global->MAIN_PHONE_SEPAR;
3181  }
3182  if (empty($countrycode) && is_object($mysoc)) {
3183  $countrycode = $mysoc->country_code;
3184  }
3185 
3186  // Short format for small screens
3187  if ($conf->dol_optimize_smallscreen) {
3188  $separ = '';
3189  }
3190 
3191  $newphone = $phone;
3192  if (strtoupper($countrycode) == "FR") {
3193  // France
3194  if (dol_strlen($phone) == 10) {
3195  $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);
3196  } elseif (dol_strlen($phone) == 7) {
3197  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3198  } elseif (dol_strlen($phone) == 9) {
3199  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3200  } elseif (dol_strlen($phone) == 11) {
3201  $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);
3202  } elseif (dol_strlen($phone) == 12) {
3203  $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);
3204  } elseif (dol_strlen($phone) == 13) {
3205  $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);
3206  }
3207  } elseif (strtoupper($countrycode) == "CA") {
3208  if (dol_strlen($phone) == 10) {
3209  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3210  }
3211  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3212  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3213  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3214  }
3215  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3216  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3217  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3218  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3219  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3220  }
3221  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3222  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3223  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3224  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3225  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3226  }
3227  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3228  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3229  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3230  }
3231  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3232  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3233  $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);
3234  }
3235  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3236  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3237  $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);
3238  }
3239  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3240  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3241  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3242  }
3243  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3244  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3245  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3246  }
3247  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3248  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3249  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3250  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3251  $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);
3252  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3253  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3254  }
3255  } elseif (strtoupper($countrycode) == "ML") {//Mali
3256  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3257  $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);
3258  }
3259  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3260  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3261  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3262  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3263  $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);
3264  }
3265  } elseif (strtoupper($countrycode) == "MU") {
3266  //Maurice
3267  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3268  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3269  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3270  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3271  }
3272  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3273  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3274  $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);
3275  }
3276  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3277  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3278  $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);
3279  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3280  $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);
3281  }
3282  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3283  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3284  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3285  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3286  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3287  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3288  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3289  }
3290  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3291  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3292  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3293  }
3294  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3295  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3296  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3297  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3298  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3299  }
3300  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3301  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3302  $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);
3303  }
3304  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3305  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3306  $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);
3307  }
3308  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3309  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3310  $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);
3311  }
3312  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3313  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3314  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3315  }
3316  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3317  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3318  $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);
3319  }
3320  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3321  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3322  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3323  }
3324  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3325  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3326  $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);
3327  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3328  $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);
3329  }
3330  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3331  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3332  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3333  }
3334  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3335  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3336  $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);
3337  }
3338  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3339  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3340  $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);
3341  }
3342  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3343  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3344  $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);
3345  }
3346  } elseif (strtoupper($countrycode) == "IT") {//Italie
3347  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3348  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3349  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3350  $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);
3351  }
3352  } elseif (strtoupper($countrycode) == "AU") {
3353  //Australie
3354  if (dol_strlen($phone) == 12) {
3355  //ex: +61_A_BCDE_FGHI
3356  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3357  }
3358  }
3359  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3360  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
3361  $newphoneform = $newphone;
3362  $newphone = '<a href="tel:'.$phone.'"';
3363  $newphone .= '>'.$newphoneform.'</a>';
3364  } elseif (!empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3365  if (empty($user->clicktodial_loaded)) {
3366  $user->fetch_clicktodial();
3367  }
3368 
3369  // Define urlmask
3370  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3371  if (!empty($conf->global->CLICKTODIAL_URL)) {
3372  $urlmask = $conf->global->CLICKTODIAL_URL;
3373  }
3374  if (!empty($user->clicktodial_url)) {
3375  $urlmask = $user->clicktodial_url;
3376  }
3377 
3378  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3379  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3380  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3381  // This line is for backward compatibility
3382  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3383  // Thoose lines are for substitution
3384  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3385  '__PHONETO__'=>urlencode($phone),
3386  '__LOGIN__'=>$clicktodial_login,
3387  '__PASS__'=>$clicktodial_password);
3388  $url = make_substitutions($url, $substitarray);
3389  $newphonesav = $newphone;
3390  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3391  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3392  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3393  $newphone .= '>'.$newphonesav.'</a>';
3394  } else {
3395  // Old method
3396  $newphone = '<a href="'.$url.'"';
3397  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3398  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3399  }
3400  $newphone .= '>'.$newphonesav.'</a>';
3401  }
3402  }
3403 
3404  //if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
3405  if (isModEnabled('agenda') && $user->rights->agenda->myactions->create) {
3406  $type = 'AC_TEL';
3407  $link = '';
3408  if ($addlink == 'AC_FAX') {
3409  $type = 'AC_FAX';
3410  }
3411  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3412  $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$type.($cid ? '&amp;contactid='.$cid : '').($socid ? '&amp;socid='.$socid : '').'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3413  }
3414  if ($link) {
3415  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3416  }
3417  }
3418  }
3419 
3420  if (empty($titlealt)) {
3421  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3422  }
3423  $rep = '';
3424 
3425  if ($hookmanager) {
3426  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3427  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3428  $rep .= $hookmanager->resPrint;
3429  }
3430  if (empty($reshook)) {
3431  $picto = '';
3432  if ($withpicto) {
3433  if ($withpicto == 'fax') {
3434  $picto = 'phoning_fax';
3435  } elseif ($withpicto == 'phone') {
3436  $picto = 'phoning';
3437  } elseif ($withpicto == 'mobile') {
3438  $picto = 'phoning_mobile';
3439  } else {
3440  $picto = '';
3441  }
3442  }
3443  if ($adddivfloat) {
3444  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3445  } else {
3446  $rep .= '<span style="margin-right: 10px;">';
3447  }
3448  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3449  if ($adddivfloat) {
3450  $rep .= '</div>';
3451  } else {
3452  $rep .= '</span>';
3453  }
3454  }
3455 
3456  return $rep;
3457 }
3458 
3466 function dol_print_ip($ip, $mode = 0)
3467 {
3468  global $conf, $langs;
3469 
3470  $ret = '';
3471 
3472  if (empty($mode)) {
3473  $ret .= $ip;
3474  }
3475 
3476  if ($mode != 2) {
3477  $countrycode = dolGetCountryCodeFromIp($ip);
3478  if ($countrycode) { // If success, countrycode is us, fr, ...
3479  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3480  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3481  } else {
3482  $ret .= ' ('.$countrycode.')';
3483  }
3484  } else {
3485  // Nothing
3486  }
3487  }
3488 
3489  return $ret;
3490 }
3491 
3501 {
3502  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3503  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3504  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3505  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3506  } else {
3507  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3508  }
3509  } else {
3510  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3511  }
3512  } else {
3513  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3514  }
3515  return $ip;
3516 }
3517 
3526 function isHTTPS()
3527 {
3528  $isSecure = false;
3529  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3530  $isSecure = true;
3531  } 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') {
3532  $isSecure = true;
3533  }
3534  return $isSecure;
3535 }
3536 
3544 {
3545  global $conf;
3546 
3547  $countrycode = '';
3548 
3549  if (!empty($conf->geoipmaxmind->enabled)) {
3550  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3551  //$ip='24.24.24.24';
3552  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3553  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3554  $geoip = new DolGeoIP('country', $datafile);
3555  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3556  $countrycode = $geoip->getCountryCodeFromIP($ip);
3557  }
3558 
3559  return $countrycode;
3560 }
3561 
3562 
3570 {
3571  global $conf, $langs, $user;
3572 
3573  //$ret=$user->xxx;
3574  $ret = '';
3575  if (!empty($conf->geoipmaxmind->enabled)) {
3576  $ip = getUserRemoteIP();
3577  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3578  //$ip='24.24.24.24';
3579  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3580  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3581  $geoip = new DolGeoIP('country', $datafile);
3582  $countrycode = $geoip->getCountryCodeFromIP($ip);
3583  $ret = $countrycode;
3584  }
3585  return $ret;
3586 }
3587 
3600 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3601 {
3602  global $conf, $user, $langs, $hookmanager;
3603 
3604  $out = '';
3605 
3606  if ($address) {
3607  if ($hookmanager) {
3608  $parameters = array('element' => $element, 'id' => $id);
3609  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3610  $out .= $hookmanager->resPrint;
3611  }
3612  if (empty($reshook)) {
3613  if (empty($charfornl)) {
3614  $out .= nl2br($address);
3615  } else {
3616  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3617  }
3618 
3619  // TODO Remove this block, we can add this using the hook now
3620  $showgmap = $showomap = 0;
3621  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3622  $showgmap = 1;
3623  }
3624  if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3625  $showgmap = 1;
3626  }
3627  if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3628  $showgmap = 1;
3629  }
3630  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3631  $showomap = 1;
3632  }
3633  if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3634  $showomap = 1;
3635  }
3636  if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3637  $showomap = 1;
3638  }
3639  if ($showgmap) {
3640  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3641  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3642  }
3643  if ($showomap) {
3644  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3645  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3646  }
3647  }
3648  }
3649  if ($noprint) {
3650  return $out;
3651  } else {
3652  print $out;
3653  }
3654 }
3655 
3656 
3666 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3667 {
3668  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3669  return true;
3670  }
3671  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3672  return true;
3673  }
3674  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3675  return true;
3676  }
3677 
3678  return false;
3679 }
3680 
3689 function isValidMXRecord($domain)
3690 {
3691  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3692  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3693  return 0;
3694  }
3695  if (function_exists('getmxrr')) {
3696  $mxhosts = array();
3697  $weight = array();
3698  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3699  if (count($mxhosts) > 1) {
3700  return 1;
3701  }
3702  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3703  return 1;
3704  }
3705 
3706  return 0;
3707  }
3708  }
3709  return -1;
3710 }
3711 
3719 function isValidPhone($phone)
3720 {
3721  return true;
3722 }
3723 
3724 
3732 function dol_strlen($string, $stringencoding = 'UTF-8')
3733 {
3734  if (function_exists('mb_strlen')) {
3735  return mb_strlen($string, $stringencoding);
3736  } else {
3737  return strlen($string);
3738  }
3739 }
3740 
3751 function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
3752 {
3753  global $langs;
3754 
3755  if (empty($stringencoding)) {
3756  $stringencoding = $langs->charset_output;
3757  }
3758 
3759  $ret = '';
3760  if (empty($trunconbytes)) {
3761  if (function_exists('mb_substr')) {
3762  $ret = mb_substr($string, $start, $length, $stringencoding);
3763  } else {
3764  $ret = substr($string, $start, $length);
3765  }
3766  } else {
3767  if (function_exists('mb_strcut')) {
3768  $ret = mb_strcut($string, $start, $length, $stringencoding);
3769  } else {
3770  $ret = substr($string, $start, $length);
3771  }
3772  }
3773  return $ret;
3774 }
3775 
3776 
3790 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
3791 {
3792  global $conf;
3793 
3794  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
3795  return $string;
3796  }
3797 
3798  if (empty($stringencoding)) {
3799  $stringencoding = 'UTF-8';
3800  }
3801  // reduce for small screen
3802  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
3803  $size = round($size / 3);
3804  }
3805 
3806  // We go always here
3807  if ($trunc == 'right') {
3808  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3809  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3810  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3811  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
3812  } else {
3813  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
3814  return $string;
3815  }
3816  } elseif ($trunc == 'middle') {
3817  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3818  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3819  $size1 = round($size / 2);
3820  $size2 = round($size / 2);
3821  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
3822  } else {
3823  return $string;
3824  }
3825  } elseif ($trunc == 'left') {
3826  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3827  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3828  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3829  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
3830  } else {
3831  return $string;
3832  }
3833  } elseif ($trunc == 'wrap') {
3834  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3835  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3836  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
3837  } else {
3838  return $string;
3839  }
3840  } else {
3841  return 'BadParam3CallingDolTrunc';
3842  }
3843 }
3844 
3865 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
3866 {
3867  global $conf, $langs;
3868 
3869  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
3870  $url = DOL_URL_ROOT;
3871  $theme = isset($conf->theme) ? $conf->theme : null;
3872  $path = 'theme/'.$theme;
3873  // Define fullpathpicto to use into src
3874  if ($pictoisfullpath) {
3875  // Clean parameters
3876  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
3877  $picto .= '.png';
3878  }
3879  $fullpathpicto = $picto;
3880  $reg = array();
3881  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3882  $morecss .= ($morecss ? ' ' : '').$reg[1];
3883  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
3884  }
3885  } else {
3886  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
3887  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
3888 
3889  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
3890  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
3891  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
3892  $pictowithouttextarray = explode('_', $pictowithouttext);
3893  $marginleftonlyshort = 0;
3894 
3895  if (!empty($pictowithouttextarray[1])) {
3896  $fakey = 'fa-'.$pictowithouttextarray[1];
3897  $fa = empty($pictowithouttextarray[2]) ? 'fa' : $pictowithouttextarray[2];
3898  $facolor = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
3899  $fasize = empty($pictowithouttextarray[4]) ? '' : $pictowithouttextarray[4];
3900  } else {
3901  $fakey = 'fa-'.$pictowithouttext;
3902  $fa = 'fa';
3903  $facolor = '';
3904  $fasize = '';
3905  }
3906 
3907  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
3908  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
3909  $morestyle = '';
3910  $reg = array();
3911  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3912  $morecss .= ($morecss ? ' ' : '').$reg[1];
3913  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
3914  }
3915  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
3916  $morestyle = $reg[1];
3917  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
3918  }
3919  $moreatt = trim($moreatt);
3920 
3921  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
3922  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
3923  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3924  $enabledisablehtml .= $titlealt;
3925  }*/
3926  $enabledisablehtml .= '</span>';
3927 
3928  return $enabledisablehtml;
3929  }
3930 
3931  if (empty($srconly) && in_array($pictowithouttext, array(
3932  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
3933  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
3934  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
3935  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
3936  'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cubes',
3937  'currency', 'multicurrency',
3938  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
3939  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt',
3940  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
3941  'generate', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
3942  'help', 'holiday',
3943  'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'knowledgemanagement',
3944  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lot', 'long-arrow-alt-right',
3945  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
3946  'off', 'on', 'order',
3947  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'puce',
3948  'stock', 'resize', 'service', 'stats', 'trip',
3949  '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',
3950  'github', 'google', 'jabber', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
3951  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
3952  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
3953  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
3954  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
3955  'technic', 'ticket',
3956  'error', 'warning',
3957  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'resource', 'recurring',
3958  'shapes', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
3959  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
3960  'uncheck', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
3961  'conferenceorbooth', 'eventorganization'
3962  ))) {
3963  $fakey = $pictowithouttext;
3964  $facolor = '';
3965  $fasize = '';
3966  $fa = 'fas';
3967  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'))) {
3968  $fa = 'far';
3969  }
3970  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
3971  $fa = 'fab';
3972  }
3973 
3974  $arrayconvpictotofa = array(
3975  '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',
3976  'bank_account'=>'university',
3977  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
3978  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
3979  'bom'=>'shapes',
3980  'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time',
3981  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
3982  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
3983  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
3984  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
3985  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
3986  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
3987  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
3988  'generic'=>'file', 'holiday'=>'umbrella-beach',
3989  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
3990  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
3991  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
3992  'sign-out'=>'sign-out-alt',
3993  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
3994  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
3995  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
3996  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
3997  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
3998  'other'=>'square',
3999  'playdisabled'=>'play', 'pdf'=>'file-pdf', 'poll'=>'check-double', 'pos'=>'cash-register', 'preview'=>'binoculars', 'project'=>'project-diagram', 'projectpub'=>'project-diagram', 'projecttask'=>'tasks', 'propal'=>'file-signature',
4000  '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',
4001  'recent' => 'question', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4002  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4003  'refresh'=>'redo', 'region'=>'map-marked', 'resource'=>'laptop-house', 'recurring'=>'history',
4004  'service'=>'concierge-bell',
4005  'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4006  'supplier'=>'building', 'technic'=>'cogs',
4007  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4008  'title_agenda'=>'calendar-alt',
4009  'uncheck'=>'times', 'uparrow'=>'share', 'vat'=>'money-check-alt', 'vcard'=>'address-card',
4010  'jabber'=>'comment-o',
4011  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4012  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4013  );
4014  if ($pictowithouttext == 'off') {
4015  $fakey = 'fa-square';
4016  $fasize = '1.3em';
4017  } elseif ($pictowithouttext == 'on') {
4018  $fakey = 'fa-check-square';
4019  $fasize = '1.3em';
4020  } elseif ($pictowithouttext == 'listlight') {
4021  $fakey = 'fa-download';
4022  $marginleftonlyshort = 1;
4023  } elseif ($pictowithouttext == 'printer') {
4024  $fakey = 'fa-print';
4025  $fasize = '1.2em';
4026  } elseif ($pictowithouttext == 'note') {
4027  $fakey = 'fa-sticky-note';
4028  $marginleftonlyshort = 1;
4029  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4030  $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');
4031  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4032  if (preg_match('/selected/', $pictowithouttext)) {
4033  $facolor = '#888';
4034  }
4035  $marginleftonlyshort = 1;
4036  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4037  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4038  } else {
4039  $fakey = 'fa-'.$pictowithouttext;
4040  }
4041 
4042  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4043  $morecss .= ' em092';
4044  }
4045  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4046  $morecss .= ' em088';
4047  }
4048  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4049  $morecss .= ' em080';
4050  }
4051 
4052  // Define $marginleftonlyshort
4053  $arrayconvpictotomarginleftonly = array(
4054  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4055  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4056  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4057  );
4058  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4059  $marginleftonlyshort = 0;
4060  }
4061 
4062  // Add CSS
4063  $arrayconvpictotomorcess = array(
4064  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4065  'bank_account'=>'bg-infobox-bank_account',
4066  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4067  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4068  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4069  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4070  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4071  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4072  'incoterm'=>'infobox-supplier_proposal',
4073  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4074  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4075  'order'=>'infobox-commande',
4076  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4077  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_red'=>'font-status8',
4078  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4079  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4080  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4081  'propal'=>'infobox-propal', 'private'=>'infobox-project',
4082  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4083  'resource'=>'infobox-action',
4084  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4085  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4086  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4087  'vat'=>'infobox-bank_account',
4088  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4089  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4090  );
4091  if (!empty($arrayconvpictotomorcess[$pictowithouttext])) {
4092  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4093  }
4094 
4095  // Define $color
4096  $arrayconvpictotocolor = array(
4097  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4098  'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4099  'dynamicprice'=>'#a69944',
4100  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4101  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4102  'lot'=>'#a69944',
4103  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4104  'other'=>'#ddd', 'world'=>'#986c6a',
4105  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4106  //'shipment'=>'#a69944',
4107  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4108  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4109  'website'=>'#304', 'workstation'=>'#a69944'
4110  );
4111  if (isset($arrayconvpictotocolor[$pictowithouttext])) {
4112  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4113  }
4114 
4115  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4116  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4117  $morestyle = '';
4118  $reg = array();
4119  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4120  $morecss .= ($morecss ? ' ' : '').$reg[1];
4121  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4122  }
4123  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4124  $morestyle = $reg[1];
4125  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4126  }
4127  $moreatt = trim($moreatt);
4128 
4129  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4130  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4131  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4132  $enabledisablehtml .= $titlealt;
4133  }*/
4134  $enabledisablehtml .= '</span>';
4135 
4136  return $enabledisablehtml;
4137  }
4138 
4139  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4140  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4141  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4142  $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
4143  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4144  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4145  }
4146 
4147  // If we ask an image into $url/$mymodule/img (instead of default path)
4148  $regs = array();
4149  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4150  $picto = $regs[1];
4151  $path = $regs[2]; // $path is $mymodule
4152  }
4153 
4154  // Clean parameters
4155  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4156  $picto .= '.png';
4157  }
4158  // If alt path are defined, define url where img file is, according to physical path
4159  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4160  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4161  if ($type == 'main') {
4162  continue;
4163  }
4164  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4165  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4166  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4167  break;
4168  }
4169  }
4170 
4171  // $url is '' or '/custom', $path is current theme or
4172  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4173  }
4174 
4175  if ($srconly) {
4176  return $fullpathpicto;
4177  }
4178  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4179  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
4180 }
4181 
4195 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4196 {
4197  if (strpos($picto, '^') === 0) {
4198  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4199  } else {
4200  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4201  }
4202 }
4203 
4215 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4216 {
4217  global $conf;
4218 
4219  if (is_numeric($picto)) {
4220  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4221  //$picto = $leveltopicto[$picto];
4222  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4223  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4224  $picto .= '.png';
4225  }
4226 
4227  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4228 
4229  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4230 }
4231 
4243 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4244 {
4245  global $conf;
4246 
4247  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4248  $picto .= '.png';
4249  }
4250 
4251  if ($pictoisfullpath) {
4252  $path = $picto;
4253  } else {
4254  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4255 
4256  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4257  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4258 
4259  if (file_exists($themepath)) {
4260  $path = $themepath;
4261  }
4262  }
4263  }
4264 
4265  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4266 }
4267 
4280 function img_action($titlealt, $numaction, $picto = '')
4281 {
4282  global $langs;
4283 
4284  if (empty($titlealt) || $titlealt == 'default') {
4285  if ($numaction == '-1' || $numaction == 'ST_NO') {
4286  $numaction = -1;
4287  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4288  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4289  $numaction = 0;
4290  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4291  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4292  $numaction = 1;
4293  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4294  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4295  $numaction = 2;
4296  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4297  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4298  $numaction = 3;
4299  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4300  } else {
4301  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4302  $numaction = 0;
4303  }
4304  }
4305  if (!is_numeric($numaction)) {
4306  $numaction = 0;
4307  }
4308 
4309  return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png');
4310 }
4311 
4319 function img_pdf($titlealt = 'default', $size = 3)
4320 {
4321  global $langs;
4322 
4323  if ($titlealt == 'default') {
4324  $titlealt = $langs->trans('Show');
4325  }
4326 
4327  return img_picto($titlealt, 'pdf'.$size.'.png');
4328 }
4329 
4337 function img_edit_add($titlealt = 'default', $other = '')
4338 {
4339  global $langs;
4340 
4341  if ($titlealt == 'default') {
4342  $titlealt = $langs->trans('Add');
4343  }
4344 
4345  return img_picto($titlealt, 'edit_add.png', $other);
4346 }
4354 function img_edit_remove($titlealt = 'default', $other = '')
4355 {
4356  global $langs;
4357 
4358  if ($titlealt == 'default') {
4359  $titlealt = $langs->trans('Remove');
4360  }
4361 
4362  return img_picto($titlealt, 'edit_remove.png', $other);
4363 }
4364 
4373 function img_edit($titlealt = 'default', $float = 0, $other = '')
4374 {
4375  global $langs;
4376 
4377  if ($titlealt == 'default') {
4378  $titlealt = $langs->trans('Modify');
4379  }
4380 
4381  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4382 }
4383 
4392 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4393 {
4394  global $langs;
4395 
4396  if ($titlealt == 'default') {
4397  $titlealt = $langs->trans('View');
4398  }
4399 
4400  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4401 
4402  return img_picto($titlealt, 'view.png', $moreatt);
4403 }
4404 
4413 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4414 {
4415  global $langs;
4416 
4417  if ($titlealt == 'default') {
4418  $titlealt = $langs->trans('Delete');
4419  }
4420 
4421  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4422 }
4423 
4431 function img_printer($titlealt = "default", $other = '')
4432 {
4433  global $langs;
4434  if ($titlealt == "default") {
4435  $titlealt = $langs->trans("Print");
4436  }
4437  return img_picto($titlealt, 'printer.png', $other);
4438 }
4439 
4447 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4448 {
4449  global $langs;
4450 
4451  if ($titlealt == 'default') {
4452  $titlealt = $langs->trans('Split');
4453  }
4454 
4455  return img_picto($titlealt, 'split.png', $other);
4456 }
4457 
4465 function img_help($usehelpcursor = 1, $usealttitle = 1)
4466 {
4467  global $langs;
4468 
4469  if ($usealttitle) {
4470  if (is_string($usealttitle)) {
4471  $usealttitle = dol_escape_htmltag($usealttitle);
4472  } else {
4473  $usealttitle = $langs->trans('Info');
4474  }
4475  }
4476 
4477  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4478 }
4479 
4486 function img_info($titlealt = 'default')
4487 {
4488  global $langs;
4489 
4490  if ($titlealt == 'default') {
4491  $titlealt = $langs->trans('Informations');
4492  }
4493 
4494  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4495 }
4496 
4505 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4506 {
4507  global $langs;
4508 
4509  if ($titlealt == 'default') {
4510  $titlealt = $langs->trans('Warning');
4511  }
4512 
4513  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4514  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4515 }
4516 
4523 function img_error($titlealt = 'default')
4524 {
4525  global $langs;
4526 
4527  if ($titlealt == 'default') {
4528  $titlealt = $langs->trans('Error');
4529  }
4530 
4531  return img_picto($titlealt, 'error.png');
4532 }
4533 
4541 function img_next($titlealt = 'default', $moreatt = '')
4542 {
4543  global $langs;
4544 
4545  if ($titlealt == 'default') {
4546  $titlealt = $langs->trans('Next');
4547  }
4548 
4549  //return img_picto($titlealt, 'next.png', $moreatt);
4550  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4551 }
4552 
4560 function img_previous($titlealt = 'default', $moreatt = '')
4561 {
4562  global $langs;
4563 
4564  if ($titlealt == 'default') {
4565  $titlealt = $langs->trans('Previous');
4566  }
4567 
4568  //return img_picto($titlealt, 'previous.png', $moreatt);
4569  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4570 }
4571 
4580 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4581 {
4582  global $langs;
4583 
4584  if ($titlealt == 'default') {
4585  $titlealt = $langs->trans('Down');
4586  }
4587 
4588  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4589 }
4590 
4599 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4600 {
4601  global $langs;
4602 
4603  if ($titlealt == 'default') {
4604  $titlealt = $langs->trans('Up');
4605  }
4606 
4607  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4608 }
4609 
4618 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4619 {
4620  global $langs;
4621 
4622  if ($titlealt == 'default') {
4623  $titlealt = $langs->trans('Left');
4624  }
4625 
4626  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4627 }
4628 
4637 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4638 {
4639  global $langs;
4640 
4641  if ($titlealt == 'default') {
4642  $titlealt = $langs->trans('Right');
4643  }
4644 
4645  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4646 }
4647 
4655 function img_allow($allow, $titlealt = 'default')
4656 {
4657  global $langs;
4658 
4659  if ($titlealt == 'default') {
4660  $titlealt = $langs->trans('Active');
4661  }
4662 
4663  if ($allow == 1) {
4664  return img_picto($titlealt, 'tick.png');
4665  }
4666 
4667  return '-';
4668 }
4669 
4677 function img_credit_card($brand, $morecss = null)
4678 {
4679  if (is_null($morecss)) {
4680  $morecss = 'fa-2x';
4681  }
4682 
4683  if ($brand == 'visa' || $brand == 'Visa') {
4684  $brand = 'cc-visa';
4685  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4686  $brand = 'cc-mastercard';
4687  } elseif ($brand == 'amex' || $brand == 'American Express') {
4688  $brand = 'cc-amex';
4689  } elseif ($brand == 'discover' || $brand == 'Discover') {
4690  $brand = 'cc-discover';
4691  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4692  $brand = 'cc-jcb';
4693  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4694  $brand = 'cc-diners-club';
4695  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4696  $brand = 'credit-card';
4697  }
4698 
4699  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4700 }
4701 
4710 function img_mime($file, $titlealt = '', $morecss = '')
4711 {
4712  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4713 
4714  $mimetype = dol_mimetype($file, '', 1);
4715  $mimeimg = dol_mimetype($file, '', 2);
4716  $mimefa = dol_mimetype($file, '', 4);
4717 
4718  if (empty($titlealt)) {
4719  $titlealt = 'Mime type: '.$mimetype;
4720  }
4721 
4722  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
4723  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
4724 }
4725 
4726 
4734 function img_search($titlealt = 'default', $other = '')
4735 {
4736  global $conf, $langs;
4737 
4738  if ($titlealt == 'default') {
4739  $titlealt = $langs->trans('Search');
4740  }
4741 
4742  $img = img_picto($titlealt, 'search.png', $other, false, 1);
4743 
4744  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
4745  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4746 
4747  return $input;
4748 }
4749 
4757 function img_searchclear($titlealt = 'default', $other = '')
4758 {
4759  global $conf, $langs;
4760 
4761  if ($titlealt == 'default') {
4762  $titlealt = $langs->trans('Search');
4763  }
4764 
4765  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
4766 
4767  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
4768  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4769 
4770  return $input;
4771 }
4772 
4784 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
4785 {
4786  global $conf, $langs;
4787 
4788  if ($infoonimgalt) {
4789  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
4790  } else {
4791  if (empty($conf->use_javascript_ajax)) {
4792  $textfordropdown = '';
4793  }
4794 
4795  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
4796  $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>');
4797 
4798  if ($textfordropdown) {
4799  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
4800  $tmpresult .= '<script type="text/javascript">
4801  jQuery(document).ready(function() {
4802  jQuery(".'.$class.'text").click(function() {
4803  console.log("toggle text");
4804  jQuery(".'.$class.'").toggle();
4805  });
4806  });
4807  </script>';
4808 
4809  $result = $tmpresult.$result;
4810  }
4811  }
4812 
4813  return $result;
4814 }
4815 
4816 
4828 function dol_print_error($db = '', $error = '', $errors = null)
4829 {
4830  global $conf, $langs, $argv;
4831  global $dolibarr_main_prod;
4832 
4833  $out = '';
4834  $syslog = '';
4835 
4836  // If error occurs before the $lang object was loaded
4837  if (!$langs) {
4838  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
4839  $langs = new Translate('', $conf);
4840  $langs->load("main");
4841  }
4842 
4843  // Load translation files required by the error messages
4844  $langs->loadLangs(array('main', 'errors'));
4845 
4846  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
4847  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
4848  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
4849  $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";
4850  }
4851  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
4852 
4853  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
4854  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
4855  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
4856  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
4857  }
4858  if (function_exists("phpversion")) {
4859  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
4860  }
4861  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
4862  if (function_exists("php_uname")) {
4863  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
4864  }
4865  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
4866  $out .= "<br>\n";
4867  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
4868  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
4869  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
4870  $out .= "<br>\n";
4871  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
4872  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
4873  } else // Mode CLI
4874  {
4875  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
4876  $syslog .= "pid=".dol_getmypid();
4877  }
4878 
4879  if (!empty($conf->modules)) {
4880  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
4881  }
4882 
4883  if (is_object($db)) {
4884  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
4885  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
4886  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ? dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
4887  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
4888  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
4889  $out .= "<br>\n";
4890  } else // Mode CLI
4891  {
4892  // No dol_escape_htmltag for output, we are in CLI mode
4893  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
4894  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
4895  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
4896  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
4897  }
4898  $syslog .= ", sql=".$db->lastquery();
4899  $syslog .= ", db_error=".$db->lasterror();
4900  }
4901 
4902  if ($error || $errors) {
4903  $langs->load("errors");
4904 
4905  // Merge all into $errors array
4906  if (is_array($error) && is_array($errors)) {
4907  $errors = array_merge($error, $errors);
4908  } elseif (is_array($error)) {
4909  $errors = $error;
4910  } elseif (is_array($errors)) {
4911  $errors = array_merge(array($error), $errors);
4912  } else {
4913  $errors = array_merge(array($error));
4914  }
4915 
4916  foreach ($errors as $msg) {
4917  if (empty($msg)) {
4918  continue;
4919  }
4920  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
4921  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
4922  } else // Mode CLI
4923  {
4924  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
4925  }
4926  $syslog .= ", msg=".$msg;
4927  }
4928  }
4929  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
4930  xdebug_print_function_stack();
4931  $out .= '<b>XDebug informations:</b>'."<br>\n";
4932  $out .= 'File: '.xdebug_call_file()."<br>\n";
4933  $out .= 'Line: '.xdebug_call_line()."<br>\n";
4934  $out .= 'Function: '.xdebug_call_function()."<br>\n";
4935  $out .= "<br>\n";
4936  }
4937 
4938  // Return a http error code if possible
4939  if (!headers_sent()) {
4940  http_response_code(500);
4941  }
4942 
4943  if (empty($dolibarr_main_prod)) {
4944  print $out;
4945  } else {
4946  if (empty($langs->defaultlang)) {
4947  $langs->setDefaultLang();
4948  }
4949  $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.
4950  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
4951  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";
4952  print $langs->trans("DolibarrHasDetectedError").'. ';
4953  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
4954  define("MAIN_CORE_ERROR", 1);
4955  }
4956 
4957  dol_syslog("Error ".$syslog, LOG_ERR);
4958 }
4959 
4970 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
4971 {
4972  global $langs, $conf;
4973 
4974  if (empty($email)) {
4975  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
4976  }
4977 
4978  $langs->load("errors");
4979  $now = dol_now();
4980 
4981  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
4982  print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S'));
4983  if ($errormessage) {
4984  print '<br><br>'.$errormessage;
4985  }
4986  if (is_array($errormessages) && count($errormessages)) {
4987  foreach ($errormessages as $mesgtoshow) {
4988  print '<br><br>'.$mesgtoshow;
4989  }
4990  }
4991  print '</div></div>';
4992 }
4993 
5010 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5011 {
5012  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5013 }
5014 
5033 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5034 {
5035  global $conf, $langs, $form;
5036  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5037 
5038  if ($moreattrib == 'class="right"') {
5039  $prefix .= 'right '; // For backward compatibility
5040  }
5041 
5042  $sortorder = strtoupper($sortorder);
5043  $out = '';
5044  $sortimg = '';
5045 
5046  $tag = 'th';
5047  if ($thead == 2) {
5048  $tag = 'div';
5049  }
5050 
5051  $tmpsortfield = explode(',', $sortfield);
5052  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5053  $tmpfield = explode(',', $field);
5054  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5055 
5056  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5057  $prefix = 'wrapcolumntitle '.$prefix;
5058  }
5059 
5060  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5061  // If field is used as sort criteria we use a specific css class liste_titre_sel
5062  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5063  $liste_titre = 'liste_titre';
5064  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5065  $liste_titre = 'liste_titre_sel';
5066  }
5067 
5068  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5069  //$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)).'"' : '');
5070  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5071  $tagstart .= '>';
5072 
5073  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5074  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5075  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5076  $options = preg_replace('/&+/i', '&', $options);
5077  if (!preg_match('/^&/', $options)) {
5078  $options = '&'.$options;
5079  }
5080 
5081  $sortordertouseinlink = '';
5082  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5083  if (preg_match('/^DESC/i', $sortorder)) {
5084  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5085  } else { // We reverse the var $sortordertouseinlink
5086  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5087  }
5088  } else { // We are on field that is the first current sorting criteria
5089  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5090  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5091  } else {
5092  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5093  }
5094  }
5095  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5096  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5097  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5098  $out .= '>';
5099  }
5100  if ($tooltip) {
5101  // You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
5102  $tmptooltip = explode(':', $tooltip);
5103  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5104  } else {
5105  $out .= $langs->trans($name);
5106  }
5107 
5108  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5109  $out .= '</a>';
5110  }
5111 
5112  if (empty($thead) && $field) { // If this is a sort field
5113  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5114  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5115  $options = preg_replace('/&+/i', '&', $options);
5116  if (!preg_match('/^&/', $options)) {
5117  $options = '&'.$options;
5118  }
5119 
5120  if (!$sortorder || $field1 != $sortfield1) {
5121  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5122  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5123  } else {
5124  if (preg_match('/^DESC/', $sortorder)) {
5125  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5126  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5127  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5128  }
5129  if (preg_match('/^ASC/', $sortorder)) {
5130  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5131  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5132  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5133  }
5134  }
5135  }
5136 
5137  $tagend = '</'.$tag.'>';
5138 
5139  $out = $tagstart.$sortimg.$out.$tagend;
5140 
5141  return $out;
5142 }
5143 
5152 function print_titre($title)
5153 {
5154  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5155 
5156  print '<div class="titre">'.$title.'</div>';
5157 }
5158 
5170 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5171 {
5172  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5173 }
5174 
5188 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5189 {
5190  global $conf;
5191 
5192  $return = '';
5193 
5194  if ($picto == 'setup') {
5195  $picto = 'generic';
5196  }
5197 
5198  $return .= "\n";
5199  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5200  $return .= '<tr class="titre">';
5201  if ($picto) {
5202  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5203  }
5204  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5205  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5206  $return .= '</td>';
5207  if (dol_strlen($morehtmlcenter)) {
5208  $return .= '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
5209  }
5210  if (dol_strlen($morehtmlright)) {
5211  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle">'.$morehtmlright.'</td>';
5212  }
5213  $return .= '</tr></table>'."\n";
5214 
5215  return $return;
5216 }
5217 
5241 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 = '')
5242 {
5243  global $conf, $langs;
5244 
5245  $savlimit = $limit;
5246  $savtotalnboflines = $totalnboflines;
5247  $totalnboflines = abs((int) $totalnboflines);
5248 
5249  if ($picto == 'setup') {
5250  $picto = 'title_setup.png';
5251  }
5252  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5253  $picto = 'title.gif';
5254  }
5255  if ($limit < 0) {
5256  $limit = $conf->liste_limit;
5257  }
5258  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5259  $nextpage = 1;
5260  } else {
5261  $nextpage = 0;
5262  }
5263  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
5264 
5265  print "\n";
5266  print "<!-- Begin title -->\n";
5267  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5268 
5269  // Left
5270 
5271  if ($picto && $titre) {
5272  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5273  }
5274  print '<td class="nobordernopadding valignmiddle col-title">';
5275  print '<div class="titre inline-block">'.$titre;
5276  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5277  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5278  }
5279  print '</div></td>';
5280 
5281  // Center
5282  if ($morehtmlcenter) {
5283  print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
5284  }
5285 
5286  // Right
5287  print '<td class="nobordernopadding valignmiddle right">';
5288  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5289  if ($sortfield) {
5290  $options .= "&sortfield=".urlencode($sortfield);
5291  }
5292  if ($sortorder) {
5293  $options .= "&sortorder=".urlencode($sortorder);
5294  }
5295  // Show navigation bar
5296  $pagelist = '';
5297  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5298  if ($totalnboflines) { // If we know total nb of lines
5299  // Define nb of extra page links before and after selected page + ... + first or last
5300  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5301 
5302  if ($limit > 0) {
5303  $nbpages = ceil($totalnboflines / $limit);
5304  } else {
5305  $nbpages = 1;
5306  }
5307  $cpt = ($page - $maxnbofpage);
5308  if ($cpt < 0) {
5309  $cpt = 0;
5310  }
5311 
5312  if ($cpt >= 1) {
5313  if (empty($pagenavastextinput)) {
5314  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5315  if ($cpt > 2) {
5316  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5317  } elseif ($cpt == 2) {
5318  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5319  }
5320  }
5321  }
5322 
5323  do {
5324  if ($pagenavastextinput) {
5325  if ($cpt == $page) {
5326  $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5327  $pagelist .= '/';
5328  }
5329  } else {
5330  if ($cpt == $page) {
5331  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5332  } else {
5333  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5334  }
5335  }
5336  $cpt++;
5337  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5338 
5339  if (empty($pagenavastextinput)) {
5340  if ($cpt < $nbpages) {
5341  if ($cpt < $nbpages - 2) {
5342  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5343  } elseif ($cpt == $nbpages - 2) {
5344  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5345  }
5346  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5347  }
5348  } else {
5349  //var_dump($page.' '.$cpt.' '.$nbpages);
5350  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5351  }
5352  } else {
5353  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5354  }
5355  }
5356 
5357  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5358  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
5359  }
5360 
5361  // js to autoselect page field on focus
5362  if ($pagenavastextinput) {
5363  print ajax_autoselect('.pageplusone');
5364  }
5365 
5366  print '</td>';
5367 
5368  print '</tr></table>'."\n";
5369  print "<!-- End title -->\n\n";
5370 }
5371 
5387 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '')
5388 {
5389  global $conf, $langs;
5390 
5391  print '<div class="pagination"><ul>';
5392  if ($beforearrows) {
5393  print '<li class="paginationbeforearrows">';
5394  print $beforearrows;
5395  print '</li>';
5396  }
5397  if ((int) $limit > 0 && empty($hideselectlimit)) {
5398  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5399  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5400  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5401  //$pagesizechoices.=',2:2';
5402  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5403  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5404  }
5405 
5406  print '<li class="pagination">';
5407  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5408  $tmpchoice = explode(',', $pagesizechoices);
5409  $tmpkey = $limit.':'.$limit;
5410  if (!in_array($tmpkey, $tmpchoice)) {
5411  $tmpchoice[] = $tmpkey;
5412  }
5413  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5414  if (!in_array($tmpkey, $tmpchoice)) {
5415  $tmpchoice[] = $tmpkey;
5416  }
5417  asort($tmpchoice, SORT_NUMERIC);
5418  foreach ($tmpchoice as $val) {
5419  $selected = '';
5420  $tmp = explode(':', $val);
5421  $key = $tmp[0];
5422  $val = $tmp[1];
5423  if ($key != '' && $val != '') {
5424  if ((int) $key == (int) $limit) {
5425  $selected = ' selected="selected"';
5426  }
5427  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5428  }
5429  }
5430  print '</select>';
5431  if ($conf->use_javascript_ajax) {
5432  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5433  <script>
5434  jQuery(document).ready(function () {
5435  jQuery(".selectlimit").change(function() {
5436  console.log("Change limit. Send submit");
5437  $(this).parents(\'form:first\').submit();
5438  });
5439  });
5440  </script>
5441  ';
5442  }
5443  print '</li>';
5444  }
5445  if ($page > 0) {
5446  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>';
5447  }
5448  if ($betweenarrows) {
5449  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5450  print $betweenarrows;
5451  print '<!--</div>-->';
5452  }
5453  if ($nextpage > 0) {
5454  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>';
5455  }
5456  if ($afterarrows) {
5457  print '<li class="paginationafterarrows">';
5458  print $afterarrows;
5459  print '</li>';
5460  }
5461  print '</ul></div>'."\n";
5462 }
5463 
5464 
5476 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5477 {
5478  $morelabel = '';
5479 
5480  if (preg_match('/%/', $rate)) {
5481  $rate = str_replace('%', '', $rate);
5482  $addpercent = true;
5483  }
5484  $reg = array();
5485  if (preg_match('/\((.*)\)/', $rate, $reg)) {
5486  $morelabel = ' ('.$reg[1].')';
5487  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5488  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5489  }
5490  if (preg_match('/\*/', $rate)) {
5491  $rate = str_replace('*', '', $rate);
5492  $info_bits |= 1;
5493  }
5494 
5495  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5496  if (!preg_match('/\//', $rate)) {
5497  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5498  } else {
5499  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5500  $ret = $rate.($addpercent ? '%' : '');
5501  }
5502  if (($info_bits & 1) && $usestarfornpr >= 0) {
5503  $ret .= ' *';
5504  }
5505  $ret .= $morelabel;
5506  return $ret;
5507 }
5508 
5509 
5525 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5526 {
5527  global $langs, $conf;
5528 
5529  // Clean parameters
5530  if (empty($amount)) {
5531  $amount = 0; // To have a numeric value if amount not defined or = ''
5532  }
5533  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5534  if ($rounding < 0) {
5535  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5536  }
5537  $nbdecimal = $rounding;
5538 
5539  if ($outlangs === 'none') {
5540  // Use international separators
5541  $dec = '.';
5542  $thousand = '';
5543  } else {
5544  // Output separators by default (french)
5545  $dec = ',';
5546  $thousand = ' ';
5547 
5548  // If $outlangs not forced, we use use language
5549  if (!is_object($outlangs)) {
5550  $outlangs = $langs;
5551  }
5552 
5553  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5554  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5555  }
5556  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5557  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5558  }
5559  if ($thousand == 'None') {
5560  $thousand = '';
5561  } elseif ($thousand == 'Space') {
5562  $thousand = ' ';
5563  }
5564  }
5565  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5566 
5567  //print "amount=".$amount."-";
5568  $amount = str_replace(',', '.', $amount); // should be useless
5569  //print $amount."-";
5570  $datas = explode('.', $amount);
5571  $decpart = isset($datas[1]) ? $datas[1] : '';
5572  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5573  //print "decpart=".$decpart."<br>";
5574  $end = '';
5575 
5576  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5577  if (dol_strlen($decpart) > $nbdecimal) {
5578  $nbdecimal = dol_strlen($decpart);
5579  }
5580  // Si on depasse max
5581  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5582  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5583  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5584  // Si un affichage est tronque, on montre des ...
5585  $end = '...';
5586  }
5587  }
5588 
5589  // If force rounding
5590  if ($forcerounding >= 0) {
5591  $nbdecimal = $forcerounding;
5592  }
5593 
5594  // Format number
5595  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5596  if ($form) {
5597  $output = preg_replace('/\s/', '&nbsp;', $output);
5598  $output = preg_replace('/\'/', '&#039;', $output);
5599  }
5600  // Add symbol of currency if requested
5601  $cursymbolbefore = $cursymbolafter = '';
5602  if ($currency_code && is_object($outlangs)) {
5603  if ($currency_code == 'auto') {
5604  $currency_code = $conf->currency;
5605  }
5606 
5607  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD');
5608  $listoflanguagesbefore = array('nl_NL');
5609  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5610  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5611  } else {
5612  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5613  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5614  }
5615  }
5616  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5617 
5618  return $output;
5619 }
5620 
5645 function price2num($amount, $rounding = '', $option = 0)
5646 {
5647  global $langs, $conf;
5648 
5649  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5650  // Numbers must be '1234.56'
5651  // Decimal delimiter for PHP and database SQL requests must be '.'
5652  $dec = ',';
5653  $thousand = ' ';
5654  if (is_null($langs)) { // $langs is not defined, we use english values.
5655  $dec = '.';
5656  $thousand = ',';
5657  } else {
5658  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5659  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5660  }
5661  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5662  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5663  }
5664  }
5665  if ($thousand == 'None') {
5666  $thousand = '';
5667  } elseif ($thousand == 'Space') {
5668  $thousand = ' ';
5669  }
5670  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5671 
5672  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5673  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5674  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5675  if (!is_numeric($amount)) {
5676  $amount = preg_replace('/[a-zA-Z\/\\\*\(\)<>\_]/', '', $amount);
5677  }
5678 
5679  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
5680  $amount = str_replace($thousand, '', $amount);
5681  }
5682 
5683  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5684  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
5685  // So if number was already a good number, it is converted into local Dolibarr setup.
5686  if (is_numeric($amount)) {
5687  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5688  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5689  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5690  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5691  $amount = number_format($amount, $nbofdec, $dec, $thousand);
5692  }
5693  //print "QQ".$amount."<br>\n";
5694 
5695  // Now make replace (the main goal of function)
5696  if ($thousand != ',' && $thousand != '.') {
5697  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5698  }
5699 
5700  $amount = str_replace(' ', '', $amount); // To avoid spaces
5701  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5702  $amount = str_replace($dec, '.', $amount);
5703 
5704  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5705  }
5706  //print ' XX'.$amount.' '.$rounding;
5707 
5708  // Now, $amount is a real PHP float number. We make a rounding if required.
5709  if ($rounding) {
5710  $nbofdectoround = '';
5711  if ($rounding == 'MU') {
5712  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5713  } elseif ($rounding == 'MT') {
5714  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
5715  } elseif ($rounding == 'MS') {
5716  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
5717  } elseif ($rounding == 'CU') {
5718  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
5719  } elseif ($rounding == 'CT') {
5720  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
5721  } elseif (is_numeric($rounding)) {
5722  $nbofdectoround = (int) $rounding;
5723  }
5724 
5725  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
5726  if (dol_strlen($nbofdectoround)) {
5727  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
5728  } else {
5729  return 'ErrorBadParameterProvidedToFunction';
5730  }
5731  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
5732 
5733  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5734  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
5735  if (is_numeric($amount)) {
5736  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5737  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5738  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5739  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5740  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
5741  }
5742  //print "TT".$amount.'<br>';
5743 
5744  // Always make replace because each math function (like round) replace
5745  // with local values and we want a number that has a SQL string format x.y
5746  if ($thousand != ',' && $thousand != '.') {
5747  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5748  }
5749 
5750  $amount = str_replace(' ', '', $amount); // To avoid spaces
5751  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5752  $amount = str_replace($dec, '.', $amount);
5753 
5754  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5755  }
5756 
5757  return $amount;
5758 }
5759 
5772 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
5773 {
5774  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
5775 
5776  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
5777  $dimension = $dimension * 1000000;
5778  $unit = $unit - 6;
5779  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
5780  $dimension = $dimension * 1000;
5781  $unit = $unit - 3;
5782  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
5783  $dimension = $dimension / 1000000;
5784  $unit = $unit + 6;
5785  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
5786  $dimension = $dimension / 1000;
5787  $unit = $unit + 3;
5788  }
5789  // Special case when we want output unit into pound or ounce
5790  /* TODO
5791  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
5792  {
5793  $dimension = // convert dimension from standard unit into ounce or pound
5794  $unit = $forceunitoutput;
5795  }
5796  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
5797  {
5798  $dimension = // convert dimension from standard unit into ounce or pound
5799  $unit = $forceunitoutput;
5800  }*/
5801 
5802  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
5803  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
5804 
5805  return $ret;
5806 }
5807 
5808 
5821 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
5822 {
5823  global $db, $conf, $mysoc;
5824 
5825  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
5826  $thirdparty_seller = $mysoc;
5827  }
5828 
5829  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);
5830 
5831  $vatratecleaned = $vatrate;
5832  $reg = array();
5833  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
5834  $vatratecleaned = trim($reg[1]);
5835  $vatratecode = $reg[2];
5836  }
5837 
5838  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
5839  {
5840  return 0;
5841  }*/
5842 
5843  // Some test to guess with no need to make database access
5844  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
5845  if ($local == 1) {
5846  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
5847  return 0;
5848  }
5849  if ($thirdparty_seller->id == $mysoc->id) {
5850  if (!$thirdparty_buyer->localtax1_assuj) {
5851  return 0;
5852  }
5853  } else {
5854  if (!$thirdparty_seller->localtax1_assuj) {
5855  return 0;
5856  }
5857  }
5858  }
5859 
5860  if ($local == 2) {
5861  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
5862  if (!$mysoc->localtax2_assuj) {
5863  return 0; // If main vat is 0, IRPF may be different than 0.
5864  }
5865  if ($thirdparty_seller->id == $mysoc->id) {
5866  if (!$thirdparty_buyer->localtax2_assuj) {
5867  return 0;
5868  }
5869  } else {
5870  if (!$thirdparty_seller->localtax2_assuj) {
5871  return 0;
5872  }
5873  }
5874  }
5875  } else {
5876  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
5877  return 0;
5878  }
5879  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
5880  return 0;
5881  }
5882  }
5883 
5884  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
5885  if (in_array($mysoc->country_code, array('ES'))) {
5886  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
5887  }
5888 
5889  // Search local taxes
5890  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
5891  if ($local == 1) {
5892  if ($thirdparty_seller != $mysoc) {
5893  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
5894  return $thirdparty_seller->localtax1_value;
5895  }
5896  } else { // i am the seller
5897  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
5898  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
5899  }
5900  }
5901  }
5902  if ($local == 2) {
5903  if ($thirdparty_seller != $mysoc) {
5904  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
5905  // TODO We should also return value defined on thirdparty only if defined
5906  return $thirdparty_seller->localtax2_value;
5907  }
5908  } else { // i am the seller
5909  if (in_array($mysoc->country_code, array('ES'))) {
5910  return $thirdparty_buyer->localtax2_value;
5911  } else {
5912  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
5913  }
5914  }
5915  }
5916  }
5917 
5918  // By default, search value of local tax on line of common tax
5919  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
5920  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5921  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
5922  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
5923  if (!empty($vatratecode)) {
5924  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
5925  } else {
5926  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
5927  }
5928 
5929  $resql = $db->query($sql);
5930 
5931  if ($resql) {
5932  $obj = $db->fetch_object($resql);
5933  if ($obj) {
5934  if ($local == 1) {
5935  return $obj->localtax1;
5936  } elseif ($local == 2) {
5937  return $obj->localtax2;
5938  }
5939  }
5940  }
5941 
5942  return 0;
5943 }
5944 
5945 
5954 function isOnlyOneLocalTax($local)
5955 {
5956  $tax = get_localtax_by_third($local);
5957 
5958  $valors = explode(":", $tax);
5959 
5960  if (count($valors) > 1) {
5961  return false;
5962  } else {
5963  return true;
5964  }
5965 }
5966 
5973 function get_localtax_by_third($local)
5974 {
5975  global $db, $mysoc;
5976  $sql = "SELECT t.localtax1, t.localtax2 ";
5977  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
5978  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux=(";
5979  $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";
5980  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1";
5981  $sql .= " )";
5982 
5983  $resql = $db->query($sql);
5984  if ($resql) {
5985  $obj = $db->fetch_object($resql);
5986  if ($local == 1) {
5987  return $obj->localtax1;
5988  } elseif ($local == 2) {
5989  return $obj->localtax2;
5990  }
5991  }
5992 
5993  return 0;
5994 }
5995 
5996 
6008 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6009 {
6010  global $db, $mysoc;
6011 
6012  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6013 
6014  // Search local taxes
6015  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6016  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6017  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6018  if ($firstparamisid) {
6019  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6020  } else {
6021  $vatratecleaned = $vatrate;
6022  $vatratecode = '';
6023  $reg = array();
6024  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6025  $vatratecleaned = $reg[1];
6026  $vatratecode = $reg[2];
6027  }
6028 
6029  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6030  /*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 ??
6031  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6032  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6033  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6034  if ($vatratecode) {
6035  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6036  }
6037  }
6038 
6039  $resql = $db->query($sql);
6040  if ($resql) {
6041  $obj = $db->fetch_object($resql);
6042  if ($obj) {
6043  return array(
6044  'rowid'=>$obj->rowid,
6045  'code'=>$obj->code,
6046  'rate'=>$obj->rate,
6047  'localtax1'=>$obj->localtax1,
6048  'localtax1_type'=>$obj->localtax1_type,
6049  'localtax2'=>$obj->localtax2,
6050  'localtax2_type'=>$obj->localtax2_type,
6051  'npr'=>$obj->npr,
6052  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6053  'accountancy_code_buy'=>$obj->accountancy_code_buy
6054  );
6055  } else {
6056  return array();
6057  }
6058  } else {
6059  dol_print_error($db);
6060  }
6061 
6062  return array();
6063 }
6064 
6081 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6082 {
6083  global $db, $mysoc;
6084 
6085  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6086 
6087  // Search local taxes
6088  $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";
6089  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6090  if ($firstparamisid) {
6091  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6092  } else {
6093  $vatratecleaned = $vatrate;
6094  $vatratecode = '';
6095  $reg = array();
6096  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6097  $vatratecleaned = $reg[1];
6098  $vatratecode = $reg[2];
6099  }
6100 
6101  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6102  if ($mysoc->country_code == 'ES') {
6103  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ??
6104  } else {
6105  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape(empty($seller->country_code) ? $mysoc->country_code : $seller->country_code)."'";
6106  }
6107  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6108  if ($vatratecode) {
6109  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6110  }
6111  }
6112 
6113  $resql = $db->query($sql);
6114  if ($resql) {
6115  $obj = $db->fetch_object($resql);
6116 
6117  if ($obj) {
6118  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6119 
6120  if ($local == 1) {
6121  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6122  } elseif ($local == 2) {
6123  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6124  } else {
6125  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);
6126  }
6127  }
6128  }
6129 
6130  return array();
6131 }
6132 
6143 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6144 {
6145  global $db, $conf, $mysoc;
6146 
6147  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6148 
6149  $ret = 0;
6150  $found = 0;
6151 
6152  if ($idprod > 0) {
6153  // Load product
6154  $product = new Product($db);
6155  $result = $product->fetch($idprod);
6156 
6157  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If country to consider is ours
6158  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6159  $product->get_buyprice($idprodfournprice, 0, 0, 0);
6160  $ret = $product->vatrate_supplier;
6161  if ($product->default_vat_code) {
6162  $ret .= ' ('.$product->default_vat_code.')';
6163  }
6164  } else {
6165  $ret = $product->tva_tx; // Default vat of product we defined
6166  if ($product->default_vat_code) {
6167  $ret .= ' ('.$product->default_vat_code.')';
6168  }
6169  }
6170  $found = 1;
6171  } else {
6172  // TODO Read default product vat according to product and another countrycode.
6173  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6174  }
6175  }
6176 
6177  if (!$found) {
6178  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6179  // If vat of product for the country not found or not defined, we return the first higher vat of country.
6180  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6181  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6182  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6183  $sql .= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
6184  $sql .= $db->plimit(1);
6185 
6186  $resql = $db->query($sql);
6187  if ($resql) {
6188  $obj = $db->fetch_object($resql);
6189  if ($obj) {
6190  $ret = $obj->vat_rate;
6191  if ($obj->default_vat_code) {
6192  $ret .= ' ('.$obj->default_vat_code.')';
6193  }
6194  }
6195  $db->free($resql);
6196  } else {
6197  dol_print_error($db);
6198  }
6199  } else {
6200  $ret = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS; // Forced value if autodetect fails
6201  }
6202  }
6203 
6204  dol_syslog("get_product_vat_for_country: ret=".$ret);
6205  return $ret;
6206 }
6207 
6217 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6218 {
6219  global $db, $mysoc;
6220 
6221  if (!class_exists('Product')) {
6222  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6223  }
6224 
6225  $ret = 0;
6226  $found = 0;
6227 
6228  if ($idprod > 0) {
6229  // Load product
6230  $product = new Product($db);
6231  $result = $product->fetch($idprod);
6232 
6233  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6234  /* Not defined yet, so we don't use this
6235  if ($local==1) $ret=$product->localtax1_tx;
6236  elseif ($local==2) $ret=$product->localtax2_tx;
6237  $found=1;
6238  */
6239  } else {
6240  // TODO Read default product vat according to product and another countrycode.
6241  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6242  }
6243  }
6244 
6245  if (!$found) {
6246  // If vat of product for the country not found or not defined, we return higher vat of country.
6247  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6248  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6249  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6250  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6251  $sql .= $db->plimit(1);
6252 
6253  $resql = $db->query($sql);
6254  if ($resql) {
6255  $obj = $db->fetch_object($resql);
6256  if ($obj) {
6257  if ($local == 1) {
6258  $ret = $obj->localtax1;
6259  } elseif ($local == 2) {
6260  $ret = $obj->localtax2;
6261  }
6262  }
6263  } else {
6264  dol_print_error($db);
6265  }
6266  }
6267 
6268  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6269  return $ret;
6270 }
6271 
6288 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6289 {
6290  global $conf;
6291 
6292  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6293 
6294  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6295  $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;
6296 
6297  $seller_country_code = $thirdparty_seller->country_code;
6298  $seller_in_cee = isInEEC($thirdparty_seller);
6299 
6300  $buyer_country_code = $thirdparty_buyer->country_code;
6301  $buyer_in_cee = isInEEC($thirdparty_buyer);
6302 
6303  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 : ''));
6304 
6305  // 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)
6306  // we use the buyer VAT.
6307  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6308  if ($seller_in_cee && $buyer_in_cee) {
6309  $isacompany = $thirdparty_buyer->isACompany();
6310  if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) {
6311  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6312  if (!isValidVATID($thirdparty_buyer)) {
6313  $isacompany = 0;
6314  }
6315  }
6316 
6317  if (!$isacompany) {
6318  //print 'VATRULE 0';
6319  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6320  }
6321  }
6322  }
6323 
6324  // If seller does not use VAT
6325  if (!$seller_use_vat) {
6326  //print 'VATRULE 1';
6327  return 0;
6328  }
6329 
6330  // 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.
6331 
6332  // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
6333  if (($seller_country_code == $buyer_country_code)
6334  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6335  //print 'VATRULE 2';
6336  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6337  }
6338 
6339  // 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.
6340  // 'VATRULE 3' - Not supported
6341 
6342  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
6343  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6344  if (($seller_in_cee && $buyer_in_cee)) {
6345  $isacompany = $thirdparty_buyer->isACompany();
6346  if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) {
6347  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6348  if (!isValidVATID($thirdparty_buyer)) {
6349  $isacompany = 0;
6350  }
6351  }
6352 
6353  if (!$isacompany) {
6354  //print 'VATRULE 4';
6355  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6356  } else {
6357  //print 'VATRULE 5';
6358  return 0;
6359  }
6360  }
6361 
6362  // Si (vendeur dans Communaute europeene et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6363  // I don't see any use case that need this rule.
6364  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6365  $isacompany = $thirdparty_buyer->isACompany();
6366  if (!$isacompany) {
6367  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6368  //print 'VATRULE extra';
6369  }
6370  }
6371 
6372  // Sinon la TVA proposee par defaut=0. Fin de regle.
6373  // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
6374  //print 'VATRULE 6';
6375  return 0;
6376 }
6377 
6378 
6389 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6390 {
6391  global $db;
6392 
6393  if ($idprodfournprice > 0) {
6394  if (!class_exists('ProductFournisseur')) {
6395  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6396  }
6397  $prodprice = new ProductFournisseur($db);
6398  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6399  return $prodprice->fourn_tva_npr;
6400  } elseif ($idprod > 0) {
6401  if (!class_exists('Product')) {
6402  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6403  }
6404  $prod = new Product($db);
6405  $prod->fetch($idprod);
6406  return $prod->tva_npr;
6407  }
6408 
6409  return 0;
6410 }
6411 
6425 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6426 {
6427  global $mysoc;
6428 
6429  if (!is_object($thirdparty_seller)) {
6430  return -1;
6431  }
6432  if (!is_object($thirdparty_buyer)) {
6433  return -1;
6434  }
6435 
6436  if ($local == 1) { // Localtax 1
6437  if ($mysoc->country_code == 'ES') {
6438  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6439  return 0;
6440  }
6441  } else {
6442  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6443  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6444  return 0;
6445  }
6446  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6447  return 0;
6448  }
6449  }
6450  } elseif ($local == 2) { //I Localtax 2
6451  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6452  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6453  return 0;
6454  }
6455  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6456  return 0;
6457  }
6458  }
6459 
6460  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6461  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6462  }
6463 
6464  return 0;
6465 }
6466 
6475 function yn($yesno, $case = 1, $color = 0)
6476 {
6477  global $langs;
6478 
6479  $result = 'unknown';
6480  $classname = '';
6481  if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') { // A mettre avant test sur no a cause du == 0
6482  $result = $langs->trans('yes');
6483  if ($case == 1 || $case == 3) {
6484  $result = $langs->trans("Yes");
6485  }
6486  if ($case == 2) {
6487  $result = '<input type="checkbox" value="1" checked disabled>';
6488  }
6489  if ($case == 3) {
6490  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6491  }
6492 
6493  $classname = 'ok';
6494  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6495  $result = $langs->trans("no");
6496  if ($case == 1 || $case == 3) {
6497  $result = $langs->trans("No");
6498  }
6499  if ($case == 2) {
6500  $result = '<input type="checkbox" value="0" disabled>';
6501  }
6502  if ($case == 3) {
6503  $result = '<input type="checkbox" value="0" disabled> '.$result;
6504  }
6505 
6506  if ($color == 2) {
6507  $classname = 'ok';
6508  } else {
6509  $classname = 'error';
6510  }
6511  }
6512  if ($color) {
6513  return '<span class="'.$classname.'">'.$result.'</span>';
6514  }
6515  return $result;
6516 }
6517 
6533 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6534 {
6535  global $conf;
6536 
6537  if (empty($modulepart) && !empty($object->module)) {
6538  $modulepart = $object->module;
6539  }
6540 
6541  $path = '';
6542 
6543  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6544  if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) {
6545  $arrayforoldpath[] = 'product';
6546  }
6547  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6548  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6549  if (empty($alpha)) {
6550  $num = preg_replace('/([^0-9])/i', '', $num);
6551  } else {
6552  $num = preg_replace('/^.*\-/i', '', $num);
6553  }
6554  $num = substr("000".$num, -$level);
6555  if ($level == 1) {
6556  $path = substr($num, 0, 1);
6557  }
6558  if ($level == 2) {
6559  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6560  }
6561  if ($level == 3) {
6562  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6563  }
6564  } else {
6565  // We will enhance here a common way of forging path for document storage.
6566  // In a future, we may distribute directories on several levels depending on setup and object.
6567  // Here, $object->id, $object->ref and $modulepart are required.
6568  //var_dump($modulepart);
6569  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6570  }
6571 
6572  if (empty($withoutslash) && !empty($path)) {
6573  $path .= '/';
6574  }
6575 
6576  return $path;
6577 }
6578 
6587 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6588 {
6589  global $conf;
6590 
6591  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6592 
6593  $dir_osencoded = dol_osencode($dir);
6594  if (@is_dir($dir_osencoded)) {
6595  return 0;
6596  }
6597 
6598  $nberr = 0;
6599  $nbcreated = 0;
6600 
6601  $ccdir = '';
6602  if (!empty($dataroot)) {
6603  // Remove data root from loop
6604  $dir = str_replace($dataroot.'/', '', $dir);
6605  $ccdir = $dataroot.'/';
6606  }
6607 
6608  $cdir = explode("/", $dir);
6609  $num = count($cdir);
6610  for ($i = 0; $i < $num; $i++) {
6611  if ($i > 0) {
6612  $ccdir .= '/'.$cdir[$i];
6613  } else {
6614  $ccdir .= $cdir[$i];
6615  }
6616  if (preg_match("/^.:$/", $ccdir, $regs)) {
6617  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6618  }
6619 
6620  // Attention, le is_dir() peut echouer bien que le rep existe.
6621  // (ex selon config de open_basedir)
6622  if ($ccdir) {
6623  $ccdir_osencoded = dol_osencode($ccdir);
6624  if (!@is_dir($ccdir_osencoded)) {
6625  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6626 
6627  umask(0);
6628  $dirmaskdec = octdec((string) $newmask);
6629  if (empty($newmask)) {
6630  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6631  }
6632  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
6633  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
6634  // Si le is_dir a renvoye une fausse info, alors on passe ici.
6635  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
6636  $nberr++;
6637  } else {
6638  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
6639  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
6640  $nbcreated++;
6641  }
6642  } else {
6643  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
6644  }
6645  }
6646  }
6647  return ($nberr ? -$nberr : $nbcreated);
6648 }
6649 
6650 
6656 function picto_required()
6657 {
6658  return '<span class="fieldrequired">*</span>';
6659 }
6660 
6661 
6678 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
6679 {
6680  if ($removelinefeed == 2) {
6681  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
6682  }
6683  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
6684 
6685  // 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)
6686  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
6687 
6688  $temp = str_replace('< ', '__ltspace__', $temp);
6689 
6690  if ($strip_tags) {
6691  $temp = strip_tags($temp);
6692  } else {
6693  // 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).
6694  $pattern = "/<[^<>]+>/";
6695  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
6696  // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
6697  // pass 2 - $temp after pass 2: 0000-021
6698  $tempbis = $temp;
6699  do {
6700  $temp = $tempbis;
6701  $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
6702  $tempbis = preg_replace($pattern, '', $tempbis);
6703  //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
6704  } while ($tempbis != $temp);
6705 
6706  $temp = $tempbis;
6707 
6708  // 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).
6709  $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
6710  }
6711 
6712  $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
6713 
6714  // Remove also carriage returns
6715  if ($removelinefeed == 1) {
6716  $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
6717  }
6718 
6719  // And double quotes
6720  if ($removedoublespaces) {
6721  while (strpos($temp, " ")) {
6722  $temp = str_replace(" ", " ", $temp);
6723  }
6724  }
6725 
6726  $temp = str_replace('__ltspace__', '< ', $temp);
6727 
6728  return trim($temp);
6729 }
6730 
6744 function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0)
6745 {
6746  $allowed_tags = array(
6747  "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link",
6748  "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",
6749  "comment" // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
6750  );
6751  if ($allowiframe) {
6752  $allowed_tags[] = "iframe";
6753  }
6754 
6755  $allowed_tags_string = join("><", $allowed_tags);
6756  $allowed_tags_string = '<'.$allowed_tags_string.'>';
6757 
6758  $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
6759 
6760  $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
6761 
6762  //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
6763  $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
6764 
6765  $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
6766  $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
6767  $stringtoclean = preg_replace('/javascript\s*:/i', '', $stringtoclean);
6768 
6769  $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired </> changing string obfuscated with </> that pass injection detection into harmfull string
6770 
6771  if ($cleanalsosomestyles) { // Clean for remaining html tags
6772  $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
6773  }
6774  if ($removeclassattribute) { // Clean for remaining html tags
6775  $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
6776  }
6777 
6778  // Remove 'javascript:' that we should not find into a text with
6779  // 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)).
6780  if ($cleanalsojavascript) {
6781  $temp = preg_replace('/javascript\s*:/i', '', $temp);
6782  }
6783 
6784  $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
6785 
6786  $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
6787 
6788 
6789  return $temp;
6790 }
6791 
6792 
6804 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"))
6805 {
6806  if (class_exists('DOMDocument') && !empty($stringtoclean)) {
6807  $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
6808 
6809  $dom = new DOMDocument(null, 'UTF-8');
6810  $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
6811 
6812  if (is_object($dom)) {
6813  for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
6814  for ($attrs = $els->item($i)->attributes, $ii = $attrs->length - 1; $ii >= 0; $ii--) {
6815  //var_dump($attrs->item($ii));
6816  if (! empty($attrs->item($ii)->name)) {
6817  // Delete attribute if not into allowed_attributes
6818  if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
6819  $els->item($i)->removeAttribute($attrs->item($ii)->name);
6820  } elseif (in_array($attrs->item($ii)->name, array('style'))) {
6821  $valuetoclean = $attrs->item($ii)->value;
6822 
6823  if (isset($valuetoclean)) {
6824  do {
6825  $oldvaluetoclean = $valuetoclean;
6826  $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
6827  $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
6828  if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
6829  $valuetoclean = preg_replace('/display\s*://m', '', $valuetoclean);
6830  $valuetoclean = preg_replace('/z-index\s*://m', '', $valuetoclean);
6831  $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*://m', '', $valuetoclean);
6832  }
6833  } while ($oldvaluetoclean != $valuetoclean);
6834  }
6835 
6836  $attrs->item($ii)->value = $valuetoclean;
6837  }
6838  }
6839  }
6840  }
6841  }
6842 
6843  $return = $dom->saveHTML();
6844  //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
6845 
6846  $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
6847  $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
6848  $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
6849  return trim($return);
6850  } else {
6851  return $stringtoclean;
6852  }
6853 }
6854 
6866 function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
6867 {
6868  $temp = $stringtoclean;
6869  foreach ($disallowed_tags as $tagtoremove) {
6870  $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
6871  $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
6872  }
6873 
6874  if ($cleanalsosomestyles) {
6875  $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
6876  }
6877 
6878  return $temp;
6879 }
6880 
6881 
6891 function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
6892 {
6893  if ($nboflines == 1) {
6894  if (dol_textishtml($text)) {
6895  $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
6896  $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
6897  } else {
6898  $firstline = preg_replace('/[\n\r].*/', '', $text);
6899  }
6900  return $firstline.((strlen($firstline) != strlen($text)) ? '...' : '');
6901  } else {
6902  $ishtml = 0;
6903  if (dol_textishtml($text)) {
6904  $text = preg_replace('/\n/', '', $text);
6905  $ishtml = 1;
6906  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
6907  } else {
6908  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
6909  }
6910 
6911  $text = strtr($text, $repTable);
6912  if ($charset == 'UTF-8') {
6913  $pattern = '/(<br[^>]*>)/Uu';
6914  } else {
6915  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
6916  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
6917  }
6918  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
6919 
6920  $firstline = '';
6921  $i = 0;
6922  $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
6923  while (($i < $nba) && ($i < ($nboflines * 2))) {
6924  if ($i % 2 == 0) {
6925  $firstline .= $a[$i];
6926  } elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) {
6927  $firstline .= ($ishtml ? "<br>\n" : "\n");
6928  }
6929  $i++;
6930  }
6931  unset($a);
6932  return $firstline.(($i < $nba) ? '...' : '');
6933  }
6934 }
6935 
6936 
6947 function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
6948 {
6949  if (!$nl2brmode) {
6950  return nl2br($stringtoencode, $forxml);
6951  } else {
6952  $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
6953  return $ret;
6954  }
6955 }
6956 
6957 
6975 function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
6976 {
6977  $newstring = $stringtoencode;
6978  if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
6979  $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.
6980  if ($removelasteolbr) {
6981  $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
6982  }
6983  $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__'));
6984  $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
6985  $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"'));
6986  } else {
6987  if ($removelasteolbr) {
6988  $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
6989  }
6990  $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
6991  }
6992  // Other substitutions that htmlentities does not do
6993  //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
6994  return $newstring;
6995 }
6996 
7004 function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
7005 {
7006  $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7007  $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
7008  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
7009  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
7010  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
7011  return $ret;
7012 }
7013 
7020 function dol_htmlcleanlastbr($stringtodecode)
7021 {
7022  $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
7023  $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
7024  return $ret;
7025 }
7026 
7036 function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
7037 {
7038  $newstring = $a;
7039  if ($keepsomeentities) {
7040  $newstring = strtr($newstring, array('&amp;'=>'__andamp__', '&lt;'=>'__andlt__', '&gt;'=>'__andgt__', '"'=>'__dquot__'));
7041  }
7042  $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
7043  if ($keepsomeentities) {
7044  $newstring = strtr($newstring, array('__andamp__'=>'&amp;', '__andlt__'=>'&lt;', '__andgt__'=>'&gt;', '__dquot__'=>'"'));
7045  }
7046  return $newstring;
7047 }
7048 
7059 function dol_htmlentities($string, $flags = ENT_QUOTES|ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
7060 {
7061  return htmlentities($string, $flags, $encoding, $double_encode);
7062 }
7063 
7073 function dol_string_is_good_iso($s, $clean = 0)
7074 {
7075  $len = dol_strlen($s);
7076  $out = '';
7077  $ok = 1;
7078  for ($scursor = 0; $scursor < $len; $scursor++) {
7079  $ordchar = ord($s[$scursor]);
7080  //print $scursor.'-'.$ordchar.'<br>';
7081  if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
7082  $ok = 0;
7083  break;
7084  } elseif ($ordchar > 126 && $ordchar < 160) {
7085  $ok = 0;
7086  break;
7087  } elseif ($clean) {
7088  $out .= $s[$scursor];
7089  }
7090  }
7091  if ($clean) {
7092  return $out;
7093  }
7094  return $ok;
7095 }
7096 
7105 function dol_nboflines($s, $maxchar = 0)
7106 {
7107  if ($s == '') {
7108  return 0;
7109  }
7110  $arraystring = explode("\n", $s);
7111  $nb = count($arraystring);
7112 
7113  return $nb;
7114 }
7115 
7116 
7126 function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
7127 {
7128  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7129  if (dol_textishtml($text)) {
7130  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7131  }
7132 
7133  $text = strtr($text, $repTable);
7134  if ($charset == 'UTF-8') {
7135  $pattern = '/(<br[^>]*>)/Uu';
7136  } else {
7137  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7138  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7139  }
7140  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7141 
7142  $nblines = (int) floor((count($a) + 1) / 2);
7143  // count possible auto line breaks
7144  if ($maxlinesize) {
7145  foreach ($a as $line) {
7146  if (dol_strlen($line) > $maxlinesize) {
7147  //$line_dec = html_entity_decode(strip_tags($line));
7148  $line_dec = html_entity_decode($line);
7149  if (dol_strlen($line_dec) > $maxlinesize) {
7150  $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
7151  $nblines += substr_count($line_dec, '\n');
7152  }
7153  }
7154  }
7155  }
7156 
7157  unset($a);
7158  return $nblines;
7159 }
7160 
7169 function dol_textishtml($msg, $option = 0)
7170 {
7171  if ($option == 1) {
7172  if (preg_match('/<html/i', $msg)) {
7173  return true;
7174  } elseif (preg_match('/<body/i', $msg)) {
7175  return true;
7176  } elseif (preg_match('/<\/textarea/i', $msg)) {
7177  return true;
7178  } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) {
7179  return true;
7180  } elseif (preg_match('/<br/i', $msg)) {
7181  return true;
7182  }
7183  return false;
7184  } else {
7185  // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
7186  $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
7187  if (preg_match('/<html/i', $msg)) {
7188  return true;
7189  } elseif (preg_match('/<body/i', $msg)) {
7190  return true;
7191  } elseif (preg_match('/<\/textarea/i', $msg)) {
7192  return true;
7193  } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) {
7194  return true;
7195  } elseif (preg_match('/<br\/>/i', $msg)) {
7196  return true;
7197  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg)) {
7198  return true;
7199  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
7200  return true;
7201  } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
7202  return true; // must accept <img src="http://example.com/aaa.png" />
7203  } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
7204  return true; // must accept <a href="http://example.com/aaa.png" />
7205  } elseif (preg_match('/<h[0-9]>/i', $msg)) {
7206  return true;
7207  } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
7208  // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
7209  return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
7210  } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
7211  return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
7212  }
7213 
7214  return false;
7215  }
7216 }
7217 
7232 function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
7233 {
7234  if (!empty($invert)) {
7235  $tmp = $text1;
7236  $text1 = $text2;
7237  $text2 = $tmp;
7238  }
7239 
7240  $ret = '';
7241  $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
7242  $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
7243  $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
7244  return $ret;
7245 }
7246 
7247 
7248 
7259 function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
7260 {
7261  global $db, $conf, $mysoc, $user, $extrafields;
7262 
7263  $substitutionarray = array();
7264 
7265  if (empty($exclude) || !in_array('user', $exclude)) {
7266  // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
7267  // this will include signature content first and then replace var found into content of signature
7268  $signature = $user->signature;
7269  $substitutionarray = array_merge($substitutionarray, array(
7270  '__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '')
7271  ));
7272 
7273  if (is_object($user)) {
7274  $substitutionarray = array_merge($substitutionarray, array(
7275  '__USER_ID__' => (string) $user->id,
7276  '__USER_LOGIN__' => (string) $user->login,
7277  '__USER_EMAIL__' => (string) $user->email,
7278  '__USER_PHONE__' => (string) dol_print_phone($user->office_phone),
7279  '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile),
7280  '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile),
7281  '__USER_FAX__' => (string) $user->office_fax,
7282  '__USER_LASTNAME__' => (string) $user->lastname,
7283  '__USER_FIRSTNAME__' => (string) $user->firstname,
7284  '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
7285  '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
7286  '__USER_JOB__' => (string) $user->job,
7287  '__USER_REMOTE_IP__' => (string) getUserRemoteIP()
7288  ));
7289  }
7290  }
7291  if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) {
7292  $substitutionarray = array_merge($substitutionarray, array(
7293  '__MYCOMPANY_NAME__' => $mysoc->name,
7294  '__MYCOMPANY_EMAIL__' => $mysoc->email,
7295  '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone),
7296  '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax),
7297  '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
7298  '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
7299  '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
7300  '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
7301  '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
7302  '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
7303  '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
7304  '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
7305  '__MYCOMPANY_ADDRESS__' => $mysoc->address,
7306  '__MYCOMPANY_ZIP__' => $mysoc->zip,
7307  '__MYCOMPANY_TOWN__' => $mysoc->town,
7308  '__MYCOMPANY_COUNTRY__' => $mysoc->country,
7309  '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
7310  '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
7311  '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
7312  ));
7313  }
7314 
7315  if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) {
7316  if ($onlykey) {
7317  $substitutionarray['__ID__'] = '__ID__';
7318  $substitutionarray['__REF__'] = '__REF__';
7319  $substitutionarray['__NEWREF__'] = '__NEWREF__';
7320  $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
7321  $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
7322  $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
7323  $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
7324  $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
7325 
7326  if (!empty($conf->societe->enabled)) { // Most objects are concerned
7327  $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
7328  $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
7329  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
7330  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
7331  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
7332  $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
7333  $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
7334  $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
7335  $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
7336  $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
7337  $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
7338  $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
7339  $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
7340  $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
7341  $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
7342  $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
7343  $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
7344  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
7345  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
7346  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
7347  }
7348  if (!empty($conf->adherent->enabled) && (!is_object($object) || $object->element == 'adherent')) {
7349  $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
7350  $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
7351  $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
7352  $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
7353  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
7354  /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
7355  $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
7356  }
7357  // add variables subtitutions ticket
7358  if (!empty($conf->ticket->enabled) && (!is_object($object) || $object->element == 'ticket')) {
7359  $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
7360  $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
7361  $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
7362  $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
7363  $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
7364  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
7365  $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
7366  $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
7367  $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
7368  }
7369 
7370  if (!empty($conf->recruitment->enabled) && (!is_object($object) || $object->element == 'recruitmentcandidature')) {
7371  $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
7372  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
7373  $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
7374  }
7375  if (!empty($conf->project->enabled)) { // Most objects
7376  $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
7377  $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
7378  $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
7379  /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
7380  $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
7381  }
7382  if (!empty($conf->contrat->enabled) && (!is_object($object) || $object->element == 'contract')) {
7383  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
7384  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
7385  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
7386  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
7387  }
7388  $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
7389  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
7390  $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
7391  $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
7392  $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
7393  $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
7394  $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
7395 
7396  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
7397  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
7398  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
7399  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
7400  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
7401 
7402  if (!empty($conf->expedition->enabled) && (!is_object($object) || $object->element == 'shipping')) {
7403  $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
7404  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
7405  }
7406  if (!empty($conf->reception->enabled) && (!is_object($object) || $object->element == 'reception')) {
7407  $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment';
7408  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
7409  }
7410  } else {
7411  $substitutionarray['__ID__'] = $object->id;
7412  $substitutionarray['__REF__'] = $object->ref;
7413  $substitutionarray['__NEWREF__'] = $object->newref;
7414  $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7415  $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7416  $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
7417  $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
7418  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7419  $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%d") : '');
7420  $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%A") : '');
7421  $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%m") : '');
7422  $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%b") : '');
7423  $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%Y") : '');
7424  $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%H") : '');
7425  $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%M") : '');
7426  $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%S") : '');
7427 
7428  // For backward compatibility
7429  $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7430  $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7431  $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7432  $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 : '')) : '');
7433 
7434  if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
7435  $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
7436 
7437  $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
7438  if (method_exists($object, 'getCivilityLabel')) {
7439  $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
7440  }
7441  $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
7442  $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
7443  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
7444  if (method_exists($object, 'getFullName')) {
7445  $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
7446  }
7447  $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
7448  $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
7449  $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
7450  $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
7451  $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
7452  $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
7453  $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
7454  $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
7455  $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
7456  $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
7457  $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
7458  $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
7459  $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
7460  $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
7461  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
7462  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
7463  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
7464  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
7465  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
7466  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
7467  }
7468 
7469  if (is_object($object) && $object->element == 'societe') {
7470  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
7471  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
7472  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
7473  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
7474  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
7475  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
7476  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
7477  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
7478  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
7479  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
7480  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
7481  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
7482  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
7483  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
7484  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
7485  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
7486  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
7487  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
7488  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
7489  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
7490  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
7491  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
7492  } elseif (is_object($object->thirdparty)) {
7493  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
7494  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
7495  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
7496  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
7497  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
7498  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
7499  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
7500  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
7501  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
7502  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
7503  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
7504  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
7505  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
7506  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
7507  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
7508  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
7509  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
7510  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
7511  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
7512  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
7513  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
7514  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
7515  }
7516 
7517  if (is_object($object) && $object->element == 'recruitmentcandidature') {
7518  $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
7519  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
7520  $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
7521  }
7522 
7523  if (is_object($object->project)) {
7524  $substitutionarray['__PROJECT_ID__'] = (is_object($object->project) ? $object->project->id : '');
7525  $substitutionarray['__PROJECT_REF__'] = (is_object($object->project) ? $object->project->ref : '');
7526  $substitutionarray['__PROJECT_NAME__'] = (is_object($object->project) ? $object->project->title : '');
7527  }
7528  if (is_object($object->projet)) { // Deprecated, for backward compatibility
7529  $substitutionarray['__PROJECT_ID__'] = (is_object($object->projet) ? $object->projet->id : '');
7530  $substitutionarray['__PROJECT_REF__'] = (is_object($object->projet) ? $object->projet->ref : '');
7531  $substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet) ? $object->projet->title : '');
7532  }
7533 
7534  if (is_object($object) && $object->element == 'shipping') {
7535  $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
7536  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
7537  }
7538  if (is_object($object) && $object->element == 'reception') {
7539  $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
7540  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
7541  }
7542 
7543  if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
7544  $dateplannedstart = '';
7545  $datenextexpiration = '';
7546  foreach ($object->lines as $line) {
7547  if ($line->date_ouverture_prevue > $dateplannedstart) {
7548  $dateplannedstart = $line->date_ouverture_prevue;
7549  }
7550  if ($line->statut == 4 && $line->date_fin_prevue && (!$datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) {
7551  $datenextexpiration = $line->date_fin_prevue;
7552  }
7553  }
7554  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
7555  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
7556  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
7557  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
7558  }
7559  // add substition variable for ticket
7560  if (is_object($object) && $object->element == 'ticket') {
7561  $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
7562  $substitutionarray['__REF__'] = $object->ref;
7563  $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
7564  $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
7565  $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
7566  $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
7567  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
7568  $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
7569  $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
7570  $userstat = new User($db);
7571  if ($object->fk_user_assign > 0) {
7572  $userstat->fetch($object->fk_user_assign);
7573  $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7574  }
7575 
7576  if ($object->fk_user_create > 0) {
7577  $userstat->fetch($object->fk_user_create);
7578  $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7579  }
7580  }
7581 
7582  // Create dynamic tags for __EXTRAFIELD_FIELD__
7583  if ($object->table_element && $object->id > 0) {
7584  if (!is_object($extrafields)) {
7585  $extrafields = new ExtraFields($db);
7586  }
7587  $extrafields->fetch_name_optionals_label($object->table_element, true);
7588 
7589  if ($object->fetch_optionals() > 0) {
7590  if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
7591  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
7592  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7593  if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
7594  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
7595  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
7596  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
7597  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
7598  $datetime = $object->array_options['options_'.$key];
7599  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
7600  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
7601  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
7602  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
7603  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
7604  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
7605  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
7606  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7607  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]);
7608  }
7609  }
7610  }
7611  }
7612  }
7613 
7614  // Complete substitution array with the url to make online payment
7615  $paymenturl = '';
7616  if (empty($substitutionarray['__REF__'])) {
7617  $paymenturl = '';
7618  } else {
7619  // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
7620  require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7621  $outputlangs->loadLangs(array('paypal', 'other'));
7622  $typeforonlinepayment = 'free';
7623  if (is_object($object) && $object->element == 'commande') {
7624  $typeforonlinepayment = 'order';
7625  }
7626  if (is_object($object) && $object->element == 'facture') {
7627  $typeforonlinepayment = 'invoice';
7628  }
7629  if (is_object($object) && $object->element == 'member') {
7630  $typeforonlinepayment = 'member';
7631  }
7632  if (is_object($object) && $object->element == 'contrat') {
7633  $typeforonlinepayment = 'contract';
7634  }
7635  $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']);
7636  $paymenturl = $url;
7637  }
7638 
7639  if ($object->id > 0) {
7640  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
7641  $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
7642 
7643  if (is_object($object) && $object->element == 'propal') {
7644  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
7645  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
7646  }
7647  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
7648  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
7649  } else {
7650  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
7651  }
7652  if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') {
7653  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
7654  } else {
7655  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
7656  }
7657  if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') {
7658  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
7659  } else {
7660  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
7661  }
7662  if (!empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'contrat') {
7663  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
7664  } else {
7665  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
7666  }
7667  if (!empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'supplier_proposal') {
7668  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
7669  } else {
7670  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
7671  }
7672 
7673  if (is_object($object) && $object->element == 'propal') {
7674  $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
7675  }
7676  if (is_object($object) && $object->element == 'commande') {
7677  $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
7678  }
7679  if (is_object($object) && $object->element == 'facture') {
7680  $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
7681  }
7682  if (is_object($object) && $object->element == 'contrat') {
7683  $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
7684  }
7685  if (is_object($object) && $object->element == 'supplier_proposal') {
7686  $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
7687  }
7688  if (is_object($object) && $object->element == 'shipping') {
7689  $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
7690  }
7691  }
7692 
7693  if (is_object($object) && $object->element == 'action') {
7694  $substitutionarray['__EVENT_LABEL__'] = $object->label;
7695  $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y');
7696  $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S');
7697  }
7698  }
7699  }
7700  if (empty($exclude) || !in_array('objectamount', $exclude)) {
7701  include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
7702 
7703  $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
7704  $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
7705 
7706  $already_payed_all = 0;
7707  if (is_object($object) && ($object instanceof Facture)) {
7708  $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
7709  }
7710 
7711  $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
7712 
7713  $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
7714  $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
7715  $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
7716 
7717  $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? $object->total_ttc - $already_payed_all : '';
7718 
7719  $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
7720  $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)) : '';
7721  $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)) : '';
7722 
7723  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
7724  $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
7725  }
7726  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
7727  $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
7728  }
7729 
7730  // Amount keys formated in a currency
7731  $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7732  $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7733  $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) : '';
7734  $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)) : '';
7735  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
7736  $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7737  }
7738  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
7739  $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7740  }
7741 
7742  $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
7743  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
7744  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
7745  // TODO Add other keys for foreign multicurrency
7746 
7747  // For backward compatibility
7748  if ($onlykey != 2) {
7749  $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
7750  $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
7751  $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
7752  }
7753  }
7754 
7755  //var_dump($substitutionarray['__AMOUNT_FORMATED__']);
7756  if (empty($exclude) || !in_array('date', $exclude)) {
7757  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7758 
7759  $tmp = dol_getdate(dol_now(), true);
7760  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
7761  $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
7762  $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
7763  $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
7764 
7765  $daytext = $outputlangs->trans('Day'.$tmp['wday']);
7766 
7767  $substitutionarray = array_merge($substitutionarray, array(
7768  '__DAY__' => (string) $tmp['mday'],
7769  '__DAY_TEXT__' => $daytext, // Monday
7770  '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
7771  '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
7772  '__MONTH__' => (string) $tmp['mon'],
7773  '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
7774  '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
7775  '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
7776  '__YEAR__' => (string) $tmp['year'],
7777  '__PREVIOUS_DAY__' => (string) $tmp2['day'],
7778  '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
7779  '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
7780  '__NEXT_DAY__' => (string) $tmp4['day'],
7781  '__NEXT_MONTH__' => (string) $tmp5['month'],
7782  '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
7783  ));
7784  }
7785 
7786  if (!empty($conf->multicompany->enabled)) {
7787  $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
7788  }
7789  if (empty($exclude) || !in_array('system', $exclude)) {
7790  $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
7791  $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
7792  $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
7793  $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
7794  }
7795 
7796  return $substitutionarray;
7797 }
7798 
7815 function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
7816 {
7817  global $conf, $langs;
7818 
7819  if (!is_array($substitutionarray)) {
7820  return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
7821  }
7822 
7823  if (empty($outputlangs)) {
7824  $outputlangs = $langs;
7825  }
7826 
7827  // Is initial text HTML or simple text ?
7828  $msgishtml = 0;
7829  if (dol_textishtml($text, 1)) {
7830  $msgishtml = 1;
7831  }
7832 
7833  // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
7834  if (is_object($outputlangs)) {
7835  $reg = array();
7836  while (preg_match('/__\(([^\)]+)\)__/', $text, $reg)) {
7837  // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
7838  $tmp = explode('|', $reg[1]);
7839  if (!empty($tmp[1])) {
7840  $outputlangs->load($tmp[1]);
7841  }
7842 
7843  $value = $outputlangs->transnoentitiesnoconv($reg[1]);
7844 
7845  if (empty($converttextinhtmlifnecessary)) {
7846  // convert $newval into HTML is necessary
7847  $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
7848  } else {
7849  if (! $msgishtml) {
7850  $valueishtml = dol_textishtml($value, 1);
7851  var_dump("valueishtml=".$valueishtml);
7852 
7853  if ($valueishtml) {
7854  $text = dol_htmlentitiesbr($text);
7855  $msgishtml = 1;
7856  }
7857  } else {
7858  $value = dol_nl2br("$value");
7859  }
7860 
7861  $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $value, $text);
7862  }
7863  }
7864  }
7865 
7866  // Make substitution for constant keys.
7867  // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
7868  $reg = array();
7869  while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
7870  $keyfound = $reg[1];
7871  if (isASecretKey($keyfound)) {
7872  $value = '*****forbidden*****';
7873  } else {
7874  $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
7875  }
7876 
7877  if (empty($converttextinhtmlifnecessary)) {
7878  // convert $newval into HTML is necessary
7879  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
7880  } else {
7881  if (! $msgishtml) {
7882  $valueishtml = dol_textishtml($value, 1);
7883 
7884  if ($valueishtml) {
7885  $text = dol_htmlentitiesbr($text);
7886  $msgishtml = 1;
7887  }
7888  } else {
7889  $value = dol_nl2br("$value");
7890  }
7891 
7892  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
7893  }
7894  }
7895 
7896  // Make substitition for array $substitutionarray
7897  foreach ($substitutionarray as $key => $value) {
7898  if (!isset($value)) {
7899  continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
7900  }
7901 
7902  if ($key == '__USER_SIGNATURE__' && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) {
7903  $value = ''; // Protection
7904  }
7905 
7906  if (empty($converttextinhtmlifnecessary)) {
7907  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
7908  } else {
7909  if (! $msgishtml) {
7910  $valueishtml = dol_textishtml($value, 1);
7911 
7912  if ($valueishtml) {
7913  $text = dol_htmlentitiesbr($text);
7914  $msgishtml = 1;
7915  }
7916  } else {
7917  $value = dol_nl2br("$value");
7918  }
7919  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
7920  }
7921  }
7922 
7923  return $text;
7924 }
7925 
7938 function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
7939 {
7940  global $conf, $user;
7941 
7942  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
7943 
7944  // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
7945 
7946  // Check if there is external substitution to do, requested by plugins
7947  $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
7948 
7949  foreach ($dirsubstitutions as $reldir) {
7950  $dir = dol_buildpath($reldir, 0);
7951 
7952  // Check if directory exists
7953  if (!dol_is_dir($dir)) {
7954  continue;
7955  }
7956 
7957  $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
7958  foreach ($substitfiles as $substitfile) {
7959  $reg = array();
7960  if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
7961  $module = $reg[1];
7962 
7963  dol_syslog("Library ".$substitfile['name']." found into ".$dir);
7964  // Include the user's functions file
7965  require_once $dir.$substitfile['name'];
7966  // Call the user's function, and only if it is defined
7967  $function_name = $module."_".$callfunc;
7968  if (function_exists($function_name)) {
7969  $function_name($substitutionarray, $outputlangs, $object, $parameters);
7970  }
7971  }
7972  }
7973  }
7974  if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) {
7975  // to list all tags in odt template
7976  $tags = '';
7977  foreach ($substitutionarray as $key => $value) {
7978  $tags .= '{'.$key.'} => '.$value."\n";
7979  }
7980  $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
7981  }
7982 }
7983 
7993 function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
7994 {
7995  print get_date_range($date_start, $date_end, $format, $outputlangs);
7996 }
7997 
8008 function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
8009 {
8010  global $langs;
8011 
8012  $out = '';
8013 
8014  if (!is_object($outputlangs)) {
8015  $outputlangs = $langs;
8016  }
8017 
8018  if ($date_start && $date_end) {
8019  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8020  }
8021  if ($date_start && !$date_end) {
8022  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8023  }
8024  if (!$date_start && $date_end) {
8025  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8026  }
8027 
8028  return $out;
8029 }
8030 
8039 function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
8040 {
8041  global $conf;
8042 
8043  $ret = '';
8044  // If order not defined, we use the setup
8045  if ($nameorder < 0) {
8046  $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
8047  }
8048  if ($nameorder == 1) {
8049  $ret .= $firstname;
8050  if ($firstname && $lastname) {
8051  $ret .= ' ';
8052  }
8053  $ret .= $lastname;
8054  } elseif ($nameorder == 2 || $nameorder == 3) {
8055  $ret .= $firstname;
8056  if (empty($ret) && $nameorder == 3) {
8057  $ret .= $lastname;
8058  }
8059  } else { // 0, 4 or 5
8060  $ret .= $lastname;
8061  if (empty($ret) && $nameorder == 5) {
8062  $ret .= $firstname;
8063  }
8064  if ($nameorder == 0) {
8065  if ($firstname && $lastname) {
8066  $ret .= ' ';
8067  }
8068  $ret .= $firstname;
8069  }
8070  }
8071  return $ret;
8072 }
8073 
8074 
8085 function setEventMessage($mesgs, $style = 'mesgs')
8086 {
8087  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
8088  if (!is_array($mesgs)) {
8089  // If mesgs is a string
8090  if ($mesgs) {
8091  $_SESSION['dol_events'][$style][] = $mesgs;
8092  }
8093  } else {
8094  // If mesgs is an array
8095  foreach ($mesgs as $mesg) {
8096  if ($mesg) {
8097  $_SESSION['dol_events'][$style][] = $mesg;
8098  }
8099  }
8100  }
8101 }
8102 
8114 function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '')
8115 {
8116  if (empty($mesg) && empty($mesgs)) {
8117  dol_syslog("Try to add a message in stack with empty message", LOG_WARNING);
8118  } else {
8119  if ($messagekey) {
8120  // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
8121  // TODO
8122  $mesg .= '';
8123  }
8124  if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
8125  if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
8126  dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages');
8127  }
8128  if (empty($mesgs)) {
8129  setEventMessage($mesg, $style);
8130  } else {
8131  if (!empty($mesg) && !in_array($mesg, $mesgs)) {
8132  setEventMessage($mesg, $style); // Add message string if not already into array
8133  }
8134  setEventMessage($mesgs, $style);
8135  }
8136  }
8137  }
8138 }
8139 
8149 function dol_htmloutput_events($disabledoutputofmessages = 0)
8150 {
8151  // Show mesgs
8152  if (isset($_SESSION['dol_events']['mesgs'])) {
8153  if (empty($disabledoutputofmessages)) {
8154  dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
8155  }
8156  unset($_SESSION['dol_events']['mesgs']);
8157  }
8158  // Show errors
8159  if (isset($_SESSION['dol_events']['errors'])) {
8160  if (empty($disabledoutputofmessages)) {
8161  dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
8162  }
8163  unset($_SESSION['dol_events']['errors']);
8164  }
8165 
8166  // Show warnings
8167  if (isset($_SESSION['dol_events']['warnings'])) {
8168  if (empty($disabledoutputofmessages)) {
8169  dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
8170  }
8171  unset($_SESSION['dol_events']['warnings']);
8172  }
8173 }
8174 
8189 function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
8190 {
8191  global $conf, $langs;
8192 
8193  $ret = 0;
8194  $return = '';
8195  $out = '';
8196  $divstart = $divend = '';
8197 
8198  // If inline message with no format, we add it.
8199  if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
8200  $divstart = '<div class="'.$style.' clearboth">';
8201  $divend = '</div>';
8202  }
8203 
8204  if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
8205  $langs->load("errors");
8206  $out .= $divstart;
8207  if (is_array($mesgarray) && count($mesgarray)) {
8208  foreach ($mesgarray as $message) {
8209  $ret++;
8210  $out .= $langs->trans($message);
8211  if ($ret < count($mesgarray)) {
8212  $out .= "<br>\n";
8213  }
8214  }
8215  }
8216  if ($mesgstring) {
8217  $ret++;
8218  $out .= $langs->trans($mesgstring);
8219  }
8220  $out .= $divend;
8221  }
8222 
8223  if ($out) {
8224  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) {
8225  $return = '<script>
8226  $(document).ready(function() {
8227  var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").'
8228  if (block) {
8229  $.dolEventValid("","'.dol_escape_js($out).'");
8230  } else {
8231  /* jnotify(message, preset of message type, keepmessage) */
8232  $.jnotify("'.dol_escape_js($out).'",
8233  "'.($style == "ok" ? 3000 : $style).'",
8234  '.($style == "ok" ? "false" : "true").',
8235  { remove: function (){} } );
8236  }
8237  });
8238  </script>';
8239  } else {
8240  $return = $out;
8241  }
8242  }
8243 
8244  return $return;
8245 }
8246 
8258 function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8259 {
8260  return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8261 }
8262 
8276 function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
8277 {
8278  if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
8279  return;
8280  }
8281 
8282  $iserror = 0;
8283  $iswarning = 0;
8284  if (is_array($mesgarray)) {
8285  foreach ($mesgarray as $val) {
8286  if ($val && preg_match('/class="error"/i', $val)) {
8287  $iserror++;
8288  break;
8289  }
8290  if ($val && preg_match('/class="warning"/i', $val)) {
8291  $iswarning++;
8292  break;
8293  }
8294  }
8295  } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
8296  $iserror++;
8297  } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
8298  $iswarning++;
8299  }
8300  if ($style == 'error') {
8301  $iserror++;
8302  }
8303  if ($style == 'warning') {
8304  $iswarning++;
8305  }
8306 
8307  if ($iserror || $iswarning) {
8308  // Remove div from texts
8309  $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
8310  $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
8311  $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
8312  // Remove div from texts array
8313  if (is_array($mesgarray)) {
8314  $newmesgarray = array();
8315  foreach ($mesgarray as $val) {
8316  if (is_string($val)) {
8317  $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
8318  $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
8319  $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
8320  $newmesgarray[] = $tmpmesgstring;
8321  } else {
8322  dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
8323  }
8324  }
8325  $mesgarray = $newmesgarray;
8326  }
8327  print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
8328  } else {
8329  print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
8330  }
8331 }
8332 
8344 function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8345 {
8346  dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8347 }
8348 
8362 function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
8363 {
8364  // Clean parameters
8365  $order = strtolower($order);
8366 
8367  if (is_array($array)) {
8368  $sizearray = count($array);
8369  if ($sizearray > 0) {
8370  $temp = array();
8371  foreach (array_keys($array) as $key) {
8372  if (is_object($array[$key])) {
8373  $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
8374  } else {
8375  $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
8376  }
8377  }
8378 
8379  if (!$natsort) {
8380  if ($order == 'asc') {
8381  asort($temp);
8382  } else {
8383  arsort($temp);
8384  }
8385  } else {
8386  if ($case_sensitive) {
8387  natsort($temp);
8388  } else {
8389  natcasesort($temp); // natecasesort is not sensible to case
8390  }
8391  if ($order != 'asc') {
8392  $temp = array_reverse($temp, true);
8393  }
8394  }
8395 
8396  $sorted = array();
8397 
8398  foreach (array_keys($temp) as $key) {
8399  (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
8400  }
8401 
8402  return $sorted;
8403  }
8404  }
8405  return $array;
8406 }
8407 
8408 
8415 function utf8_check($str)
8416 {
8417  $str = (string) $str; // Sometimes string is an int.
8418 
8419  // We must use here a binary strlen function (so not dol_strlen)
8420  $strLength = dol_strlen($str);
8421  for ($i = 0; $i < $strLength; $i++) {
8422  if (ord($str[$i]) < 0x80) {
8423  continue; // 0bbbbbbb
8424  } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
8425  $n = 1; // 110bbbbb
8426  } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
8427  $n = 2; // 1110bbbb
8428  } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
8429  $n = 3; // 11110bbb
8430  } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
8431  $n = 4; // 111110bb
8432  } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
8433  $n = 5; // 1111110b
8434  } else {
8435  return false; // Does not match any model
8436  }
8437  for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
8438  if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
8439  return false;
8440  }
8441  }
8442  }
8443  return true;
8444 }
8445 
8452 function ascii_check($str)
8453 {
8454  if (function_exists('mb_check_encoding')) {
8455  //if (mb_detect_encoding($str, 'ASCII', true) return false;
8456  if (!mb_check_encoding($str, 'ASCII')) {
8457  return false;
8458  }
8459  } else {
8460  if (preg_match('/[^\x00-\x7f]/', $str)) {
8461  return false; // Contains a byte > 7f
8462  }
8463  }
8464 
8465  return true;
8466 }
8467 
8468 
8476 function dol_osencode($str)
8477 {
8478  global $conf;
8479 
8480  $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
8481  if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
8482  $tmp = 'iso-8859-1'; // By default for windows
8483  }
8484  if (empty($tmp)) {
8485  $tmp = 'utf-8'; // By default for other
8486  }
8487  if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) {
8488  $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING;
8489  }
8490 
8491  if ($tmp == 'iso-8859-1') {
8492  return utf8_decode($str);
8493  }
8494  return $str;
8495 }
8496 
8497 
8512 function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
8513 {
8514  global $cache_codes;
8515 
8516  // If key empty
8517  if ($key == '') {
8518  return '';
8519  }
8520 
8521  // Check in cache
8522  if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
8523  return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
8524  }
8525 
8526  dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
8527 
8528  $sql = "SELECT ".$fieldid." as valuetoget";
8529  $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
8530  $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
8531  if (!empty($entityfilter)) {
8532  $sql .= " AND entity IN (".getEntity($tablename).")";
8533  }
8534  if ($filters) {
8535  $sql .= $filters;
8536  }
8537 
8538  $resql = $db->query($sql);
8539  if ($resql) {
8540  $obj = $db->fetch_object($resql);
8541  if ($obj) {
8542  $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
8543  } else {
8544  $cache_codes[$tablename][$key][$fieldid] = '';
8545  }
8546  $db->free($resql);
8547  return $cache_codes[$tablename][$key][$fieldid];
8548  } else {
8549  return -1;
8550  }
8551 }
8552 
8559 function verifCond($strToEvaluate)
8560 {
8561  global $user, $conf, $langs;
8562  global $leftmenu;
8563  global $rights; // To export to dol_eval function
8564 
8565  //print $strToEvaluate."<br>\n";
8566  $rights = true;
8567  if (isset($strToEvaluate) && $strToEvaluate !== '') {
8568  //$str = 'if(!('.$strToEvaluate.')) $rights = false;';
8569  //dol_eval($str, 0, 1, '2'); // The dol_eval must contains all the global $xxx used into a condition
8570  //var_dump($strToEvaluate);
8571  $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
8572  $rights = $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
8573  //var_dump($rights);
8574  }
8575  return $rights;
8576 }
8577 
8588 function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
8589 {
8590  // Only global variables can be changed by eval function and returned to caller
8591  global $db, $langs, $user, $conf, $website, $websitepage;
8592  global $action, $mainmenu, $leftmenu;
8593  global $rights;
8594  global $object;
8595  global $mysoc;
8596 
8597  global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
8598  global $soc; // For backward compatibility
8599 
8600  // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
8601  if ($onlysimplestring == '1') {
8602  // We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
8603  // We must accept: '$conf->barcode->enabled && preg_match(\'/^(AAA|BBB)/\',$leftmenu)'
8604  // We must accept: '$user->rights->cabinetmed->read && $object->canvas=="patient@cabinetmed"'
8605  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@', '/').']/i', $s)) {
8606  if ($returnvalue) {
8607  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8608  } else {
8609  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8610  return '';
8611  }
8612  // TODO We can exclude all () that is not '($db)' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match('
8613  // ...
8614  }
8615  } elseif ($onlysimplestring == '2') {
8616  // 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"
8617  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@;[]', '/').']/i', $s)) {
8618  if ($returnvalue) {
8619  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8620  } else {
8621  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8622  return '';
8623  }
8624  }
8625  }
8626  if (strpos($s, '::') !== false) {
8627  if ($returnvalue) {
8628  return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
8629  } else {
8630  dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s);
8631  return '';
8632  }
8633  }
8634  if (strpos($s, '`') !== false) {
8635  if ($returnvalue) {
8636  return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
8637  } else {
8638  dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s);
8639  return '';
8640  }
8641  }
8642  if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
8643  if ($returnvalue) {
8644  return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
8645  } else {
8646  dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s);
8647  return '';
8648  }
8649  }
8650 
8651  // We block use of php exec or php file functions
8652  $forbiddenphpstrings = array('$$');
8653  $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST'));
8654 
8655  $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen", "eval", "dol_eval", "executeCLI", 'verifCond');
8656  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
8657  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
8658 
8659  $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
8660 
8661  do {
8662  $oldstringtoclean = $s;
8663  $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
8664  $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
8665  //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\(/i', '', $s); // Remove $function( call and $mycall->mymethod(
8666  } while ($oldstringtoclean != $s);
8667 
8668  if (strpos($s, '__forbiddenstring__') !== false) {
8669  dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
8670  if ($returnvalue) {
8671  return 'Bad string syntax to evaluate: '.$s;
8672  } else {
8673  dol_syslog('Bad string syntax to evaluate: '.$s);
8674  return '';
8675  }
8676  }
8677 
8678  //print $s."<br>\n";
8679  if ($returnvalue) {
8680  if ($hideerrors) {
8681  return @eval('return '.$s.';');
8682  } else {
8683  return eval('return '.$s.';');
8684  }
8685  } else {
8686  if ($hideerrors) {
8687  @eval($s);
8688  } else {
8689  eval($s);
8690  }
8691  }
8692 }
8693 
8700 function dol_validElement($element)
8701 {
8702  return (trim($element) != '');
8703 }
8704 
8713 function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
8714 {
8715  if (empty($codelang)) {
8716  return '';
8717  }
8718 
8719  if ($codelang == 'auto') {
8720  return '<span class="fa fa-language"></span>';
8721  }
8722 
8723  $langtocountryflag = array(
8724  'ar_AR' => '',
8725  'ca_ES' => 'catalonia',
8726  'da_DA' => 'dk',
8727  'fr_CA' => 'mq',
8728  'sv_SV' => 'se',
8729  'sw_SW' => 'unknown',
8730  'AQ' => 'unknown',
8731  'CW' => 'unknown',
8732  'IM' => 'unknown',
8733  'JE' => 'unknown',
8734  'MF' => 'unknown',
8735  'BL' => 'unknown',
8736  'SX' => 'unknown'
8737  );
8738 
8739  if (isset($langtocountryflag[$codelang])) {
8740  $flagImage = $langtocountryflag[$codelang];
8741  } else {
8742  $tmparray = explode('_', $codelang);
8743  $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
8744  }
8745 
8746  return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
8747 }
8748 
8756 function getLanguageCodeFromCountryCode($countrycode)
8757 {
8758  global $mysoc;
8759 
8760  if (empty($countrycode)) {
8761  return null;
8762  }
8763 
8764  if (strtoupper($countrycode) == 'MQ') {
8765  return 'fr_CA';
8766  }
8767  if (strtoupper($countrycode) == 'SE') {
8768  return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
8769  }
8770  if (strtoupper($countrycode) == 'CH') {
8771  if ($mysoc->country_code == 'FR') {
8772  return 'fr_CH';
8773  }
8774  if ($mysoc->country_code == 'DE') {
8775  return 'de_CH';
8776  }
8777  if ($mysoc->country_code == 'IT') {
8778  return 'it_CH';
8779  }
8780  }
8781 
8782  // Locale list taken from:
8783  // http://stackoverflow.com/questions/3191664/
8784  // list-of-all-locales-and-their-short-codes
8785  $locales = array(
8786  'af-ZA',
8787  'am-ET',
8788  'ar-AE',
8789  'ar-BH',
8790  'ar-DZ',
8791  'ar-EG',
8792  'ar-IQ',
8793  'ar-JO',
8794  'ar-KW',
8795  'ar-LB',
8796  'ar-LY',
8797  'ar-MA',
8798  'ar-OM',
8799  'ar-QA',
8800  'ar-SA',
8801  'ar-SY',
8802  'ar-TN',
8803  'ar-YE',
8804  //'as-IN', // Moved after en-IN
8805  'ba-RU',
8806  'be-BY',
8807  'bg-BG',
8808  'bn-BD',
8809  //'bn-IN', // Moved after en-IN
8810  'bo-CN',
8811  'br-FR',
8812  'ca-ES',
8813  'co-FR',
8814  'cs-CZ',
8815  'cy-GB',
8816  'da-DK',
8817  'de-AT',
8818  'de-CH',
8819  'de-DE',
8820  'de-LI',
8821  'de-LU',
8822  'dv-MV',
8823  'el-GR',
8824  'en-AU',
8825  'en-BZ',
8826  'en-CA',
8827  'en-GB',
8828  'en-IE',
8829  'en-IN',
8830  'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
8831  'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
8832  'en-JM',
8833  'en-MY',
8834  'en-NZ',
8835  'en-PH',
8836  'en-SG',
8837  'en-TT',
8838  'en-US',
8839  'en-ZA',
8840  'en-ZW',
8841  'es-AR',
8842  'es-BO',
8843  'es-CL',
8844  'es-CO',
8845  'es-CR',
8846  'es-DO',
8847  'es-EC',
8848  'es-ES',
8849  'es-GT',
8850  'es-HN',
8851  'es-MX',
8852  'es-NI',
8853  'es-PA',
8854  'es-PE',
8855  'es-PR',
8856  'es-PY',
8857  'es-SV',
8858  'es-US',
8859  'es-UY',
8860  'es-VE',
8861  'et-EE',
8862  'eu-ES',
8863  'fa-IR',
8864  'fi-FI',
8865  'fo-FO',
8866  'fr-BE',
8867  'fr-CA',
8868  'fr-CH',
8869  'fr-FR',
8870  'fr-LU',
8871  'fr-MC',
8872  'fy-NL',
8873  'ga-IE',
8874  'gd-GB',
8875  'gl-ES',
8876  'gu-IN',
8877  'he-IL',
8878  'hi-IN',
8879  'hr-BA',
8880  'hr-HR',
8881  'hu-HU',
8882  'hy-AM',
8883  'id-ID',
8884  'ig-NG',
8885  'ii-CN',
8886  'is-IS',
8887  'it-CH',
8888  'it-IT',
8889  'ja-JP',
8890  'ka-GE',
8891  'kk-KZ',
8892  'kl-GL',
8893  'km-KH',
8894  'kn-IN',
8895  'ko-KR',
8896  'ky-KG',
8897  'lb-LU',
8898  'lo-LA',
8899  'lt-LT',
8900  'lv-LV',
8901  'mi-NZ',
8902  'mk-MK',
8903  'ml-IN',
8904  'mn-MN',
8905  'mr-IN',
8906  'ms-BN',
8907  'ms-MY',
8908  'mt-MT',
8909  'nb-NO',
8910  'ne-NP',
8911  'nl-BE',
8912  'nl-NL',
8913  'nn-NO',
8914  'oc-FR',
8915  'or-IN',
8916  'pa-IN',
8917  'pl-PL',
8918  'ps-AF',
8919  'pt-BR',
8920  'pt-PT',
8921  'rm-CH',
8922  'ro-MD',
8923  'ro-RO',
8924  'ru-RU',
8925  'rw-RW',
8926  'sa-IN',
8927  'se-FI',
8928  'se-NO',
8929  'se-SE',
8930  'si-LK',
8931  'sk-SK',
8932  'sl-SI',
8933  'sq-AL',
8934  'sv-FI',
8935  'sv-SE',
8936  'sw-KE',
8937  'ta-IN',
8938  'te-IN',
8939  'th-TH',
8940  'tk-TM',
8941  'tn-ZA',
8942  'tr-TR',
8943  'tt-RU',
8944  'ug-CN',
8945  'uk-UA',
8946  'ur-PK',
8947  'vi-VN',
8948  'wo-SN',
8949  'xh-ZA',
8950  'yo-NG',
8951  'zh-CN',
8952  'zh-HK',
8953  'zh-MO',
8954  'zh-SG',
8955  'zh-TW',
8956  'zu-ZA',
8957  );
8958 
8959  $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
8960  if (in_array($buildprimarykeytotest, $locales)) {
8961  return strtolower($countrycode).'_'.strtoupper($countrycode);
8962  }
8963 
8964  if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
8965  foreach ($locales as $locale) {
8966  $locale_language = locale_get_primary_language($locale);
8967  $locale_region = locale_get_region($locale);
8968  if (strtoupper($countrycode) == $locale_region) {
8969  //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
8970  return strtolower($locale_language).'_'.strtoupper($locale_region);
8971  }
8972  }
8973  } else {
8974  dol_syslog("Warning Exention php-intl is not available", LOG_WARNING);
8975  }
8976 
8977  return null;
8978 }
8979 
9009 function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add')
9010 {
9011  global $hookmanager, $db;
9012 
9013  if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
9014  foreach ($conf->modules_parts['tabs'][$type] as $value) {
9015  $values = explode(':', $value);
9016 
9017  $reg = array();
9018  if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
9019  if (count($values) == 6) {
9020  // new declaration with permissions:
9021  // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9022  // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9023  if ($values[0] != $type) {
9024  continue;
9025  }
9026  //var_dump(verifCond($values[4]));
9027 
9028  if (verifCond($values[4])) {
9029  if ($values[3]) {
9030  $langs->load($values[3]);
9031  }
9032  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9033  $substitutionarray = array();
9034  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9035  $label = make_substitutions($reg[1], $substitutionarray);
9036  } else {
9037  $labeltemp = explode(',', $values[2]);
9038  $label = $langs->trans($labeltemp[0]);
9039  if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
9040  dol_include_once($labeltemp[2]);
9041  $classtoload = $labeltemp[1];
9042  if (class_exists($classtoload)) {
9043  $obj = new $classtoload($db);
9044  $function = $labeltemp[3];
9045  if ($obj && $function && method_exists($obj, $function)) {
9046  $nbrec = $obj->$function($object->id, $obj);
9047  $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
9048  }
9049  }
9050  }
9051  }
9052 
9053  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
9054  $head[$h][1] = $label;
9055  $head[$h][2] = str_replace('+', '', $values[1]);
9056  $h++;
9057  }
9058  } elseif (count($values) == 5) { // deprecated
9059  dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
9060 
9061  if ($values[0] != $type) {
9062  continue;
9063  }
9064  if ($values[3]) {
9065  $langs->load($values[3]);
9066  }
9067  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9068  $substitutionarray = array();
9069  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9070  $label = make_substitutions($reg[1], $substitutionarray);
9071  } else {
9072  $label = $langs->trans($values[2]);
9073  }
9074 
9075  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
9076  $head[$h][1] = $label;
9077  $head[$h][2] = str_replace('+', '', $values[1]);
9078  $h++;
9079  }
9080  } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
9081  if ($values[0] != $type) {
9082  continue;
9083  }
9084  $tabname = str_replace('-', '', $values[1]);
9085  foreach ($head as $key => $val) {
9086  $condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
9087  //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
9088  if ($head[$key][2] == $tabname && $condition) {
9089  unset($head[$key]);
9090  break;
9091  }
9092  }
9093  }
9094  }
9095  }
9096 
9097  // No need to make a return $head. Var is modified as a reference
9098  if (!empty($hookmanager)) {
9099  $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head);
9100  $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters);
9101  if ($reshook > 0) { // Hook ask to replace completely the array
9102  $head = $hookmanager->resArray;
9103  } else { // Hook
9104  $head = array_merge($head, $hookmanager->resArray);
9105  }
9106  $h = count($head);
9107  }
9108 }
9109 
9121 function printCommonFooter($zone = 'private')
9122 {
9123  global $conf, $hookmanager, $user, $debugbar;
9124  global $action;
9125  global $micro_start_time;
9126 
9127  if ($zone == 'private') {
9128  print "\n".'<!-- Common footer for private page -->'."\n";
9129  } else {
9130  print "\n".'<!-- Common footer for public page -->'."\n";
9131  }
9132 
9133  // A div to store page_y POST parameter so we can read it using javascript
9134  print "\n<!-- A div to store page_y POST parameter -->\n";
9135  print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
9136 
9137  $parameters = array();
9138  $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
9139  if (empty($reshook)) {
9140  if (!empty($conf->global->MAIN_HTML_FOOTER)) {
9141  print $conf->global->MAIN_HTML_FOOTER."\n";
9142  }
9143 
9144  print "\n";
9145  if (!empty($conf->use_javascript_ajax)) {
9146  print '<script>'."\n";
9147  print 'jQuery(document).ready(function() {'."\n";
9148 
9149  if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
9150  print "\n";
9151  print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
9152  print 'jQuery("li.menuhider").click(function(event) {';
9153  print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
9154  print ' console.log("We click on .menuhider");'."\n";
9155  print ' $("body").toggleClass("sidebar-collapse")'."\n";
9156  print '});'."\n";
9157  }
9158 
9159  // Management of focus and mandatory for fields
9160  if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"])))) {
9161  print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
9162  $relativepathstring = $_SERVER["PHP_SELF"];
9163  // Clean $relativepathstring
9164  if (constant('DOL_URL_ROOT')) {
9165  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
9166  }
9167  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
9168  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
9169  //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
9170  if (!empty($user->default_values[$relativepathstring]['focus'])) {
9171  foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
9172  $qualified = 0;
9173  if ($defkey != '_noquery_') {
9174  $tmpqueryarraytohave = explode('&', $defkey);
9175  $foundintru = 0;
9176  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9177  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9178  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9179  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9180  $foundintru = 1;
9181  }
9182  }
9183  if (!$foundintru) {
9184  $qualified = 1;
9185  }
9186  //var_dump($defkey.'-'.$qualified);
9187  } else {
9188  $qualified = 1;
9189  }
9190 
9191  if ($qualified) {
9192  foreach ($defval as $paramkey => $paramval) {
9193  // Set focus on field
9194  print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
9195  print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
9196  print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
9197  }
9198  }
9199  }
9200  }
9201  if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
9202  foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
9203  $qualified = 0;
9204  if ($defkey != '_noquery_') {
9205  $tmpqueryarraytohave = explode('&', $defkey);
9206  $foundintru = 0;
9207  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9208  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9209  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9210  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9211  $foundintru = 1;
9212  }
9213  }
9214  if (!$foundintru) {
9215  $qualified = 1;
9216  }
9217  //var_dump($defkey.'-'.$qualified);
9218  } else {
9219  $qualified = 1;
9220  }
9221 
9222  if ($qualified) {
9223  foreach ($defval as $paramkey => $paramval) {
9224  // Add property 'required' on input
9225  print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9226  print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9227  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";
9228  print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9229  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
9230  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
9231 
9232  // Add 'field required' class on closest td for all input elements : input, textarea and select
9233  print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
9234  }
9235  }
9236  }
9237  }
9238  }
9239 
9240  print '});'."\n";
9241 
9242  // End of tuning
9243  if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) {
9244  print "\n";
9245  print "/* JS CODE TO ENABLE to add memory info */\n";
9246  print 'window.console && console.log("';
9247  if (!empty($conf->global->MEMCACHED_SERVER)) {
9248  print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
9249  }
9250  print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off');
9251  if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
9252  $micro_end_time = microtime(true);
9253  print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
9254  }
9255 
9256  if (function_exists("memory_get_usage")) {
9257  print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
9258  }
9259  if (function_exists("memory_get_peak_usage")) {
9260  print ' - Real mem peak: '.memory_get_peak_usage(true);
9261  }
9262  if (function_exists("zend_loader_file_encoded")) {
9263  print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
9264  }
9265  print '");'."\n";
9266  }
9267 
9268  print "\n".'</script>'."\n";
9269 
9270  // Google Analytics
9271  // TODO Add a hook here
9272  if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID)) {
9273  $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID);
9274  foreach ($tmptagarray as $tmptag) {
9275  print "\n";
9276  print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
9277  print "
9278  <!-- Global site tag (gtag.js) - Google Analytics -->
9279  <script async src=\"https://www.googletagmanager.com/gtag/js?id=".trim($tmptag)."\"></script>
9280  <script>
9281  window.dataLayer = window.dataLayer || [];
9282  function gtag(){dataLayer.push(arguments);}
9283  gtag('js', new Date());
9284 
9285  gtag('config', '".trim($tmptag)."');
9286  </script>";
9287  print "\n";
9288  }
9289  }
9290  }
9291 
9292  // Add Xdebug coverage of code
9293  if (defined('XDEBUGCOVERAGE')) {
9294  print_r(xdebug_get_code_coverage());
9295  }
9296 
9297  // Add DebugBar data
9298  if (!empty($user->rights->debugbar->read) && is_object($debugbar)) {
9299  $debugbar['time']->stopMeasure('pageaftermaster');
9300  print '<!-- Output debugbar data -->'."\n";
9301  $renderer = $debugbar->getRenderer();
9302  print $debugbar->getRenderer()->render();
9303  } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
9304  print "\n";
9305  print "<!-- Start of log output\n";
9306  //print '<div class="hidden">'."\n";
9307  foreach ($conf->logbuffer as $logline) {
9308  print $logline."<br>\n";
9309  }
9310  //print '</div>'."\n";
9311  print "End of log output -->\n";
9312  }
9313  }
9314 }
9315 
9325 function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
9326 {
9327  if ($a = explode($delimiter, $string)) {
9328  $ka = array();
9329  foreach ($a as $s) { // each part
9330  if ($s) {
9331  if ($pos = strpos($s, $kv)) { // key/value delimiter
9332  $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
9333  } else { // key delimiter not found
9334  $ka[] = trim($s);
9335  }
9336  }
9337  }
9338  return $ka;
9339  }
9340  return array();
9341 }
9342 
9343 
9350 function dol_set_focus($selector)
9351 {
9352  print "\n".'<!-- Set focus onto a specific field -->'."\n";
9353  print '<script>jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
9354 }
9355 
9356 
9364 function dol_getmypid()
9365 {
9366  if (!function_exists('getmypid')) {
9367  return mt_rand(1, 32768);
9368  } else {
9369  return getmypid();
9370  }
9371 }
9372 
9373 
9391 function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
9392 {
9393  global $db, $langs;
9394 
9395  $value = trim($value);
9396 
9397  if ($mode == 0) {
9398  $value = preg_replace('/\*/', '%', $value); // Replace * with %
9399  }
9400  if ($mode == 1) {
9401  $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
9402  }
9403 
9404  $value = preg_replace('/\s*\|\s*/', '|', $value);
9405 
9406  $crits = explode(' ', $value);
9407  $res = '';
9408  if (!is_array($fields)) {
9409  $fields = array($fields);
9410  }
9411 
9412  $j = 0;
9413  foreach ($crits as $crit) {
9414  $crit = trim($crit);
9415  $i = 0;
9416  $i2 = 0;
9417  $newres = '';
9418  foreach ($fields as $field) {
9419  if ($mode == 1) {
9420  $operator = '=';
9421  $newcrit = preg_replace('/([<>=]+)/', '', $crit);
9422 
9423  $reg = array();
9424  preg_match('/([<>=]+)/', $crit, $reg);
9425  if (!empty($reg[1])) {
9426  $operator = $reg[1];
9427  }
9428  if ($newcrit != '') {
9429  $numnewcrit = price2num($newcrit);
9430  if (is_numeric($numnewcrit)) {
9431  $newres .= ($i2 > 0 ? ' OR ' : '').$field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
9432  } else {
9433  $newres .= ($i2 > 0 ? ' OR ' : '').'1 = 2'; // force false
9434  }
9435  $i2++; // a criteria was added to string
9436  }
9437  } elseif ($mode == 2 || $mode == -2) {
9438  $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
9439  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
9440  $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
9441  if ($mode == -2) {
9442  $newres .= ' OR '.$field.' IS NULL';
9443  }
9444  $i2++; // a criteria was added to string
9445  } elseif ($mode == 3 || $mode == -3) {
9446  $tmparray = explode(',', $crit);
9447  if (count($tmparray)) {
9448  $listofcodes = '';
9449  foreach ($tmparray as $val) {
9450  $val = trim($val);
9451  if ($val) {
9452  $listofcodes .= ($listofcodes ? ',' : '');
9453  $listofcodes .= "'".$db->escape($val)."'";
9454  }
9455  }
9456  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
9457  $i2++; // a criteria was added to string
9458  }
9459  if ($mode == -3) {
9460  $newres .= ' OR '.$field.' IS NULL';
9461  }
9462  } elseif ($mode == 4) {
9463  $tmparray = explode(',', $crit);
9464  if (count($tmparray)) {
9465  $listofcodes = '';
9466  foreach ($tmparray as $val) {
9467  $val = trim($val);
9468  if ($val) {
9469  $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
9470  $newres .= ' OR '.$field." = '".$db->escape($val)."'";
9471  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
9472  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
9473  $newres .= ')';
9474  $i2++;
9475  }
9476  }
9477  }
9478  } else // $mode=0
9479  {
9480  $tmpcrits = explode('|', $crit);
9481  $i3 = 0;
9482  foreach ($tmpcrits as $tmpcrit) {
9483  if ($tmpcrit !== '0' && empty($tmpcrit)) {
9484  continue;
9485  }
9486 
9487  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
9488 
9489  if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
9490  $newres .= $field." = ".(is_numeric(trim($tmpcrit)) ? ((float) trim($tmpcrit)) : '0');
9491  } else {
9492  $tmpcrit = trim($tmpcrit);
9493  $tmpcrit2 = $tmpcrit;
9494  $tmpbefore = '%';
9495  $tmpafter = '%';
9496  if (preg_match('/^!/', $tmpcrit)) {
9497  $newres .= $field." NOT LIKE '"; // ! as exclude character
9498  $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
9499  } else $newres .= $field." LIKE '";
9500 
9501  if (preg_match('/^[\^\$]/', $tmpcrit)) {
9502  $tmpbefore = '';
9503  $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
9504  }
9505  if (preg_match('/[\^\$]$/', $tmpcrit)) {
9506  $tmpafter = '';
9507  $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
9508  }
9509  $newres .= $tmpbefore;
9510  $newres .= $db->escape($tmpcrit2);
9511  $newres .= $tmpafter;
9512  $newres .= "'";
9513  if ($tmpcrit2 == '') {
9514  $newres .= " OR ".$field." IS NULL";
9515  }
9516  }
9517 
9518  $i3++;
9519  }
9520  $i2++; // a criteria was added to string
9521  }
9522  $i++;
9523  }
9524  if ($newres) {
9525  $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
9526  }
9527  $j++;
9528  }
9529  $res = ($nofirstand ? "" : " AND ")."(".$res.")";
9530  //print 'xx'.$res.'yy';
9531  return $res;
9532 }
9533 
9540 function showDirectDownloadLink($object)
9541 {
9542  global $conf, $langs;
9543 
9544  $out = '';
9545  $url = $object->getLastMainDocLink($object->element);
9546 
9547  $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
9548  if ($url) {
9549  $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
9550  $out .= ajax_autoselect("directdownloadlink", 0);
9551  } else {
9552  $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
9553  }
9554 
9555  return $out;
9556 }
9557 
9566 function getImageFileNameForSize($file, $extName, $extImgTarget = '')
9567 {
9568  $dirName = dirname($file);
9569  if ($dirName == '.') {
9570  $dirName = '';
9571  }
9572 
9573  $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
9574  $fileName = basename($fileName);
9575 
9576  if (empty($extImgTarget)) {
9577  $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
9578  }
9579  if (empty($extImgTarget)) {
9580  $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
9581  }
9582  if (empty($extImgTarget)) {
9583  $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
9584  }
9585  if (empty($extImgTarget)) {
9586  $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
9587  }
9588  if (empty($extImgTarget)) {
9589  $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
9590  }
9591  if (empty($extImgTarget)) {
9592  $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
9593  }
9594 
9595  if (!$extImgTarget) {
9596  return $file;
9597  }
9598 
9599  $subdir = '';
9600  if ($extName) {
9601  $subdir = 'thumbs/';
9602  }
9603 
9604  return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
9605 }
9606 
9607 
9617 function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
9618 {
9619  global $conf, $langs;
9620 
9621  if (empty($conf->use_javascript_ajax)) {
9622  return '';
9623  }
9624 
9625  $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
9626 
9627  if ($alldata == 1) {
9628  if ($isAllowedForPreview) {
9629  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));
9630  } else {
9631  return array();
9632  }
9633  }
9634 
9635  // old behavior, return a string
9636  if ($isAllowedForPreview) {
9637  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')).'\')';
9638  } else {
9639  return '';
9640  }
9641 }
9642 
9643 
9652 function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
9653 {
9654  global $langs;
9655  $out = '<script>
9656  jQuery(document).ready(function () {
9657  jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
9658  });
9659  </script>';
9660  if ($addlink) {
9661  if ($textonlink === 'image') {
9662  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
9663  } else {
9664  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
9665  }
9666  }
9667  return $out;
9668 }
9669 
9677 function dolIsAllowedForPreview($file)
9678 {
9679  global $conf;
9680 
9681  // Check .noexe extension in filename
9682  if (preg_match('/\.noexe$/i', $file)) {
9683  return 0;
9684  }
9685 
9686  // Check mime types
9687  $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
9688  if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) {
9689  $mime_preview[] = 'svg+xml';
9690  }
9691  //$mime_preview[]='vnd.oasis.opendocument.presentation';
9692  //$mime_preview[]='archive';
9693  $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
9694  if ($num_mime !== false) {
9695  return 1;
9696  }
9697 
9698  // By default, not allowed for preview
9699  return 0;
9700 }
9701 
9702 
9712 function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
9713 {
9714  $mime = $default;
9715  $imgmime = 'other.png';
9716  $famime = 'file-o';
9717  $srclang = '';
9718 
9719  $tmpfile = preg_replace('/\.noexe$/', '', $file);
9720 
9721  // Plain text files
9722  if (preg_match('/\.txt$/i', $tmpfile)) {
9723  $mime = 'text/plain';
9724  $imgmime = 'text.png';
9725  $famime = 'file-text-o';
9726  }
9727  if (preg_match('/\.rtx$/i', $tmpfile)) {
9728  $mime = 'text/richtext';
9729  $imgmime = 'text.png';
9730  $famime = 'file-text-o';
9731  }
9732  if (preg_match('/\.csv$/i', $tmpfile)) {
9733  $mime = 'text/csv';
9734  $imgmime = 'text.png';
9735  $famime = 'file-text-o';
9736  }
9737  if (preg_match('/\.tsv$/i', $tmpfile)) {
9738  $mime = 'text/tab-separated-values';
9739  $imgmime = 'text.png';
9740  $famime = 'file-text-o';
9741  }
9742  if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
9743  $mime = 'text/plain';
9744  $imgmime = 'text.png';
9745  $famime = 'file-text-o';
9746  }
9747  if (preg_match('/\.ini$/i', $tmpfile)) {
9748  $mime = 'text/plain';
9749  $imgmime = 'text.png';
9750  $srclang = 'ini';
9751  $famime = 'file-text-o';
9752  }
9753  if (preg_match('/\.md$/i', $tmpfile)) {
9754  $mime = 'text/plain';
9755  $imgmime = 'text.png';
9756  $srclang = 'md';
9757  $famime = 'file-text-o';
9758  }
9759  if (preg_match('/\.css$/i', $tmpfile)) {
9760  $mime = 'text/css';
9761  $imgmime = 'css.png';
9762  $srclang = 'css';
9763  $famime = 'file-text-o';
9764  }
9765  if (preg_match('/\.lang$/i', $tmpfile)) {
9766  $mime = 'text/plain';
9767  $imgmime = 'text.png';
9768  $srclang = 'lang';
9769  $famime = 'file-text-o';
9770  }
9771  // Certificate files
9772  if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) {
9773  $mime = 'text/plain';
9774  $imgmime = 'text.png';
9775  $famime = 'file-text-o';
9776  }
9777  // XML based (HTML/XML/XAML)
9778  if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) {
9779  $mime = 'text/html';
9780  $imgmime = 'html.png';
9781  $srclang = 'html';
9782  $famime = 'file-text-o';
9783  }
9784  if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
9785  $mime = 'text/xml';
9786  $imgmime = 'other.png';
9787  $srclang = 'xml';
9788  $famime = 'file-text-o';
9789  }
9790  if (preg_match('/\.xaml$/i', $tmpfile)) {
9791  $mime = 'text/xml';
9792  $imgmime = 'other.png';
9793  $srclang = 'xaml';
9794  $famime = 'file-text-o';
9795  }
9796  // Languages
9797  if (preg_match('/\.bas$/i', $tmpfile)) {
9798  $mime = 'text/plain';
9799  $imgmime = 'text.png';
9800  $srclang = 'bas';
9801  $famime = 'file-code-o';
9802  }
9803  if (preg_match('/\.(c)$/i', $tmpfile)) {
9804  $mime = 'text/plain';
9805  $imgmime = 'text.png';
9806  $srclang = 'c';
9807  $famime = 'file-code-o';
9808  }
9809  if (preg_match('/\.(cpp)$/i', $tmpfile)) {
9810  $mime = 'text/plain';
9811  $imgmime = 'text.png';
9812  $srclang = 'cpp';
9813  $famime = 'file-code-o';
9814  }
9815  if (preg_match('/\.cs$/i', $tmpfile)) {
9816  $mime = 'text/plain';
9817  $imgmime = 'text.png';
9818  $srclang = 'cs';
9819  $famime = 'file-code-o';
9820  }
9821  if (preg_match('/\.(h)$/i', $tmpfile)) {
9822  $mime = 'text/plain';
9823  $imgmime = 'text.png';
9824  $srclang = 'h';
9825  $famime = 'file-code-o';
9826  }
9827  if (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
9828  $mime = 'text/plain';
9829  $imgmime = 'text.png';
9830  $srclang = 'java';
9831  $famime = 'file-code-o';
9832  }
9833  if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
9834  $mime = 'text/plain';
9835  $imgmime = 'php.png';
9836  $srclang = 'php';
9837  $famime = 'file-code-o';
9838  }
9839  if (preg_match('/\.phtml$/i', $tmpfile)) {
9840  $mime = 'text/plain';
9841  $imgmime = 'php.png';
9842  $srclang = 'php';
9843  $famime = 'file-code-o';
9844  }
9845  if (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
9846  $mime = 'text/plain';
9847  $imgmime = 'pl.png';
9848  $srclang = 'perl';
9849  $famime = 'file-code-o';
9850  }
9851  if (preg_match('/\.sql$/i', $tmpfile)) {
9852  $mime = 'text/plain';
9853  $imgmime = 'text.png';
9854  $srclang = 'sql';
9855  $famime = 'file-code-o';
9856  }
9857  if (preg_match('/\.js$/i', $tmpfile)) {
9858  $mime = 'text/x-javascript';
9859  $imgmime = 'jscript.png';
9860  $srclang = 'js';
9861  $famime = 'file-code-o';
9862  }
9863  // Open office
9864  if (preg_match('/\.odp$/i', $tmpfile)) {
9865  $mime = 'application/vnd.oasis.opendocument.presentation';
9866  $imgmime = 'ooffice.png';
9867  $famime = 'file-powerpoint-o';
9868  }
9869  if (preg_match('/\.ods$/i', $tmpfile)) {
9870  $mime = 'application/vnd.oasis.opendocument.spreadsheet';
9871  $imgmime = 'ooffice.png';
9872  $famime = 'file-excel-o';
9873  }
9874  if (preg_match('/\.odt$/i', $tmpfile)) {
9875  $mime = 'application/vnd.oasis.opendocument.text';
9876  $imgmime = 'ooffice.png';
9877  $famime = 'file-word-o';
9878  }
9879  // MS Office
9880  if (preg_match('/\.mdb$/i', $tmpfile)) {
9881  $mime = 'application/msaccess';
9882  $imgmime = 'mdb.png';
9883  $famime = 'file-o';
9884  }
9885  if (preg_match('/\.doc(x|m)?$/i', $tmpfile)) {
9886  $mime = 'application/msword';
9887  $imgmime = 'doc.png';
9888  $famime = 'file-word-o';
9889  }
9890  if (preg_match('/\.dot(x|m)?$/i', $tmpfile)) {
9891  $mime = 'application/msword';
9892  $imgmime = 'doc.png';
9893  $famime = 'file-word-o';
9894  }
9895  if (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
9896  $mime = 'application/vnd.ms-excel';
9897  $imgmime = 'xls.png';
9898  $famime = 'file-excel-o';
9899  }
9900  if (preg_match('/\.xla(m)?$/i', $tmpfile)) {
9901  $mime = 'application/vnd.ms-excel';
9902  $imgmime = 'xls.png';
9903  $famime = 'file-excel-o';
9904  }
9905  if (preg_match('/\.xls$/i', $tmpfile)) {
9906  $mime = 'application/vnd.ms-excel';
9907  $imgmime = 'xls.png';
9908  $famime = 'file-excel-o';
9909  }
9910  if (preg_match('/\.xls(b|m|x)$/i', $tmpfile)) {
9911  $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
9912  $imgmime = 'xls.png';
9913  $famime = 'file-excel-o';
9914  }
9915  if (preg_match('/\.pps(m|x)?$/i', $tmpfile)) {
9916  $mime = 'application/vnd.ms-powerpoint';
9917  $imgmime = 'ppt.png';
9918  $famime = 'file-powerpoint-o';
9919  }
9920  if (preg_match('/\.ppt(m|x)?$/i', $tmpfile)) {
9921  $mime = 'application/x-mspowerpoint';
9922  $imgmime = 'ppt.png';
9923  $famime = 'file-powerpoint-o';
9924  }
9925  // Other
9926  if (preg_match('/\.pdf$/i', $tmpfile)) {
9927  $mime = 'application/pdf';
9928  $imgmime = 'pdf.png';
9929  $famime = 'file-pdf-o';
9930  }
9931  // Scripts
9932  if (preg_match('/\.bat$/i', $tmpfile)) {
9933  $mime = 'text/x-bat';
9934  $imgmime = 'script.png';
9935  $srclang = 'dos';
9936  $famime = 'file-code-o';
9937  }
9938  if (preg_match('/\.sh$/i', $tmpfile)) {
9939  $mime = 'text/x-sh';
9940  $imgmime = 'script.png';
9941  $srclang = 'bash';
9942  $famime = 'file-code-o';
9943  }
9944  if (preg_match('/\.ksh$/i', $tmpfile)) {
9945  $mime = 'text/x-ksh';
9946  $imgmime = 'script.png';
9947  $srclang = 'bash';
9948  $famime = 'file-code-o';
9949  }
9950  if (preg_match('/\.bash$/i', $tmpfile)) {
9951  $mime = 'text/x-bash';
9952  $imgmime = 'script.png';
9953  $srclang = 'bash';
9954  $famime = 'file-code-o';
9955  }
9956  // Images
9957  if (preg_match('/\.ico$/i', $tmpfile)) {
9958  $mime = 'image/x-icon';
9959  $imgmime = 'image.png';
9960  $famime = 'file-image-o';
9961  }
9962  if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
9963  $mime = 'image/jpeg';
9964  $imgmime = 'image.png';
9965  $famime = 'file-image-o';
9966  }
9967  if (preg_match('/\.png$/i', $tmpfile)) {
9968  $mime = 'image/png';
9969  $imgmime = 'image.png';
9970  $famime = 'file-image-o';
9971  }
9972  if (preg_match('/\.gif$/i', $tmpfile)) {
9973  $mime = 'image/gif';
9974  $imgmime = 'image.png';
9975  $famime = 'file-image-o';
9976  }
9977  if (preg_match('/\.bmp$/i', $tmpfile)) {
9978  $mime = 'image/bmp';
9979  $imgmime = 'image.png';
9980  $famime = 'file-image-o';
9981  }
9982  if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
9983  $mime = 'image/tiff';
9984  $imgmime = 'image.png';
9985  $famime = 'file-image-o';
9986  }
9987  if (preg_match('/\.svg$/i', $tmpfile)) {
9988  $mime = 'image/svg+xml';
9989  $imgmime = 'image.png';
9990  $famime = 'file-image-o';
9991  }
9992  if (preg_match('/\.webp$/i', $tmpfile)) {
9993  $mime = 'image/webp';
9994  $imgmime = 'image.png';
9995  $famime = 'file-image-o';
9996  }
9997  // Calendar
9998  if (preg_match('/\.vcs$/i', $tmpfile)) {
9999  $mime = 'text/calendar';
10000  $imgmime = 'other.png';
10001  $famime = 'file-text-o';
10002  }
10003  if (preg_match('/\.ics$/i', $tmpfile)) {
10004  $mime = 'text/calendar';
10005  $imgmime = 'other.png';
10006  $famime = 'file-text-o';
10007  }
10008  // Other
10009  if (preg_match('/\.torrent$/i', $tmpfile)) {
10010  $mime = 'application/x-bittorrent';
10011  $imgmime = 'other.png';
10012  $famime = 'file-o';
10013  }
10014  // Audio
10015  if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) {
10016  $mime = 'audio';
10017  $imgmime = 'audio.png';
10018  $famime = 'file-audio-o';
10019  }
10020  // Video
10021  if (preg_match('/\.mp4$/i', $tmpfile)) {
10022  $mime = 'video/mp4';
10023  $imgmime = 'video.png';
10024  $famime = 'file-video-o';
10025  }
10026  if (preg_match('/\.ogv$/i', $tmpfile)) {
10027  $mime = 'video/ogg';
10028  $imgmime = 'video.png';
10029  $famime = 'file-video-o';
10030  }
10031  if (preg_match('/\.webm$/i', $tmpfile)) {
10032  $mime = 'video/webm';
10033  $imgmime = 'video.png';
10034  $famime = 'file-video-o';
10035  }
10036  if (preg_match('/\.avi$/i', $tmpfile)) {
10037  $mime = 'video/x-msvideo';
10038  $imgmime = 'video.png';
10039  $famime = 'file-video-o';
10040  }
10041  if (preg_match('/\.divx$/i', $tmpfile)) {
10042  $mime = 'video/divx';
10043  $imgmime = 'video.png';
10044  $famime = 'file-video-o';
10045  }
10046  if (preg_match('/\.xvid$/i', $tmpfile)) {
10047  $mime = 'video/xvid';
10048  $imgmime = 'video.png';
10049  $famime = 'file-video-o';
10050  }
10051  if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
10052  $mime = 'video';
10053  $imgmime = 'video.png';
10054  $famime = 'file-video-o';
10055  }
10056  // Archive
10057  if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) {
10058  $mime = 'archive';
10059  $imgmime = 'archive.png';
10060  $famime = 'file-archive-o';
10061  } // application/xxx where zzz is zip, ...
10062  // Exe
10063  if (preg_match('/\.(exe|com)$/i', $tmpfile)) {
10064  $mime = 'application/octet-stream';
10065  $imgmime = 'other.png';
10066  $famime = 'file-o';
10067  }
10068  // Lib
10069  if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) {
10070  $mime = 'library';
10071  $imgmime = 'library.png';
10072  $famime = 'file-o';
10073  }
10074  // Err
10075  if (preg_match('/\.err$/i', $tmpfile)) {
10076  $mime = 'error';
10077  $imgmime = 'error.png';
10078  $famime = 'file-text-o';
10079  }
10080 
10081  // Return string
10082  if ($mode == 1) {
10083  $tmp = explode('/', $mime);
10084  return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
10085  }
10086  if ($mode == 2) {
10087  return $imgmime;
10088  }
10089  if ($mode == 3) {
10090  return $srclang;
10091  }
10092  if ($mode == 4) {
10093  return $famime;
10094  }
10095  return $mime;
10096 }
10097 
10109 function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
10110 {
10111  global $conf, $db;
10112 
10113  $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
10114 
10115  $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
10116 
10117  if (is_null($dictvalues)) {
10118  $dictvalues = array();
10119 
10120  $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
10121  if ($checkentity) {
10122  $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
10123  }
10124 
10125  $resql = $db->query($sql);
10126  if ($resql) {
10127  while ($obj = $db->fetch_object($resql)) {
10128  $dictvalues[$obj->{$rowidfield}] = $obj; // $obj is stdClass
10129  }
10130  } else {
10131  dol_print_error($db);
10132  }
10133 
10134  $conf->cache['dictvalues_'.$tablename] = $dictvalues;
10135  }
10136 
10137  if (!empty($dictvalues[$id])) {
10138  // Found
10139  $tmp = $dictvalues[$id];
10140  return (property_exists($tmp, $field) ? $tmp->$field : '');
10141  } else {
10142  // Not found
10143  return '';
10144  }
10145 }
10146 
10153 function colorIsLight($stringcolor)
10154 {
10155  $stringcolor = str_replace('#', '', $stringcolor);
10156  $res = -1;
10157  if (!empty($stringcolor)) {
10158  $res = 0;
10159  $tmp = explode(',', $stringcolor);
10160  if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
10161  $r = $tmp[0];
10162  $g = $tmp[1];
10163  $b = $tmp[2];
10164  } else {
10165  $hexr = $stringcolor[0].$stringcolor[1];
10166  $hexg = $stringcolor[2].$stringcolor[3];
10167  $hexb = $stringcolor[4].$stringcolor[5];
10168  $r = hexdec($hexr);
10169  $g = hexdec($hexg);
10170  $b = hexdec($hexb);
10171  }
10172  $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
10173  if ($bright > 0.6) {
10174  $res = 1;
10175  }
10176  }
10177  return $res;
10178 }
10179 
10188 function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
10189 {
10190  global $conf;
10191 
10192  //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
10193  //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
10194  if (empty($menuentry['enabled'])) {
10195  return 0; // Entry disabled by condition
10196  }
10197  if ($type_user && $menuentry['module']) {
10198  $tmploops = explode('|', $menuentry['module']);
10199  $found = 0;
10200  foreach ($tmploops as $tmploop) {
10201  if (in_array($tmploop, $listofmodulesforexternal)) {
10202  $found++;
10203  break;
10204  }
10205  }
10206  if (!$found) {
10207  return 0; // Entry is for menus all excluded to external users
10208  }
10209  }
10210  if (!$menuentry['perms'] && $type_user) {
10211  return 0; // No permissions and user is external
10212  }
10213  if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) {
10214  return 0; // No permissions and option to hide when not allowed, even for internal user, is on
10215  }
10216  if (!$menuentry['perms']) {
10217  return 2; // No permissions and user is external
10218  }
10219  return 1;
10220 }
10221 
10229 function roundUpToNextMultiple($n, $x = 5)
10230 {
10231  return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
10232 }
10233 
10245 function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
10246 {
10247  $attr = array(
10248  'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css'])
10249  );
10250 
10251  if (empty($html)) {
10252  $html = $label;
10253  }
10254 
10255  if (!empty($url)) {
10256  $attr['href'] = $url;
10257  }
10258 
10259  if ($mode === 'dot') {
10260  $attr['class'] .= ' classfortooltip';
10261  $attr['title'] = $html;
10262  $attr['aria-label'] = $label;
10263  $html = '';
10264  }
10265 
10266  // Override attr
10267  if (!empty($params['attr']) && is_array($params['attr'])) {
10268  foreach ($params['attr'] as $key => $value) {
10269  if ($key == 'class') {
10270  $attr['class'] .= ' '.$value;
10271  } elseif ($key == 'classOverride') {
10272  $attr['class'] = $value;
10273  } else {
10274  $attr[$key] = $value;
10275  }
10276  }
10277  }
10278 
10279  // TODO: add hook
10280 
10281  // escape all attribute
10282  $attr = array_map('dol_escape_htmltag', $attr);
10283 
10284  $TCompiledAttr = array();
10285  foreach ($attr as $key => $value) {
10286  $TCompiledAttr[] = $key.'="'.$value.'"';
10287  }
10288 
10289  $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
10290 
10291  $tag = !empty($url) ? 'a' : 'span';
10292 
10293  return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
10294 }
10295 
10296 
10309 function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
10310 {
10311  global $conf;
10312 
10313  $return = '';
10314  $dolGetBadgeParams = array();
10315 
10316  if (!empty($params['badgeParams'])) {
10317  $dolGetBadgeParams = $params['badgeParams'];
10318  }
10319 
10320  // TODO : add a hook
10321  if ($displayMode == 0) {
10322  $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
10323  } elseif ($displayMode == 1) {
10324  $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10325  } elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) {
10326  // Use status with images (for backward compatibility)
10327  $return = '';
10328  $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
10329  $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>' : '');
10330 
10331  // For small screen, we always use the short label instead of long label.
10332  if (!empty($conf->dol_optimize_smallscreen)) {
10333  if ($displayMode == 0) {
10334  $displayMode = 1;
10335  } elseif ($displayMode == 4) {
10336  $displayMode = 2;
10337  } elseif ($displayMode == 6) {
10338  $displayMode = 5;
10339  }
10340  }
10341 
10342  // For backward compatibility. Image's filename are still in French, so we use this array to convert
10343  $statusImg = array(
10344  'status0' => 'statut0',
10345  'status1' => 'statut1',
10346  'status2' => 'statut2',
10347  'status3' => 'statut3',
10348  'status4' => 'statut4',
10349  'status5' => 'statut5',
10350  'status6' => 'statut6',
10351  'status7' => 'statut7',
10352  'status8' => 'statut8',
10353  'status9' => 'statut9'
10354  );
10355 
10356  if (!empty($statusImg[$statusType])) {
10357  $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
10358  } else {
10359  $htmlImg = img_picto($statusLabel, $statusType);
10360  }
10361 
10362  if ($displayMode === 2) {
10363  $return = $htmlImg.' '.$htmlLabelShort;
10364  } elseif ($displayMode === 3) {
10365  $return = $htmlImg;
10366  } elseif ($displayMode === 4) {
10367  $return = $htmlImg.' '.$htmlLabel;
10368  } elseif ($displayMode === 5) {
10369  $return = $htmlLabelShort.' '.$htmlImg;
10370  } else { // $displayMode >= 6
10371  $return = $htmlLabel.' '.$htmlImg;
10372  }
10373  } elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) {
10374  // Use new badge
10375  $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10376 
10377  $dolGetBadgeParams['attr']['class'] = 'badge-status';
10378  $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
10379 
10380  if ($displayMode == 3) {
10381  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
10382  } elseif ($displayMode === 5) {
10383  $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
10384  } else {
10385  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
10386  }
10387  }
10388 
10389  return $return;
10390 }
10391 
10392 
10421 function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
10422 {
10423  global $hookmanager, $action, $object, $langs;
10424 
10425  $class = 'butAction';
10426  if ($actionType == 'danger' || $actionType == 'delete') {
10427  $class = 'butActionDelete';
10428  if (!empty($url) && strpos($url, 'token=') === false) $url .= '&token='.newToken();
10429  }
10430 
10431  $attr = array(
10432  'class' => $class,
10433  'href' => empty($url) ? '' : $url,
10434  'title' => $label
10435  );
10436 
10437  if (empty($html)) {
10438  $html = $label;
10439  $attr['title'] = ''; // if html not set, leave label on title is redundant
10440  } else {
10441  $attr['aria-label'] = $label;
10442  }
10443 
10444  if (empty($userRight)) {
10445  $attr['class'] = 'butActionRefused';
10446  $attr['href'] = '';
10447  }
10448 
10449  if (!empty($id)) {
10450  $attr['id'] = $id;
10451  }
10452 
10453 
10454  // Override attr
10455  if (!empty($params['attr']) && is_array($params['attr'])) {
10456  foreach ($params['attr'] as $key => $value) {
10457  if ($key == 'class') {
10458  $attr['class'] .= ' '.$value;
10459  } elseif ($key == 'classOverride') {
10460  $attr['class'] = $value;
10461  } else {
10462  $attr[$key] = $value;
10463  }
10464  }
10465  }
10466 
10467  // automatic add tooltip when title is detected
10468  if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
10469  $attr['class'].= ' classfortooltip';
10470  }
10471 
10472  // Js Confirm button
10473  if ($userRight && !empty($params['confirm'])) {
10474  if (!is_array($params['confirm'])) {
10475  $params['confirm'] = array();
10476  }
10477 
10478  if (empty($params['confirm']['url'])) {
10479  $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
10480  }
10481 
10482  // for js desabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
10483  $attr['data-confirm-url'] = $params['confirm']['url'];
10484  $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
10485  $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
10486  $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
10487  $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
10488  $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
10489  $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
10490 
10491  $attr['class'].= ' butActionConfirm';
10492  }
10493 
10494  if (isset($attr['href']) && empty($attr['href'])) {
10495  unset($attr['href']);
10496  }
10497 
10498  // escape all attribute
10499  $attr = array_map('dol_escape_htmltag', $attr);
10500 
10501  $TCompiledAttr = array();
10502  foreach ($attr as $key => $value) {
10503  $TCompiledAttr[] = $key.'="'.$value.'"';
10504  }
10505 
10506  $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
10507 
10508  $tag = !empty($attr['href']) ? 'a' : 'span';
10509 
10510 
10511  $parameters = array(
10512  'TCompiledAttr' => $TCompiledAttr, // array
10513  'compiledAttributes' => $compiledAttributes, // string
10514  'attr' => $attr,
10515  'tag' => $tag,
10516  'label' => $label,
10517  'html' => $html,
10518  'actionType' => $actionType,
10519  'url' => $url,
10520  'id' => $id,
10521  'userRight' => $userRight,
10522  'params' => $params
10523  );
10524 
10525  $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
10526  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
10527 
10528  if (empty($reshook)) {
10529  return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
10530  } else {
10531  return $hookmanager->resPrint;
10532  }
10533 }
10534 
10541 function dolGetButtonTitleSeparator($moreClass = "")
10542 {
10543  return '<span class="button-title-separator '.$moreClass.'" ></span>';
10544 }
10545 
10552 function getFieldErrorIcon($fieldValidationErrorMsg)
10553 {
10554  $out = '';
10555  if (!empty($fieldValidationErrorMsg)) {
10556  $out.= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
10557  $out.= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
10558  $out.= '</span>';
10559  }
10560 
10561  return $out;
10562 }
10563 
10576 function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
10577 {
10578  global $langs, $conf, $user;
10579 
10580  // Actually this conf is used in css too for external module compatibility and smooth transition to this function
10581  if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) {
10582  return '';
10583  }
10584 
10585  $class = 'btnTitle';
10586  if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots'))) {
10587  $class .= ' btnTitlePlus';
10588  }
10589  $useclassfortooltip = 1;
10590 
10591  if (!empty($params['morecss'])) {
10592  $class .= ' '.$params['morecss'];
10593  }
10594 
10595  $attr = array(
10596  'class' => $class,
10597  'href' => empty($url) ? '' : $url
10598  );
10599 
10600  if (!empty($helpText)) {
10601  $attr['title'] = dol_escape_htmltag($helpText);
10602  } elseif (empty($attr['title']) && $label) {
10603  $attr['title'] = $label;
10604  $useclassfortooltip = 0;
10605  }
10606 
10607  if ($status == 2) {
10608  $attr['class'] .= ' btnTitleSelected';
10609  } elseif ($status <= 0) {
10610  $attr['class'] .= ' refused';
10611 
10612  $attr['href'] = '';
10613 
10614  if ($status == -1) { // disable
10615  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
10616  } elseif ($status == 0) { // Not enough permissions
10617  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
10618  }
10619  }
10620 
10621  if (!empty($attr['title']) && $useclassfortooltip) {
10622  $attr['class'] .= ' classfortooltip';
10623  }
10624 
10625  if (!empty($id)) {
10626  $attr['id'] = $id;
10627  }
10628 
10629  // Override attr
10630  if (!empty($params['attr']) && is_array($params['attr'])) {
10631  foreach ($params['attr'] as $key => $value) {
10632  if ($key == 'class') {
10633  $attr['class'] .= ' '.$value;
10634  } elseif ($key == 'classOverride') {
10635  $attr['class'] = $value;
10636  } else {
10637  $attr[$key] = $value;
10638  }
10639  }
10640  }
10641 
10642  if (isset($attr['href']) && empty($attr['href'])) {
10643  unset($attr['href']);
10644  }
10645 
10646  // TODO : add a hook
10647 
10648  // escape all attribute
10649  $attr = array_map('dol_escape_htmltag', $attr);
10650 
10651  $TCompiledAttr = array();
10652  foreach ($attr as $key => $value) {
10653  $TCompiledAttr[] = $key.'="'.$value.'"';
10654  }
10655 
10656  $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
10657 
10658  $tag = (empty($attr['href']) ? 'span' : 'a');
10659 
10660  $button = '<'.$tag.' '.$compiledAttributes.'>';
10661  $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
10662  if (!empty($params['forcenohideoftext'])) {
10663  $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
10664  }
10665  $button .= '</'.$tag.'>';
10666 
10667  return $button;
10668 }
10669 
10677 function getElementProperties($element_type)
10678 {
10679  $regs = array();
10680 
10681  $classfile = $classname = $classpath = '';
10682 
10683  // Parse element/subelement (ex: project_task)
10684  $module = $element_type;
10685  $element = $element_type;
10686  $subelement = $element_type;
10687 
10688  // If we ask an resource form external module (instead of default path)
10689  if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) {
10690  $element = $subelement = $regs[1];
10691  $module = $regs[2];
10692  }
10693 
10694  //print '<br>1. element : '.$element.' - module : '.$module .'<br>';
10695  if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) {
10696  $module = $element = $regs[1];
10697  $subelement = $regs[2];
10698  }
10699 
10700  // For compat
10701  if ($element_type == "action") {
10702  $classpath = 'comm/action/class';
10703  $subelement = 'Actioncomm';
10704  $module = 'agenda';
10705  }
10706 
10707  // To work with non standard path
10708  if ($element_type == 'facture' || $element_type == 'invoice') {
10709  $classpath = 'compta/facture/class';
10710  $module = 'facture';
10711  $subelement = 'facture';
10712  }
10713  if ($element_type == 'commande' || $element_type == 'order') {
10714  $classpath = 'commande/class';
10715  $module = 'commande';
10716  $subelement = 'commande';
10717  }
10718  if ($element_type == 'propal') {
10719  $classpath = 'comm/propal/class';
10720  }
10721  if ($element_type == 'supplier_proposal') {
10722  $classpath = 'supplier_proposal/class';
10723  }
10724  if ($element_type == 'shipping') {
10725  $classpath = 'expedition/class';
10726  $subelement = 'expedition';
10727  $module = 'expedition_bon';
10728  }
10729  if ($element_type == 'delivery') {
10730  $classpath = 'delivery/class';
10731  $subelement = 'delivery';
10732  $module = 'delivery_note';
10733  }
10734  if ($element_type == 'contract') {
10735  $classpath = 'contrat/class';
10736  $module = 'contrat';
10737  $subelement = 'contrat';
10738  }
10739  if ($element_type == 'member') {
10740  $classpath = 'adherents/class';
10741  $module = 'adherent';
10742  $subelement = 'adherent';
10743  }
10744  if ($element_type == 'cabinetmed_cons') {
10745  $classpath = 'cabinetmed/class';
10746  $module = 'cabinetmed';
10747  $subelement = 'cabinetmedcons';
10748  }
10749  if ($element_type == 'fichinter') {
10750  $classpath = 'fichinter/class';
10751  $module = 'ficheinter';
10752  $subelement = 'fichinter';
10753  }
10754  if ($element_type == 'dolresource' || $element_type == 'resource') {
10755  $classpath = 'resource/class';
10756  $module = 'resource';
10757  $subelement = 'dolresource';
10758  }
10759  if ($element_type == 'propaldet') {
10760  $classpath = 'comm/propal/class';
10761  $module = 'propal';
10762  $subelement = 'propaleligne';
10763  }
10764  if ($element_type == 'order_supplier') {
10765  $classpath = 'fourn/class';
10766  $module = 'fournisseur';
10767  $subelement = 'commandefournisseur';
10768  $classfile = 'fournisseur.commande';
10769  }
10770  if ($element_type == 'invoice_supplier') {
10771  $classpath = 'fourn/class';
10772  $module = 'fournisseur';
10773  $subelement = 'facturefournisseur';
10774  $classfile = 'fournisseur.facture';
10775  }
10776  if ($element_type == "service") {
10777  $classpath = 'product/class';
10778  $subelement = 'product';
10779  }
10780 
10781  if (empty($classfile)) {
10782  $classfile = strtolower($subelement);
10783  }
10784  if (empty($classname)) {
10785  $classname = ucfirst($subelement);
10786  }
10787  if (empty($classpath)) {
10788  $classpath = $module.'/class';
10789  }
10790 
10791  $element_properties = array(
10792  'module' => $module,
10793  'classpath' => $classpath,
10794  'element' => $element,
10795  'subelement' => $subelement,
10796  'classfile' => $classfile,
10797  'classname' => $classname
10798  );
10799  return $element_properties;
10800 }
10801 
10811 function fetchObjectByElement($element_id, $element_type, $element_ref = '')
10812 {
10813  global $conf, $db;
10814 
10815  $element_prop = getElementProperties($element_type);
10816  if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled) {
10817  dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
10818 
10819  $objecttmp = new $element_prop['classname']($db);
10820  $ret = $objecttmp->fetch($element_id, $element_ref);
10821  if ($ret >= 0) {
10822  return $objecttmp;
10823  }
10824  }
10825  return 0;
10826 }
10827 
10834 function isAFileWithExecutableContent($filename)
10835 {
10836  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)) {
10837  return true;
10838  }
10839 
10840  return false;
10841 }
10842 
10849 function newToken()
10850 {
10851  return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
10852 }
10853 
10860 function currentToken()
10861 {
10862  return isset($_SESSION['token']) ? $_SESSION['token'] : '';
10863 }
10864 
10877 function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1)
10878 {
10879  global $langs;
10880 
10881  print '<div class="div-table-responsive-no-min">';
10882  print '<table class="noborder centpercent">';
10883  print '<tr class="liste_titre">';
10884 
10885  print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">';
10886 
10887  print $langs->trans($header);
10888 
10889  // extra space between the first header and the number
10890  if ($number > -1) {
10891  print ' ';
10892  }
10893 
10894  if (!empty($link)) {
10895  if (!empty($arguments)) {
10896  print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
10897  } else {
10898  print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
10899  }
10900  }
10901 
10902  if ($number > -1) {
10903  print '<span class="badge">'.$number.'</span>';
10904  }
10905 
10906  if (!empty($link)) {
10907  print '</a>';
10908  }
10909 
10910  print '</th>';
10911 
10912  if ($number < 0 && !empty($link)) {
10913  print '<th class="right">';
10914 
10915  if (!empty($arguments)) {
10916  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
10917  } else {
10918  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">';
10919  }
10920 
10921  print $langs->trans("FullList");
10922  print '</a>';
10923  print '</th>';
10924  }
10925 
10926  print '</tr>';
10927 }
10928 
10937 function finishSimpleTable($addLineBreak = false)
10938 {
10939  print '</table>';
10940  print '</div>';
10941 
10942  if ($addLineBreak) {
10943  print '<br>';
10944  }
10945 }
10946 
10958 function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
10959 {
10960  global $langs;
10961 
10962  if ($num === 0) {
10963  print '<tr class="oddeven">';
10964  print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>';
10965  print '</tr>';
10966  return;
10967  }
10968 
10969  if ($nbofloop === 0) {
10970  // don't show a summary line
10971  return;
10972  }
10973 
10974  if ($num === 0) {
10975  $colspan = $tableColumnCount;
10976  } elseif ($num > $nbofloop) {
10977  $colspan = $tableColumnCount;
10978  } else {
10979  $colspan = $tableColumnCount - 1;
10980  }
10981 
10982  if ($extraRightColumn) {
10983  $colspan--;
10984  }
10985 
10986  print '<tr class="liste_total">';
10987 
10988  if ($nbofloop > 0 && $num > $nbofloop) {
10989  print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
10990  } else {
10991  print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
10992  print '<td class="right" width="100">'.price($total).'</td>';
10993  }
10994 
10995  if ($extraRightColumn) {
10996  print '<td></td>';
10997  }
10998 
10999  print '</tr>';
11000 }
11001 
11010 function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
11011 {
11012  global $conf;
11013 
11014  if ($method == -1) {
11015  $method = 0;
11016  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) {
11017  $method = 1;
11018  }
11019  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) {
11020  $method = 2;
11021  }
11022  }
11023 
11024  // Be sure we don't have output buffering enabled to have readfile working correctly
11025  while (ob_get_level()) {
11026  ob_end_flush();
11027  }
11028 
11029  // Solution 0
11030  if ($method == 0) {
11031  readfile($fullpath_original_file_osencoded);
11032  } elseif ($method == 1) {
11033  // Solution 1
11034  $handle = fopen($fullpath_original_file_osencoded, "rb");
11035  while (!feof($handle)) {
11036  print fread($handle, 8192);
11037  }
11038  fclose($handle);
11039  } elseif ($method == 2) {
11040  // Solution 2
11041  $handle1 = fopen($fullpath_original_file_osencoded, "rb");
11042  $handle2 = fopen("php://output", "wb");
11043  stream_copy_to_stream($handle1, $handle2);
11044  fclose($handle1);
11045  fclose($handle2);
11046  }
11047 }
11048 
11058 function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
11059 {
11060  /*
11061  global $conf;
11062 
11063  if (!empty($conf->dol_no_mouse_hover)) {
11064  $showonlyonhover = 0;
11065  }*/
11066 
11067  $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
11068  if ($texttoshow === 'none') {
11069  $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>';
11070  } elseif ($texttoshow) {
11071  $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>';
11072  } else {
11073  $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>';
11074  }
11075 
11076  return $result;
11077 }
11078 
11079 
11086 function jsonOrUnserialize($stringtodecode)
11087 {
11088  $result = json_decode($stringtodecode);
11089  if ($result === null) {
11090  $result = unserialize($stringtodecode);
11091  }
11092 
11093  return $result;
11094 }
11095 
11096 
11097 
11113 function dolCheckFilters($sqlfilters, &$error = '')
11114 {
11115  //$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
11116  //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
11117  $tmp = $sqlfilters;
11118  $i = 0; $nb = strlen($tmp);
11119  $counter = 0;
11120  while ($i < $nb) {
11121  if ($tmp[$i] == '(') {
11122  $counter++;
11123  }
11124  if ($tmp[$i] == ')') {
11125  $counter--;
11126  }
11127  if ($counter < 0) {
11128  $error = "Bad sqlfilters=".$sqlfilters;
11129  dol_syslog($error, LOG_WARNING);
11130  return false;
11131  }
11132  $i++;
11133  }
11134  return true;
11135 }
11136 
11152 function dolForgeCriteriaCallback($matches)
11153 {
11154  global $db;
11155 
11156  dol_syslog("Convert matches ".$matches[1]);
11157  if (empty($matches[1])) {
11158  return '';
11159  }
11160  $tmp = explode(':', $matches[1], 3);
11161 
11162  if (count($tmp) < 3) {
11163  return '';
11164  }
11165 
11166  $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
11167 
11168  $operator = strtoupper(preg_replace('/[^a-z<>=]/i', '', trim($tmp[1])));
11169  if ($operator == 'NOTLIKE') {
11170  $operator = 'NOT LIKE';
11171  }
11172 
11173  $tmpescaped = trim($tmp[2]);
11174  $regbis = array();
11175  if ($operator == 'IN') {
11176  $tmpescaped = "(".$db->sanitize($tmpescaped, 1).")";
11177  } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
11178  $tmpescaped = "'".$db->escape($regbis[1])."'";
11179  } else {
11180  $tmpescaped = $db->sanitize($db->escape($tmpescaped));
11181  }
11182 
11183  return $db->escape($operand).' '.$db->escape($operator)." ".$tmpescaped;
11184 }
dol_convert_file($fileinput, $ext= 'png', $fileoutput= '', $page= '')
Convert an image file or a PDF into another image format.
Definition: files.lib.php:1963
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...
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
GETPOST($paramname, $check= 'alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolIsAllowedForPreview($file)
Return if a file is qualified for preview.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto= 'UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
dol_sanitizePathName($str, $newstr= '_', $unaccent=1)
Clean a string to use it as a path name.
img_edit($titlealt= 'default', $float=0, $other= '')
Show logo editer/modifier fiche.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formated messages to output (Used to show messages on html output).
dol_getcache($memoryid)
Read a memory area shared by all users, all sessions on server.
Definition: memory.lib.php:135
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow= '')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
dol_substr($string, $start, $length, $stringencoding= '', $trunconbytes=0)
Make a substring.
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
getDoliDBInstance($type, $host, $user, $pass, $name, $port)
Return a DoliDB instance (database handler).
if($cancel &&!$id) if($action== 'add'&&!$cancel) if($action== 'delete') if($id) $form
Actions.
Definition: card.php:142
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...
getUserRemoteIP()
Return the IP of remote user.
dol_get_prev_month($month, $year)
Return previous month.
Definition: date.lib.php:470
isACompany()
Return if third party is a company (Business) or an end user (Consumer)
dol_mkdir($dir, $dataroot= '', $newmask= '')
Creation of a directory (this can create recursive subdir)
dol_print_url($url, $target= '_blank', $max=32, $withpicto=0)
Show Url link.
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding= 'UTF-8', $double_encode=false)
Replace htmlentities functions.
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.
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition: inc.php:300
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor &quot;?&quot;.
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
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.
getArrayOfSocialNetworks()
Get array of social network dictionary.
setEntity($currentobject)
Set entity id to use when to create an object.
printCommonFooter($zone= 'private')
Print common footer : conf-&gt;global-&gt;MAIN_HTML_FOOTER js for switch of menu hider js for conf-&gt;global-...
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0)
Clean a string to keep only desirable HTML tags.
img_weather($titlealt, $picto, $moreatt= '', $pictoisfullpath=0, $morecss= '')
Show weather picto.
img_credit_card($brand, $morecss=null)
Return image of a credit card according to its brand name.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
dol_shutdown()
Function called at end of web php process.
dol_html_entity_decode($a, $b, $c= 'UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
Class to manage products or services.
dol_fiche_head($links=array(), $active= '0', $title= '', $notab=0, $picto= '', $pictoisfullpath=0, $morehtmlright= '', $morecss= '', $limittoshow=0, $moretabssuffix= '')
Show tab header of a card.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
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.
dol_now($mode= 'auto')
Return date for now.
picto_required()
Return picto saying a field is required.
img_view($titlealt= 'default', $float=0, $other= 'class="valignmiddle"')
Show logo view card.
get_date_range($date_start, $date_end, $format= '', $outputlangs= '', $withparenthesis=1)
Format output for start and end date.
Class to manage Dolibarr users.
Definition: user.class.php:44
if(!function_exists('utf8_encode')) if(!function_exists('utf8_decode')) getDolGlobalString($key, $default= '')
Return dolibarr global constant string value.
get_htmloutput_errors($mesgstring= '', $mesgarray=array(), $keepembedded=0)
Get formated error messages to output (Used to show messages on html output).
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
dol_print_phone($phone, $countrycode= '', $cid=0, $socid=0, $addlink= '', $separ="&nbsp;", $withpicto= '', $titlealt= '', $adddivfloat=0)
Format phone numbers according to country.
jsonOrUnserialize($stringtodecode)
Decode an encode string.
setEventMessage($mesgs, $style= 'mesgs')
Set event message in dol_events session object.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for properties) With native = 0: P...
dol_getmypid()
Return getmypid() or random PID when function is disabled Some web hosts disable this php function fo...
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...
dolGetButtonAction($label, $html= '', $actionType= 'default', $url= '', $id= '', $userRight=1, $params=array())
Function dolGetButtonAction.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
num2Alpha($n)
Return a numeric value into an Excel like column number.
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...
dol_string_nospecial($str, $newstr= '_', $badcharstoreplace= '', $badcharstoremove= '')
Clean a string from all punctuation characters to use it as a ref or login.
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:446
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:122
dol_sanitizeUrl($stringtoclean, $type=1)
Clean a string to use it as an URL (into a href or src attribute)
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...
checkVal($out= '', $check= 'alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
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...
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition: date.lib.php:439
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error_email($prefixcode, $errormessage= '', $errormessages=array(), $morecss= 'error', $email= '')
Show a public email and error code to contact if technical error.
dol_validElement($element)
Return if var element is ok.
dol_getIdFromCode($db, $key, $tablename, $fieldkey= 'code', $fieldid= 'id', $entityfilter=0, $filters= '')
Return an id or code from a code or id.
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller Note: This function applies same rules than get_default_tva.
img_left($titlealt= 'default', $selected=0, $moreatt= '')
Show left arrow logo.
img_mime($file, $titlealt= '', $morecss= '')
Show MIME img of a file.
ajax_autoselect($htmlname, $addlink= '', $textonlink= 'Link')
Make content of an input box selected when we click into input field.
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...
img_action($titlealt, $numaction, $picto= '')
Show logo action.
img_warning($titlealt= 'default', $moreatt= '', $morecss= 'pictowarning')
Show warning logo.
getLanguageCodeFromCountryCode($countrycode)
Return default language from country code.
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...
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled= '', $morecss= 'button bordertransp', $backtopagejsfields= '')
Return HTML code to output a button to open a dialog popup box.
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
img_search($titlealt= 'default', $other= '')
Show search logo.
dol_print_ip($ip, $mode=0)
Return an IP formated to be shown on screen.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
dol_nboflines($s, $maxchar=0)
Return nb of lines of a clear text.
Class to manage standard extra fields.
Class to manage hooks.
getImageFileNameForSize($file, $extName, $extImgTarget= '')
Return the filename of file to get the thumbs.
setEventMessages($mesg, $mesgs, $style= 'mesgs', $messagekey= '')
Set event messages in dol_events session object.
verifCond($strToEvaluate)
Verify if condition in string is ok or not.
img_error($titlealt= 'default')
Show error logo.
dol_eval($s, $returnvalue=0, $hideerrors=1, $onlysimplestring= '1')
Replace eval function to add more security.
showDirectDownloadLink($object)
Return string with full Url.
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.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput= 'no', $use_short_label=0)
Output a dimension with best unit.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form...
img_down($titlealt= 'default', $selected=0, $moreclass= '')
Show down arrow logo.
Class to manage third parties objects (customers, suppliers, prospects...)
dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
img_pdf($titlealt= 'default', $size=3)
Show pdf logo.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
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 ...
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
get_htmloutput_mesg($mesgstring= '', $mesgarray= '', $style= 'ok', $keepembedded=0)
Get formated messages to output (Used to show messages on html output).
dol_convertToWord($num, $langs, $currency= '', $centimes=false)
Function to return a number into a text.
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
getServerTimeZoneInt($refgmtdate= 'now')
Return server timezone int.
Definition: date.lib.php:83
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge, other ! Closing when no payment: replaced, abandoned
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin= '1', $morecss= 'hideonsmartphone', $textfordropdown= '')
Show information for admin users or standard users.
dol_mimetype($file, $default= 'application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
load_fiche_titre($titre, $morehtmlright= '', $picto= 'generic', $pictoisfullpath=0, $id= '', $morecssontable= '', $morehtmlcenter= '')
Load a title with picto.
dol_strlen($string, $stringencoding= 'UTF-8')
Make a strlen call.
img_up($titlealt= 'default', $selected=0, $moreclass= '')
Show top arrow logo.
dolCheckFilters($sqlfilters, &$error= '')
Return if a $sqlfilters parameter is valid and will pass the preg_replace_callback() to replace Gener...
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
Function to test if an entry is enabled or not.
img_searchclear($titlealt= 'default', $other= '')
Show search logo.
dol_size($size, $type= '')
Optimize a size for some browsers (phone, smarphone, ...)
img_printer($titlealt="default", $other= '')
Show printer logo.
dol_get_next_month($month, $year)
Return next month.
Definition: date.lib.php:489
dol_fiche_end($notab=0)
Show tab footer of a card.
img_picto_common($titlealt, $picto, $moreatt= '', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
dol_get_next_day($day, $month, $year)
Return next day.
Definition: date.lib.php:455
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart= '')
Return a path to have a the directory according to object where files are stored. ...
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array. ...
dol_getdate($timestamp, $fast=false, $forcetimezone= '')
Return an array with locale date info.
GETPOSTINT($paramname, $method=0)
Return value of a param into GET or POST supervariable.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
getBrowserInfo($user_agent)
Return information about user browser.
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...
Class to manage translations.
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access) ...
dol_sanitizeFileName($str, $newstr= '_', $unaccent=1)
Clean a string to use it as a file name.
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_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_user_country()
Return country code for current user.
utf8_check($str)
Check if a string is in UTF8.
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
img_right($titlealt= 'default', $selected=0, $moreatt= '')
Show right arrow logo.
img_next($titlealt= 'default', $moreatt= '')
Show next logo.
dolExplodeIntoArray($string, $delimiter= ';', $kv= '=')
Split a string with 2 keys into key array.
getElementProperties($element_type)
Get an array with properties of an element.
img_edit_add($titlealt= 'default', $other= '')
Show logo +.
print_fiche_titre($title, $mesg= '', $picto= 'generic', $pictoisfullpath=0, $id= '')
Show a title with picto.
if(!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition: main.inc.php:61
img_edit_remove($titlealt= 'default', $other= '')
Show logo -.
img_allow($allow, $titlealt= 'default')
Show tick logo if allowed.
dolGetFirstLineOfText($text, $nboflines=1, $charset= 'UTF-8')
Return first line of text.
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...
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
dol_get_fiche_head($links=array(), $active= '', $title= '', $notab=0, $picto= '', $pictoisfullpath=0, $morehtmlright= '', $morecss= '', $limittoshow=0, $moretabssuffix= '')
Show tabs of a record.
dol_htmloutput_mesg($mesgstring= '', $mesgarray=array(), $style= 'ok', $keepembedded=0)
Print formated messages to output (Used to show messages on html output).
div float
Buy price without taxes.
Definition: style.css.php:809
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formated size.
dol_print_date($time, $format= '', $tzoutput= 'auto', $outputlangs= '', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
img_previous($titlealt= 'default', $moreatt= '')
Show previous logo.
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...
print_fleche_navigation($page, $file, $options= '', $nextpage=0, $betweenarrows= '', $afterarrows= '', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows= '')
Function to show navigation arrows into lists.
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
isHTTPS()
Return if we are using a HTTPS connexion Check HTTPS (no way to be modified by user but may be empty ...
startSimpleTable($header, $link="", $arguments="", $emptyRows=0, $number=-1)
Start a table with headers and a optinal clickable number (don&#39;t forget to use &quot;finishSimpleTable()&quot; ...
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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...
dolGetBadge($label, $html= '', $type= 'primary', $mode= '', $url= '', $params=array())
Function dolGetBadge.
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
img_info($titlealt= 'default')
Show info logo.
get_localtax_by_third($local)
Get values of localtaxes (1 or 2) for company country for the common vat with the highest value...
newToken()
Return the value of token currently saved into session with name &#39;newtoken&#39;.
dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto= 'UTF-8')
This function is called to decode a HTML string (it decodes entities and br tags) ...
dol_get_fiche_end($notab=0)
Return tab footer of a card.
print_titre($title)
Show a title.
dolForgeCriteriaCallback($matches)
Function to forge a SQL criteria from a Generic filter string.
isModEnabled($module)
Is Dolibarr module enabled.
dol_trunc($string, $size=40, $trunc= 'right', $stringencoding= 'UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;…&#39; if string larger than length. ...
print_date_range($date_start, $date_end, $format= '', $outputlangs= '')
Format output for start and end date.
dolGetStatus($statusLabel= '', $statusLabelShort= '', $html= '', $statusType= 'status0', $displayMode=0, $url= '', $params=array())
Output the badge of a status.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=&gt;newva...
Class to manage invoices.
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip= '', $forcenowrapcolumntitle=0)
Get title line of an array.
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.
dol_htmloutput_errors($mesgstring= '', $mesgarray=array(), $keepembedded=0)
Print formated error messages to output (Used to show messages on html output).
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
getFieldErrorIcon($fieldValidationErrorMsg)
get field error icon
fetchObjectByElement($element_id, $element_type, $element_ref= '')
Fetch an object from its id and element_type Inclusion of classes is automatic.
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode= 'add')
Complete or removed entries into a head array (used to build tabs).
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:643
colorIsLight($stringcolor)
Return true if the color is light.
img_delete($titlealt= 'default', $other= 'class="pictodelete"', $morecss= '')
Show delete logo.
img_split($titlealt= 'default', $other= 'class="pictosplit"')
Show split logo.
picto_from_langcode($codelang, $moreatt= '', $notitlealt=0)
Return img flag of country for a language code or country code.
currentToken()
Return the value of token currently saved into session with name &#39;token&#39;.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param= '')
Return URL we can use for advanced preview links.
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl= '')
Format address string.
dol_set_focus($selector)
Set focus onto field with selector (similar behaviour of &#39;autofocus&#39; HTML5 tag)
sanitizeVal($out= '', $check= 'alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_print_profids($profID, $profIDtype, $countrycode= '', $addcpButton=1, $separ= '&nbsp;')
Format profIDs according to country.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
Class to manage predefined suppliers products.
ascii_check($str)
Check if a string is in ASCII.
isValidPhone($phone)
Return true if phone number syntax is ok TODO Decide what to do with this.
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
roundUpToNextMultiple($n, $x=5)
Round to next multiple.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
dol_bc($var, $moreclass= '')
Return string to add class property on html element with pair/impair.
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 &quot;subst...
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...
Classe to manage GeoIP Usage: $geoip=new GeoIP(&#39;country&#39;,$datfile); $geoip-&gt;getCountryCodeFromIP($ip)...
dol_textishtml($msg, $option=0)
Return if a text is a html content.
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid