dolibarr  x.y.z
functions.lib.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4  * Copyright (C) 2004-2022 Laurent Destailleur <eldy@users.sourceforge.net>
5  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7  * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8  * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9  * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10  * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11  * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12  * Copyright (C) 2013-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
13  * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15  * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16  * Copyright (C) 2018-2022 Frédéric France <frederic.france@netlogic.fr>
17  * Copyright (C) 2019-2022 Thibault Foucart <support@ptibogxiv.net>
18  * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19  * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20  * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21  * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22  * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
23  *
24  * This program is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License as published by
26  * the Free Software Foundation; either version 3 of the License, or
27  * (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program. If not, see <https://www.gnu.org/licenses/>.
36  * or see https://www.gnu.org/
37  */
38 
45 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
46 
47 
48 if (!function_exists('utf8_encode')) {
55  function utf8_encode($elements)
56  {
57  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
58  }
59 }
60 
61 if (!function_exists('utf8_decode')) {
68  function utf8_decode($elements)
69  {
70  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
71  }
72 }
73 
74 
82 function getDolGlobalString($key, $default = '')
83 {
84  global $conf;
85  // return $conf->global->$key ?? $default;
86  return (string) (empty($conf->global->$key) ? $default : $conf->global->$key);
87 }
88 
96 function getDolGlobalInt($key, $default = 0)
97 {
98  global $conf;
99  // return $conf->global->$key ?? $default;
100  return (int) (empty($conf->global->$key) ? $default : $conf->global->$key);
101 }
102 
110 function getDolUserString($key, $default = '')
111 {
112  global $user;
113  // return $conf->global->$key ?? $default;
114  return (string) (empty($user->conf->$key) ? $default : $user->conf->$key);
115 }
116 
124 function getDolUserInt($key, $default = 0)
125 {
126  global $user;
127  // return $conf->global->$key ?? $default;
128  return (int) (empty($user->conf->$key) ? $default : $user->conf->$key);
129 }
130 
137 function isModEnabled($module)
138 {
139  global $conf;
140  return !empty($conf->$module->enabled);
141 }
142 
154 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
155 {
156  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
157 
158  $class = 'DoliDB'.ucfirst($type);
159  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
160  return $dolidb;
161 }
162 
180 function getEntity($element, $shared = 1, $currentobject = null)
181 {
182  global $conf, $mc, $hookmanager, $object, $action, $db;
183 
184  if (! is_object($hookmanager)) {
185  $hookmanager = new HookManager($db);
186  }
187 
188  // fix different element names (France to English)
189  switch ($element) {
190  case 'contrat':
191  $element = 'contract';
192  break; // "/contrat/class/contrat.class.php"
193  case 'order_supplier':
194  $element = 'supplier_order';
195  break; // "/fourn/class/fournisseur.commande.class.php"
196  case 'invoice_supplier':
197  $element = 'supplier_invoice';
198  break; // "/fourn/class/fournisseur.facture.class.php"
199  }
200 
201  if (is_object($mc)) {
202  $out = $mc->getEntity($element, $shared, $currentobject);
203  } else {
204  $out = '';
205  $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
206  if (in_array($element, $addzero)) {
207  $out .= '0,';
208  }
209  $out .= ((int) $conf->entity);
210  }
211 
212  // Manipulate entities to query on the fly
213  $parameters = array(
214  'element' => $element,
215  'shared' => $shared,
216  'object' => $object,
217  'currentobject' => $currentobject,
218  'out' => $out
219  );
220  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
221 
222  if (is_numeric($reshook)) {
223  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
224  $out .= ','.$hookmanager->resPrint; // add
225  } elseif ($reshook == 1) {
226  $out = $hookmanager->resPrint; // replace
227  }
228  }
229 
230  return $out;
231 }
232 
239 function setEntity($currentobject)
240 {
241  global $conf, $mc;
242 
243  if (is_object($mc) && method_exists($mc, 'setEntity')) {
244  return $mc->setEntity($currentobject);
245  } else {
246  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
247  }
248 }
249 
256 function isASecretKey($keyname)
257 {
258  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
259 }
260 
261 
268 function num2Alpha($n)
269 {
270  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
271  $r = chr($n % 26 + 0x41) . $r;
272  return $r;
273 }
274 
275 
292 function getBrowserInfo($user_agent)
293 {
294  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
295 
296  $name = 'unknown';
297  $version = '';
298  $os = 'unknown';
299  $phone = '';
300 
301  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
302 
303  $detectmobile = new Mobile_Detect(null, $user_agent);
304  $tablet = $detectmobile->isTablet();
305 
306  if ($detectmobile->isMobile()) {
307  $phone = 'unknown';
308 
309  // If phone/smartphone, we set phone os name.
310  if ($detectmobile->is('AndroidOS')) {
311  $os = $phone = 'android';
312  } elseif ($detectmobile->is('BlackBerryOS')) {
313  $os = $phone = 'blackberry';
314  } elseif ($detectmobile->is('iOS')) {
315  $os = 'ios';
316  $phone = 'iphone';
317  } elseif ($detectmobile->is('PalmOS')) {
318  $os = $phone = 'palm';
319  } elseif ($detectmobile->is('SymbianOS')) {
320  $os = 'symbian';
321  } elseif ($detectmobile->is('webOS')) {
322  $os = 'webos';
323  } elseif ($detectmobile->is('MaemoOS')) {
324  $os = 'maemo';
325  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
326  $os = 'windows';
327  }
328  }
329 
330  // OS
331  if (preg_match('/linux/i', $user_agent)) {
332  $os = 'linux';
333  } elseif (preg_match('/macintosh/i', $user_agent)) {
334  $os = 'macintosh';
335  } elseif (preg_match('/windows/i', $user_agent)) {
336  $os = 'windows';
337  }
338 
339  // Name
340  $reg = array();
341  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
342  $name = 'firefox';
343  $version = empty($reg[2]) ? '' : $reg[2];
344  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
345  $name = 'edge';
346  $version = empty($reg[2]) ? '' : $reg[2];
347  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
348  $name = 'chrome';
349  $version = empty($reg[2]) ? '' : $reg[2];
350  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
351  // we can have 'chrome (Mozilla...) chrome x.y' in one string
352  $name = 'chrome';
353  } elseif (preg_match('/iceweasel/i', $user_agent)) {
354  $name = 'iceweasel';
355  } elseif (preg_match('/epiphany/i', $user_agent)) {
356  $name = 'epiphany';
357  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
358  $name = 'safari';
359  $version = empty($reg[2]) ? '' : $reg[2];
360  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
361  // Safari is often present in string for mobile but its not.
362  $name = 'opera';
363  $version = empty($reg[2]) ? '' : $reg[2];
364  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
365  $name = 'ie';
366  $version = end($reg);
367  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
368  // MS products at end
369  $name = 'ie';
370  $version = end($reg);
371  } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
372  // MS products at end
373  $name = 'lynxlinks';
374  $version = empty($reg[3]) ? '' : $reg[3];
375  }
376 
377  if ($tablet) {
378  $layout = 'tablet';
379  } elseif ($phone) {
380  $layout = 'phone';
381  } else {
382  $layout = 'classic';
383  }
384 
385  return array(
386  'browsername' => $name,
387  'browserversion' => $version,
388  'browseros' => $os,
389  'layout' => $layout,
390  'phone' => $phone,
391  'tablet' => $tablet
392  );
393 }
394 
400 function dol_shutdown()
401 {
402  global $conf, $user, $langs, $db;
403  $disconnectdone = false;
404  $depth = 0;
405  if (is_object($db) && !empty($db->connected)) {
406  $depth = $db->transaction_opened;
407  $disconnectdone = $db->close();
408  }
409  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
410 }
411 
418 function GETPOSTISSET($paramname)
419 {
420  $isset = false;
421 
422  $relativepathstring = $_SERVER["PHP_SELF"];
423  // Clean $relativepathstring
424  if (constant('DOL_URL_ROOT')) {
425  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
426  }
427  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
428  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
429  //var_dump($relativepathstring);
430  //var_dump($user->default_values);
431 
432  // Code for search criteria persistence.
433  // Retrieve values if restore_lastsearch_values
434  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
435  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
436  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
437  if (is_array($tmp)) {
438  foreach ($tmp as $key => $val) {
439  if ($key == $paramname) { // We are on the requested parameter
440  $isset = true;
441  break;
442  }
443  }
444  }
445  }
446  // If there is saved contextpage, limit, page or mode
447  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
448  $isset = true;
449  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
450  $isset = true;
451  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
452  $isset = true;
453  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
454  $isset = true;
455  }
456  } else {
457  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
458  }
459 
460  return $isset;
461 }
462 
471 function GETPOSTISARRAY($paramname, $method = 0)
472 {
473  // for $method test need return the same $val as GETPOST
474  if (empty($method)) {
475  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
476  } elseif ($method == 1) {
477  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
478  } elseif ($method == 2) {
479  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
480  } elseif ($method == 3) {
481  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
482  } else {
483  $val = 'BadFirstParameterForGETPOST';
484  }
485 
486  return is_array($val);
487 }
488 
517 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
518 {
519  global $mysoc, $user, $conf;
520 
521  if (empty($paramname)) {
522  return 'BadFirstParameterForGETPOST';
523  }
524  if (empty($check)) {
525  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
526  // Enable this line to know who call the GETPOST with '' $check parameter.
527  //var_dump(debug_backtrace()[0]);
528  }
529 
530  if (empty($method)) {
531  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
532  } elseif ($method == 1) {
533  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
534  } elseif ($method == 2) {
535  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
536  } elseif ($method == 3) {
537  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
538  } else {
539  return 'BadThirdParameterForGETPOST';
540  }
541 
542  if (empty($method) || $method == 3 || $method == 4) {
543  $relativepathstring = $_SERVER["PHP_SELF"];
544  // Clean $relativepathstring
545  if (constant('DOL_URL_ROOT')) {
546  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
547  }
548  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
549  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
550  //var_dump($relativepathstring);
551  //var_dump($user->default_values);
552 
553  // Code for search criteria persistence.
554  // Retrieve values if restore_lastsearch_values
555  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
556  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
557  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
558  if (is_array($tmp)) {
559  foreach ($tmp as $key => $val) {
560  if ($key == $paramname) { // We are on the requested parameter
561  $out = $val;
562  break;
563  }
564  }
565  }
566  }
567  // If there is saved contextpage, page or limit
568  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
569  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
570  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
571  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
572  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
573  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
574  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
575  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
576  }
577  } elseif (!isset($_GET['sortfield'])) {
578  // Else, retrieve default values if we are not doing a sort
579  // 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
580  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
581  // Search default value from $object->field
582  global $object;
583  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
584  $out = $object->fields[$paramname]['default'];
585  }
586  }
587  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
588  if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
589  // Now search in setup to overwrite default values
590  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
591  if (isset($user->default_values[$relativepathstring]['createform'])) {
592  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
593  $qualified = 0;
594  if ($defkey != '_noquery_') {
595  $tmpqueryarraytohave = explode('&', $defkey);
596  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
597  $foundintru = 0;
598  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
599  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
600  $foundintru = 1;
601  }
602  }
603  if (!$foundintru) {
604  $qualified = 1;
605  }
606  //var_dump($defkey.'-'.$qualified);
607  } else {
608  $qualified = 1;
609  }
610 
611  if ($qualified) {
612  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
613  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
614  break;
615  }
616  }
617  }
618  }
619  }
620  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
621  // Management of default search_filters and sort order
622  if (!empty($user->default_values)) {
623  // $user->default_values defined from menu 'Setup - Default values'
624  //var_dump($user->default_values[$relativepathstring]);
625  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
626  // Sorted on which fields ? ASC or DESC ?
627  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
628  // Even if paramname is sortfield, data are stored into ['sortorder...']
629  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
630  $qualified = 0;
631  if ($defkey != '_noquery_') {
632  $tmpqueryarraytohave = explode('&', $defkey);
633  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
634  $foundintru = 0;
635  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
636  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
637  $foundintru = 1;
638  }
639  }
640  if (!$foundintru) {
641  $qualified = 1;
642  }
643  //var_dump($defkey.'-'.$qualified);
644  } else {
645  $qualified = 1;
646  }
647 
648  if ($qualified) {
649  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
650  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
651  if ($out) {
652  $out .= ', ';
653  }
654  if ($paramname == 'sortfield') {
655  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
656  }
657  if ($paramname == 'sortorder') {
658  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
659  }
660  }
661  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
662  }
663  }
664  }
665  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
666  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
667  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
668  continue;
669  }
670  $qualified = 0;
671  if ($defkey != '_noquery_') {
672  $tmpqueryarraytohave = explode('&', $defkey);
673  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
674  $foundintru = 0;
675  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
676  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
677  $foundintru = 1;
678  }
679  }
680  if (!$foundintru) {
681  $qualified = 1;
682  }
683  //var_dump($defkey.'-'.$qualified);
684  } else {
685  $qualified = 1;
686  }
687 
688  if ($qualified) {
689  // We must keep $_POST and $_GET here
690  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
691  // We made a search from quick search menu, do we still use default filter ?
692  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
693  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
694  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
695  }
696  } else {
697  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
698  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
699  }
700  break;
701  }
702  }
703  }
704  }
705  }
706  }
707  }
708  }
709 
710  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
711  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
712  // 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.
713  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
714  $reg = array();
715  $maxloop = 20;
716  $loopnb = 0; // Protection against infinite loop
717  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.
718  $loopnb++;
719  $newout = '';
720 
721  if ($reg[1] == 'DAY') {
722  $tmp = dol_getdate(dol_now(), true);
723  $newout = $tmp['mday'];
724  } elseif ($reg[1] == 'MONTH') {
725  $tmp = dol_getdate(dol_now(), true);
726  $newout = $tmp['mon'];
727  } elseif ($reg[1] == 'YEAR') {
728  $tmp = dol_getdate(dol_now(), true);
729  $newout = $tmp['year'];
730  } elseif ($reg[1] == 'PREVIOUS_DAY') {
731  $tmp = dol_getdate(dol_now(), true);
732  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
733  $newout = $tmp2['day'];
734  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
735  $tmp = dol_getdate(dol_now(), true);
736  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
737  $newout = $tmp2['month'];
738  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
739  $tmp = dol_getdate(dol_now(), true);
740  $newout = ($tmp['year'] - 1);
741  } elseif ($reg[1] == 'NEXT_DAY') {
742  $tmp = dol_getdate(dol_now(), true);
743  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
744  $newout = $tmp2['day'];
745  } elseif ($reg[1] == 'NEXT_MONTH') {
746  $tmp = dol_getdate(dol_now(), true);
747  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
748  $newout = $tmp2['month'];
749  } elseif ($reg[1] == 'NEXT_YEAR') {
750  $tmp = dol_getdate(dol_now(), true);
751  $newout = ($tmp['year'] + 1);
752  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
753  $newout = $mysoc->country_id;
754  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
755  $newout = $user->id;
756  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
757  $newout = $user->fk_user;
758  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
759  $newout = $conf->entity;
760  } else {
761  $newout = ''; // Key not found, we replace with empty string
762  }
763  //var_dump('__'.$reg[1].'__ -> '.$newout);
764  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
765  }
766  }
767 
768  // Check rule
769  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
770  if (!is_array($out) || empty($out)) {
771  $out = array();
772  } else {
773  $tmparray = explode(':', $check);
774  if (!empty($tmparray[1])) {
775  $tmpcheck = $tmparray[1];
776  } else {
777  $tmpcheck = 'alphanohtml';
778  }
779  foreach ($out as $outkey => $outval) {
780  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
781  }
782  }
783  } else {
784  $out = sanitizeVal($out, $check, $filter, $options);
785  }
786 
787  // Sanitizing for special parameters.
788  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
789  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
790  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
791  $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.
792  do {
793  $oldstringtoclean = $out;
794  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
795  $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'
796  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
797  } while ($oldstringtoclean != $out);
798  }
799 
800  // Code for search criteria persistence.
801  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
802  if (empty($method) || $method == 3 || $method == 4) {
803  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
804  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
805 
806  // We save search key only if $out not empty that means:
807  // - posted value not empty, or
808  // - 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).
809 
810  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
811  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
812  }
813  }
814  }
815 
816  return $out;
817 }
818 
828 function GETPOSTINT($paramname, $method = 0)
829 {
830  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
831 }
832 
833 
844 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
845 {
846  return sanitizeVal($out, $check, $filter, $options);
847 }
848 
858 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
859 {
860  global $conf;
861 
862  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
863  // Check is done after replacement
864  switch ($check) {
865  case 'none':
866  break;
867  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
868  if (!is_numeric($out)) {
869  $out = '';
870  }
871  break;
872  case 'intcomma':
873  if (preg_match('/[^0-9,-]+/i', $out)) {
874  $out = '';
875  }
876  break;
877  case 'san_alpha':
878  $out = filter_var($out, FILTER_SANITIZE_STRING);
879  break;
880  case 'email':
881  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
882  break;
883  case 'aZ':
884  if (!is_array($out)) {
885  $out = trim($out);
886  if (preg_match('/[^a-z]+/i', $out)) {
887  $out = '';
888  }
889  }
890  break;
891  case 'aZ09':
892  if (!is_array($out)) {
893  $out = trim($out);
894  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
895  $out = '';
896  }
897  }
898  break;
899  case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
900  if (!is_array($out)) {
901  $out = trim($out);
902  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
903  $out = '';
904  }
905  }
906  break;
907  case 'nohtml': // No html
908  $out = dol_string_nohtmltag($out, 0);
909  break;
910  case 'alpha': // No html and no ../ and "
911  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
912  if (!is_array($out)) {
913  $out = trim($out);
914  do {
915  $oldstringtoclean = $out;
916  // Remove html tags
917  $out = dol_string_nohtmltag($out, 0);
918  // Remove also other dangerous string sequences
919  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
920  // '../' or '..\' is dangerous because it allows dir transversals
921  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
922  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
923  } while ($oldstringtoclean != $out);
924  // keep lines feed
925  }
926  break;
927  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'
928  if (!is_array($out)) {
929  $out = trim($out);
930  do {
931  $oldstringtoclean = $out;
932  // Remove html tags
933  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
934  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
935  // '../' or '..\' is dangerous because it allows dir transversals
936  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
937  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
938  } while ($oldstringtoclean != $out);
939  }
940  break;
941  case 'restricthtml': // Recommended for most html textarea
942  case 'restricthtmlallowunvalid':
943  $out = dol_htmlwithnojs($out, 1, $check);
944  break;
945  case 'custom':
946  if (!empty($out)) {
947  if (empty($filter)) {
948  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
949  }
950  /*if (empty($options)) {
951  return 'BadParameterForGETPOST - Param 4 of sanitizeVal()';
952  }*/
953  $out = filter_var($out, $filter, $options);
954  }
955  break;
956  }
957 
958  return $out;
959 }
960 
961 
962 if (!function_exists('dol_getprefix')) {
972  function dol_getprefix($mode = '')
973  {
974  // If prefix is for email (we need to have $conf already loaded for this case)
975  if ($mode == 'email') {
976  global $conf;
977 
978  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
979  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
980  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
981  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
982  return $_SERVER["SERVER_NAME"];
983  }
984  }
985 
986  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
987  if (!empty($conf->file->instance_unique_id)) {
988  return sha1('dolibarr'.$conf->file->instance_unique_id);
989  }
990 
991  // For backward compatibility when instance_unique_id is not set
992  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
993  }
994 
995  // If prefix is for session (no need to have $conf loaded)
996  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
997  $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
998 
999  // The recommended value (may be not defined for old versions)
1000  if (!empty($tmp_instance_unique_id)) {
1001  return sha1('dolibarr'.$tmp_instance_unique_id);
1002  }
1003 
1004  // For backward compatibility when instance_unique_id is not set
1005  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1006  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1007  } else {
1008  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1009  }
1010  }
1011 }
1012 
1023 function dol_include_once($relpath, $classname = '')
1024 {
1025  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']
1026 
1027  $fullpath = dol_buildpath($relpath);
1028 
1029  if (!file_exists($fullpath)) {
1030  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1031  return false;
1032  }
1033 
1034  if (!empty($classname) && !class_exists($classname)) {
1035  return include $fullpath;
1036  } else {
1037  return include_once $fullpath;
1038  }
1039 }
1040 
1041 
1052 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1053 {
1054  global $conf;
1055 
1056  $path = preg_replace('/^\//', '', $path);
1057 
1058  if (empty($type)) { // For a filesystem path
1059  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1060  if (is_array($conf->file->dol_document_root)) {
1061  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1062  if ($key == 'main') {
1063  continue;
1064  }
1065  if (file_exists($dirroot.'/'.$path)) {
1066  $res = $dirroot.'/'.$path;
1067  return $res;
1068  }
1069  }
1070  }
1071  if ($returnemptyifnotfound) {
1072  // Not found into alternate dir
1073  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1074  return '';
1075  }
1076  }
1077  } else {
1078  // For an url path
1079  // We try to get local path of file on filesystem from url
1080  // Note that trying to know if a file on disk exist by forging path on disk from url
1081  // works only for some web server and some setup. This is bugged when
1082  // using proxy, rewriting, virtual path, etc...
1083  $res = '';
1084  if ($type == 1) {
1085  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1086  }
1087  if ($type == 2) {
1088  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1089  }
1090  if ($type == 3) {
1091  $res = DOL_URL_ROOT.'/'.$path;
1092  }
1093 
1094  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1095  if ($key == 'main') {
1096  if ($type == 3) {
1097  global $dolibarr_main_url_root;
1098 
1099  // Define $urlwithroot
1100  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1101  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1102  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1103 
1104  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1105  }
1106  continue;
1107  }
1108  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1109  if (!empty($regs[1])) {
1110  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1111  if (file_exists($dirroot.'/'.$regs[1])) {
1112  if ($type == 1) {
1113  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1114  }
1115  if ($type == 2) {
1116  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1117  }
1118  if ($type == 3) {
1119  global $dolibarr_main_url_root;
1120 
1121  // Define $urlwithroot
1122  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1123  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1124  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1125 
1126  $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
1127  }
1128  break;
1129  }
1130  }
1131  }
1132  }
1133 
1134  return $res;
1135 }
1136 
1148 function dol_clone($object, $native = 0)
1149 {
1150  if ($native == 0) {
1151  // deprecated method, use the method with native = 2 instead
1152  $tmpsavdb = null;
1153  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1154  $tmpsavdb = $object->db;
1155  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1156  }
1157 
1158  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1159 
1160  if (!empty($tmpsavdb)) {
1161  $object->db = $tmpsavdb;
1162  }
1163  } elseif ($native == 2) {
1164  // recommended method to have a full isolated cloned object
1165  $myclone = new stdClass();
1166  $tmparray = get_object_vars($object); // return only public properties
1167 
1168  if (is_array($tmparray)) {
1169  foreach ($tmparray as $propertykey => $propertyval) {
1170  if (is_scalar($propertyval) || is_array($propertyval)) {
1171  $myclone->$propertykey = $propertyval;
1172  }
1173  }
1174  }
1175  } else {
1176  $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)
1177  }
1178 
1179  return $myclone;
1180 }
1181 
1191 function dol_size($size, $type = '')
1192 {
1193  global $conf;
1194  if (empty($conf->dol_optimize_smallscreen)) {
1195  return $size;
1196  }
1197  if ($type == 'width' && $size > 250) {
1198  return 250;
1199  } else {
1200  return 10;
1201  }
1202 }
1203 
1204 
1216 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1217 {
1218  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1219  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1220  // Char '/' and '\' are file delimiters.
1221  // 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
1222  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';');
1223  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1224  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1225  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1226  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1227  $tmp = str_replace('..', '', $tmp);
1228  return $tmp;
1229 }
1230 
1242 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1243 {
1244  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1245  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1246  // 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
1247  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';');
1248  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1249  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1250  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1251  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1252  $tmp = str_replace('..', '', $tmp);
1253  return $tmp;
1254 }
1255 
1263 function dol_sanitizeUrl($stringtoclean, $type = 1)
1264 {
1265  // 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)
1266  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1267  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1268  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1269  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1270 
1271  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1272  if ($type == 1) {
1273  // removing : should disable links to external url like http:aaa)
1274  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1275  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1276  }
1277 
1278  do {
1279  $oldstringtoclean = $stringtoclean;
1280  // removing '&colon' should disable links to external url like http:aaa)
1281  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1282  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1283  } while ($oldstringtoclean != $stringtoclean);
1284 
1285  if ($type == 1) {
1286  // removing '//' should disable links to external url like //aaa or http//)
1287  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1288  }
1289 
1290  return $stringtoclean;
1291 }
1292 
1299 function dol_sanitizeEmail($stringtoclean)
1300 {
1301  do {
1302  $oldstringtoclean = $stringtoclean;
1303  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1304  } while ($oldstringtoclean != $stringtoclean);
1305 
1306  return $stringtoclean;
1307 }
1308 
1317 function dol_string_unaccent($str)
1318 {
1319  global $conf;
1320 
1321  if (utf8_check($str)) {
1322  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1323  $transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
1324  return $transliterator->transliterate($str);
1325  }
1326  // See http://www.utf8-chartable.de/
1327  $string = rawurlencode($str);
1328  $replacements = array(
1329  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1330  '%C3%87' => 'C',
1331  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1332  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1333  '%C3%91' => 'N',
1334  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1335  '%C5%A0' => 'S',
1336  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1337  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1338  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1339  '%C3%A7' => 'c',
1340  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1341  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1342  '%C3%B1' => 'n',
1343  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1344  '%C5%A1' => 's',
1345  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1346  '%C3%BD' => 'y', '%C3%BF' => 'y'
1347  );
1348  $string = strtr($string, $replacements);
1349  return rawurldecode($string);
1350  } else {
1351  // See http://www.ascii-code.com/
1352  $string = strtr(
1353  $str,
1354  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1355  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1356  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1357  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1358  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1359  \xF9\xFA\xFB\xFC\xFD\xFF",
1360  "AAAAAAC
1361  EEEEIIIIDN
1362  OOOOOUUUY
1363  aaaaaaceeee
1364  iiiidnooooo
1365  uuuuyy"
1366  );
1367  $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"));
1368  return $string;
1369  }
1370 }
1371 
1384 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '')
1385 {
1386  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName
1387  $forbidden_chars_to_remove = array();
1388  //$forbidden_chars_to_remove=array("(",")");
1389 
1390  if (is_array($badcharstoreplace)) {
1391  $forbidden_chars_to_replace = $badcharstoreplace;
1392  }
1393  if (is_array($badcharstoremove)) {
1394  $forbidden_chars_to_remove = $badcharstoremove;
1395  }
1396 
1397  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1398 }
1399 
1400 
1414 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1415 {
1416  if ($removetabcrlf) {
1417  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1418  } else {
1419  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
1420  }
1421 }
1422 
1431 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1432 {
1433  // escape quotes and backslashes, newlines, etc.
1434  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1435  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1436  if (empty($noescapebackslashn)) {
1437  $substitjs["\n"] = '\\n';
1438  $substitjs['\\'] = '\\\\';
1439  }
1440  if (empty($mode)) {
1441  $substitjs["'"] = "\\'";
1442  $substitjs['"'] = "\\'";
1443  } elseif ($mode == 1) {
1444  $substitjs["'"] = "\\'";
1445  } elseif ($mode == 2) {
1446  $substitjs['"'] = '\\"';
1447  } elseif ($mode == 3) {
1448  $substitjs["'"] = "\\'";
1449  $substitjs['"'] = "\\\"";
1450  }
1451  return strtr($stringtoescape, $substitjs);
1452 }
1453 
1460 function dol_escape_json($stringtoescape)
1461 {
1462  return str_replace('"', '\"', $stringtoescape);
1463 }
1464 
1476 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0)
1477 {
1478  if ($noescapetags == 'common') {
1479  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1480  }
1481 
1482  // escape quotes and backslashes, newlines, etc.
1483  if ($escapeonlyhtmltags) {
1484  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1485  } else {
1486  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1487  }
1488  if (!$keepb) {
1489  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1490  }
1491  if (!$keepn) {
1492  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1493  }
1494 
1495  if ($escapeonlyhtmltags) {
1496  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1497  } else {
1498  // Escape tags to keep
1499  // TODO Does not works yet when there is attributes into tag
1500  $tmparrayoftags = array();
1501  if ($noescapetags) {
1502  $tmparrayoftags = explode(',', $noescapetags);
1503  }
1504  if (count($tmparrayoftags)) {
1505  foreach ($tmparrayoftags as $tagtoreplace) {
1506  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1507  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1508  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1509  }
1510  }
1511 
1512  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1513 
1514  if (count($tmparrayoftags)) {
1515  foreach ($tmparrayoftags as $tagtoreplace) {
1516  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1517  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1518  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1519  }
1520  }
1521 
1522  return $result;
1523  }
1524 }
1525 
1533 function dol_strtolower($string, $encoding = "UTF-8")
1534 {
1535  if (function_exists('mb_strtolower')) {
1536  return mb_strtolower($string, $encoding);
1537  } else {
1538  return strtolower($string);
1539  }
1540 }
1541 
1549 function dol_strtoupper($string, $encoding = "UTF-8")
1550 {
1551  if (function_exists('mb_strtoupper')) {
1552  return mb_strtoupper($string, $encoding);
1553  } else {
1554  return strtoupper($string);
1555  }
1556 }
1557 
1565 function dol_ucfirst($string, $encoding = "UTF-8")
1566 {
1567  if (function_exists('mb_substr')) {
1568  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1569  } else {
1570  return ucfirst($string);
1571  }
1572 }
1573 
1581 function dol_ucwords($string, $encoding = "UTF-8")
1582 {
1583  if (function_exists('mb_convert_case')) {
1584  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1585  } else {
1586  return ucwords($string);
1587  }
1588 }
1589 
1611 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1612 {
1613  global $conf, $user, $debugbar;
1614 
1615  // If syslog module enabled
1616  if (empty($conf->syslog->enabled)) {
1617  return;
1618  }
1619 
1620  // Check if we are into execution of code of a website
1621  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1622  global $website, $websitekey;
1623  if (is_object($website) && !empty($website->ref)) {
1624  $suffixinfilename .= '_website_'.$website->ref;
1625  } elseif (!empty($websitekey)) {
1626  $suffixinfilename .= '_website_'.$websitekey;
1627  }
1628  }
1629 
1630  // Check if we have a forced suffix
1631  if (defined('USESUFFIXINLOG')) {
1632  $suffixinfilename .= constant('USESUFFIXINLOG');
1633  }
1634 
1635  if ($ident < 0) {
1636  foreach ($conf->loghandlers as $loghandlerinstance) {
1637  $loghandlerinstance->setIdent($ident);
1638  }
1639  }
1640 
1641  if (!empty($message)) {
1642  // Test log level
1643  $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');
1644  if (!array_key_exists($level, $logLevels)) {
1645  throw new Exception('Incorrect log level');
1646  }
1647  if ($level > $conf->global->SYSLOG_LEVEL) {
1648  return;
1649  }
1650 
1651  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1652  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1653  }
1654 
1655  // If adding log inside HTML page is required
1656  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1657  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1658  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1659  }
1660 
1661  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1662  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1663  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1664  print "\n\n<!-- Log start\n";
1665  print dol_escape_htmltag($message)."\n";
1666  print "Log end -->\n";
1667  }
1668 
1669  $data = array(
1670  'message' => $message,
1671  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1672  'level' => $level,
1673  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1674  'ip' => false
1675  );
1676 
1677  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1678  if (!empty($remoteip)) {
1679  $data['ip'] = $remoteip;
1680  // This is when server run behind a reverse proxy
1681  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1682  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1683  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1684  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1685  }
1686  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1687  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1688  $data['ip'] = $_SERVER['SERVER_ADDR'];
1689  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1690  // 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).
1691  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1692  } elseif (!empty($_SERVER['LOGNAME'])) {
1693  // 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).
1694  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1695  }
1696 
1697  // Loop on each log handler and send output
1698  foreach ($conf->loghandlers as $loghandlerinstance) {
1699  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1700  continue;
1701  }
1702  $loghandlerinstance->export($data, $suffixinfilename);
1703  }
1704  unset($data);
1705  }
1706 
1707  if ($ident > 0) {
1708  foreach ($conf->loghandlers as $loghandlerinstance) {
1709  $loghandlerinstance->setIdent($ident);
1710  }
1711  }
1712 }
1713 
1728 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'button bordertransp', $backtopagejsfields = '')
1729 {
1730  global $conf;
1731 
1732  if (strpos($url, '?') > 0) {
1733  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1734  } else {
1735  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1736  }
1737 
1738  $out = '';
1739 
1740  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1741  if ($backtopagejsfields) {
1742  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1743  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1744  $backtopagejsfields = $name.":".$backtopagejsfields;
1745  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1746  } else {
1747  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1748  }
1749  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1750  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1751  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1752  }
1753 
1754  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1755  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
1756  $out .= '<a class="cursorpointer classlink button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
1757  if (empty($conf->use_javascript_ajax)) {
1758  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
1759  }
1760  $out .= '>'.$buttonstring.'</a>';
1761 
1762  if (!empty($conf->use_javascript_ajax)) {
1763  // Add code to open url using the popup. Add also hidden field to retreive the returned variables
1764  $out .= '<!-- code to open popup and variables to retreive returned variables -->';
1765  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1766  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1767  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1768  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1769  $out .= '<script type="text/javascript">
1770  jQuery(document).ready(function () {
1771  jQuery(".button_'.$name.'").click(function () {
1772  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1773  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1774  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1775  $tmpdialog.dialog({
1776  autoOpen: false,
1777  modal: true,
1778  height: (window.innerHeight - 150),
1779  width: \'80%\',
1780  title: \''.dol_escape_js($label).'\',
1781  open: function (event, ui) {
1782  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1783  },
1784  close: function (event, ui) {
1785  returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1786  returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1787  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1788  if (returnedid != "" && returnedid != "div for returned id") {
1789  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1790  }
1791  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1792  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1793  }
1794  }
1795  });
1796 
1797  $tmpdialog.dialog(\'open\');
1798  });
1799  });
1800  </script>';
1801  }
1802  return $out;
1803 }
1804 
1821 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1822 {
1823  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1824 }
1825 
1841 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1842 {
1843  global $conf, $langs, $hookmanager;
1844 
1845  // Show title
1846  $showtitle = 1;
1847  if (!empty($conf->dol_optimize_smallscreen)) {
1848  $showtitle = 0;
1849  }
1850 
1851  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1852 
1853  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1854  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1855  }
1856 
1857  // Show right part
1858  if ($morehtmlright) {
1859  $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.
1860  }
1861 
1862  // Show title
1863  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1864  $limittitle = 30;
1865  $out .= '<a class="tabTitle">';
1866  if ($picto) {
1867  $noprefix = $pictoisfullpath;
1868  if (strpos($picto, 'fontawesome_') !== false) {
1869  $noprefix = 1;
1870  }
1871  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
1872  }
1873  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
1874  $out .= '</a>';
1875  }
1876 
1877  // Show tabs
1878 
1879  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1880  $maxkey = -1;
1881  if (is_array($links) && !empty($links)) {
1882  $keys = array_keys($links);
1883  if (count($keys)) {
1884  $maxkey = max($keys);
1885  }
1886  }
1887 
1888  // Show tabs
1889  // if =0 we don't use the feature
1890  if (empty($limittoshow)) {
1891  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
1892  }
1893  if (!empty($conf->dol_optimize_smallscreen)) {
1894  $limittoshow = 2;
1895  }
1896 
1897  $displaytab = 0;
1898  $nbintab = 0;
1899  $popuptab = 0;
1900  $outmore = '';
1901  for ($i = 0; $i <= $maxkey; $i++) {
1902  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1903  // If active tab is already present
1904  if ($i >= $limittoshow) {
1905  $limittoshow--;
1906  }
1907  }
1908  }
1909 
1910  for ($i = 0; $i <= $maxkey; $i++) {
1911  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1912  $isactive = true;
1913  } else {
1914  $isactive = false;
1915  }
1916 
1917  if ($i < $limittoshow || $isactive) {
1918  // Output entry with a visible tab
1919  $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])).' -->';
1920 
1921  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1922  if (!empty($links[$i][0])) {
1923  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1924  } else {
1925  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1926  }
1927  } elseif (!empty($links[$i][1])) {
1928  //print "x $i $active ".$links[$i][2]." z";
1929  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
1930  if (!empty($links[$i][0])) {
1931  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
1932  $out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tab inline-block valignmiddle'.($morecss ? ' '.$morecss : '').(!empty($links[$i][5]) ? ' '.$links[$i][5] : '').'" href="'.$links[$i][0].'" title="'.dol_escape_htmltag($titletoshow).'">';
1933  }
1934  $out .= $links[$i][1];
1935  if (!empty($links[$i][0])) {
1936  $out .= '</a>'."\n";
1937  }
1938  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
1939  $out .= '</div>';
1940  }
1941 
1942  $out .= '</div>';
1943  } else {
1944  // Add entry into the combo popup with the other tabs
1945  if (!$popuptab) {
1946  $popuptab = 1;
1947  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1948  }
1949  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
1950  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1951  if (!empty($links[$i][0])) {
1952  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1953  } else {
1954  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1955  }
1956  } elseif (!empty($links[$i][1])) {
1957  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1958  $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.
1959  $outmore .= '</a>'."\n";
1960  }
1961  $outmore .= '</div>';
1962 
1963  $nbintab++;
1964  }
1965  $displaytab = $i;
1966  }
1967  if ($popuptab) {
1968  $outmore .= '</div>';
1969  }
1970 
1971  if ($popuptab) { // If there is some tabs not shown
1972  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
1973  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
1974  $widthofpopup = 200;
1975 
1976  $tabsname = $moretabssuffix;
1977  if (empty($tabsname)) {
1978  $tabsname = str_replace("@", "", $picto);
1979  }
1980  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
1981  $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".
1982  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
1983  $out .= $outmore;
1984  $out .= '</div>';
1985  $out .= '<div></div>';
1986  $out .= "</div>\n";
1987 
1988  $out .= "<script>";
1989  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
1990  var x = this.offsetLeft, y = this.offsetTop;
1991  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
1992  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
1993  $('#moretabsList".$tabsname."').css('".$right."','8px');
1994  }
1995  $('#moretabsList".$tabsname."').css('".$left."','auto');
1996  });
1997  ";
1998  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
1999  $out .= "</script>";
2000  }
2001 
2002  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2003  $out .= "</div>\n";
2004  }
2005 
2006  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2007  $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom'))).'">'."\n";
2008  }
2009 
2010  $parameters = array('tabname' => $active, 'out' => $out);
2011  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2012  if ($reshook > 0) {
2013  $out = $hookmanager->resPrint;
2014  }
2015 
2016  return $out;
2017 }
2018 
2026 function dol_fiche_end($notab = 0)
2027 {
2028  print dol_get_fiche_end($notab);
2029 }
2030 
2037 function dol_get_fiche_end($notab = 0)
2038 {
2039  if (!$notab || $notab == -1) {
2040  return "\n</div>\n";
2041  } else {
2042  return '';
2043  }
2044 }
2045 
2065 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2066 {
2067  global $conf, $form, $user, $langs, $hookmanager, $action;
2068 
2069  $error = 0;
2070 
2071  $maxvisiblephotos = 1;
2072  $showimage = 1;
2073  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2074  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2075  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2076  $showbarcode = 0;
2077  }
2078  $modulepart = 'unknown';
2079 
2080  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2081  $modulepart = $object->element;
2082  } elseif ($object->element == 'member') {
2083  $modulepart = 'memberphoto';
2084  } elseif ($object->element == 'user') {
2085  $modulepart = 'userphoto';
2086  }
2087 
2088  if (class_exists("Imagick")) {
2089  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2090  $modulepart = $object->element;
2091  } elseif ($object->element == 'fichinter') {
2092  $modulepart = 'ficheinter';
2093  } elseif ($object->element == 'contrat') {
2094  $modulepart = 'contract';
2095  } elseif ($object->element == 'order_supplier') {
2096  $modulepart = 'supplier_order';
2097  } elseif ($object->element == 'invoice_supplier') {
2098  $modulepart = 'supplier_invoice';
2099  }
2100  }
2101 
2102  if ($object->element == 'product') {
2103  $width = 80;
2104  $cssclass = 'photowithmargin photoref';
2105  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2106  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2107  if ($conf->browser->layout == 'phone') {
2108  $maxvisiblephotos = 1;
2109  }
2110  if ($showimage) {
2111  $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>';
2112  } else {
2113  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2114  $nophoto = '';
2115  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2116  } else { // Show no photo link
2117  $nophoto = '/public/theme/common/nophoto.png';
2118  $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>';
2119  }
2120  }
2121  } elseif ($object->element == 'ticket') {
2122  $width = 80;
2123  $cssclass = 'photoref';
2124  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2125  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2126  if ($conf->browser->layout == 'phone') {
2127  $maxvisiblephotos = 1;
2128  }
2129 
2130  if ($showimage) {
2131  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2132  if ($object->nbphoto > 0) {
2133  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2134  } else {
2135  $showimage = 0;
2136  }
2137  }
2138  if (!$showimage) {
2139  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2140  $nophoto = '';
2141  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2142  } else { // Show no photo link
2143  $nophoto = img_picto('No photo', 'object_ticket');
2144  $morehtmlleft .= '<!-- No photo to show -->';
2145  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2146  $morehtmlleft .= $nophoto;
2147  $morehtmlleft .= '</div></div>';
2148  }
2149  }
2150  } else {
2151  if ($showimage) {
2152  if ($modulepart != 'unknown') {
2153  $phototoshow = '';
2154  // Check if a preview file is available
2155  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2156  $objectref = dol_sanitizeFileName($object->ref);
2157  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2158  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2159  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2160  $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
2161  } else {
2162  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2163  }
2164  if (empty($subdir)) {
2165  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2166  }
2167 
2168  $filepath = $dir_output.$subdir."/";
2169 
2170  $filepdf = $filepath.$objectref.".pdf";
2171  $relativepath = $subdir.'/'.$objectref.'.pdf';
2172 
2173  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2174  $fileimage = $filepdf.'_preview.png';
2175  $relativepathimage = $relativepath.'_preview.png';
2176 
2177  $pdfexists = file_exists($filepdf);
2178 
2179  // If PDF file exists
2180  if ($pdfexists) {
2181  // Conversion du PDF en image png si fichier png non existant
2182  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2183  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2184  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2185  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2186  if ($ret < 0) {
2187  $error++;
2188  }
2189  }
2190  }
2191  }
2192 
2193  if ($pdfexists && !$error) {
2194  $heightforphotref = 80;
2195  if (!empty($conf->dol_optimize_smallscreen)) {
2196  $heightforphotref = 60;
2197  }
2198  // If the preview file is found
2199  if (file_exists($fileimage)) {
2200  $phototoshow = '<div class="photoref">';
2201  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2202  $phototoshow .= '</div>';
2203  }
2204  }
2205  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2206  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2207  }
2208 
2209  if ($phototoshow) {
2210  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2211  $morehtmlleft .= $phototoshow;
2212  $morehtmlleft .= '</div>';
2213  }
2214  }
2215 
2216  if (empty($phototoshow)) { // Show No photo link (picto of object)
2217  if ($object->element == 'action') {
2218  $width = 80;
2219  $cssclass = 'photorefcenter';
2220  $nophoto = img_picto('No photo', 'title_agenda');
2221  } else {
2222  $width = 14;
2223  $cssclass = 'photorefcenter';
2224  $picto = $object->picto;
2225  $prefix = 'object_';
2226  if ($object->element == 'project' && !$object->public) {
2227  $picto = 'project'; // instead of projectpub
2228  }
2229  if (strpos($picto, 'fontawesome_') !== false) {
2230  $prefix = '';
2231  }
2232  $nophoto = img_picto('No photo', $prefix.$picto);
2233  }
2234  $morehtmlleft .= '<!-- No photo to show -->';
2235  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2236  $morehtmlleft .= $nophoto;
2237  $morehtmlleft .= '</div></div>';
2238  }
2239  }
2240  }
2241 
2242  if ($showbarcode) {
2243  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2244  }
2245 
2246  if ($object->element == 'societe') {
2247  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2248  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2249  } else {
2250  $morehtmlstatus .= $object->getLibStatut(6);
2251  }
2252  } elseif ($object->element == 'product') {
2253  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2254  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2255  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2256  } else {
2257  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2258  }
2259  $morehtmlstatus .= ' &nbsp; ';
2260  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2261  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2262  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2263  } else {
2264  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2265  }
2266  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2267  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2268  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2269  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2270  }
2271  $morehtmlstatus .= $tmptxt;
2272  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2273  if ($object->statut == 0) {
2274  $morehtmlstatus .= $object->getLibStatut(5);
2275  } else {
2276  $morehtmlstatus .= $object->getLibStatut(4);
2277  }
2278  } elseif ($object->element == 'facturerec') {
2279  if ($object->frequency == 0) {
2280  $morehtmlstatus .= $object->getLibStatut(2);
2281  } else {
2282  $morehtmlstatus .= $object->getLibStatut(5);
2283  }
2284  } elseif ($object->element == 'project_task') {
2285  $object->fk_statut = 1;
2286  if ($object->progress > 0) {
2287  $object->fk_statut = 2;
2288  }
2289  if ($object->progress >= 100) {
2290  $object->fk_statut = 3;
2291  }
2292  $tmptxt = $object->getLibStatut(5);
2293  $morehtmlstatus .= $tmptxt; // No status on task
2294  } else { // Generic case
2295  if (isset($object->status)) {
2296  $tmptxt = $object->getLibStatut(6);
2297  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2298  $tmptxt = $object->getLibStatut(5);
2299  }
2300  $morehtmlstatus .= $tmptxt;
2301  }
2302  }
2303 
2304  // Add if object was dispatched "into accountancy"
2305  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2306  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2307  if (method_exists($object, 'getVentilExportCompta')) {
2308  $accounted = $object->getVentilExportCompta();
2309  $langs->load("accountancy");
2310  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2311  }
2312  }
2313 
2314  // Add alias for thirdparty
2315  if (!empty($object->name_alias)) {
2316  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->name_alias.'</div>';
2317  }
2318 
2319  // Add label
2320  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2321  if (!empty($object->label)) {
2322  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2323  }
2324  }
2325 
2326  // Show address and email
2327  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2328  $moreaddress = $object->getBannerAddress('refaddress', $object);
2329  if ($moreaddress) {
2330  $morehtmlref .= '<div class="refidno">';
2331  $morehtmlref .= $moreaddress;
2332  $morehtmlref .= '</div>';
2333  }
2334  }
2335  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)) {
2336  $morehtmlref .= '<div style="clear: both;"></div>';
2337  $morehtmlref .= '<div class="refidno opacitymedium">';
2338  $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id;
2339  $morehtmlref .= '</div>';
2340  }
2341 
2342  $parameters=array('morehtmlref'=>$morehtmlref);
2343  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2344  if ($reshook < 0) {
2345  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2346  } elseif (empty($reshook)) {
2347  $morehtmlref .= $hookmanager->resPrint;
2348  } elseif ($reshook > 0) {
2349  $morehtmlref = $hookmanager->resPrint;
2350  }
2351 
2352 
2353  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2354  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2355  print '</div>';
2356  print '<div class="underrefbanner clearboth"></div>';
2357 }
2358 
2368 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2369 {
2370  global $langs;
2371  $ret = '';
2372  if ($fieldrequired) {
2373  $ret .= '<span class="fieldrequired">';
2374  }
2375  $ret .= '<label for="'.$fieldkey.'">';
2376  $ret .= $langs->trans($langkey);
2377  $ret .= '</label>';
2378  if ($fieldrequired) {
2379  $ret .= '</span>';
2380  }
2381  return $ret;
2382 }
2383 
2391 function dol_bc($var, $moreclass = '')
2392 {
2393  global $bc;
2394  $ret = ' '.$bc[$var];
2395  if ($moreclass) {
2396  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2397  }
2398  return $ret;
2399 }
2400 
2414 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2415 {
2416  global $conf, $langs, $hookmanager;
2417 
2418  $ret = '';
2419  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2420 
2421  // See format of addresses on https://en.wikipedia.org/wiki/Address
2422  // Address
2423  if (empty($mode)) {
2424  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address));
2425  }
2426  // Zip/Town/State
2427  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2428  // US: title firstname name \n address lines \n town, state, zip \n country
2429  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2430  $ret .= (($ret && $town) ? $sep : '').$town;
2431 
2432  if (!empty($object->state)) {
2433  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2434  }
2435  if (!empty($object->zip)) {
2436  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2437  }
2438  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2439  // UK: title firstname name \n address lines \n town state \n zip \n country
2440  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2441  $ret .= ($ret ? $sep : '').$town;
2442  if (!empty($object->state)) {
2443  $ret .= ($ret ? ", " : '').$object->state;
2444  }
2445  if (!empty($object->zip)) {
2446  $ret .= ($ret ? $sep : '').$object->zip;
2447  }
2448  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2449  // ES: title firstname name \n address lines \n zip town \n state \n country
2450  $ret .= ($ret ? $sep : '').$object->zip;
2451  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2452  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2453  if (!empty($object->state)) {
2454  $ret .= "\n".$object->state;
2455  }
2456  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2457  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2458  // See https://www.sljfaq.org/afaq/addresses.html
2459  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2460  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2461  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2462  // IT: title firstname name\n address lines \n zip town state_code \n country
2463  $ret .= ($ret ? $sep : '').$object->zip;
2464  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2465  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2466  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2467  } else {
2468  // Other: title firstname name \n address lines \n zip town[, state] \n country
2469  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2470  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2471  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2472  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2473  $ret .= ($ret ? ", " : '').$object->state;
2474  }
2475  }
2476  if (!is_object($outputlangs)) {
2477  $outputlangs = $langs;
2478  }
2479  if ($withcountry) {
2480  $langs->load("dict");
2481  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2482  }
2483  if ($hookmanager) {
2484  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2485  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2486  if ($reshook > 0) {
2487  $ret = '';
2488  }
2489  $ret .= $hookmanager->resPrint;
2490  }
2491 
2492  return $ret;
2493 }
2494 
2495 
2496 
2505 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2506 {
2507  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2508  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2509  } else {
2510  return 'Error date into a not supported range';
2511  }
2512 }
2513 
2535 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2536 {
2537  global $conf, $langs;
2538 
2539  // If date undefined or "", we return ""
2540  if (dol_strlen($time) == 0) {
2541  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2542  }
2543 
2544  if ($tzoutput === 'auto') {
2545  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2546  }
2547 
2548  // Clean parameters
2549  $to_gmt = false;
2550  $offsettz = $offsetdst = 0;
2551  if ($tzoutput) {
2552  $to_gmt = true; // For backward compatibility
2553  if (is_string($tzoutput)) {
2554  if ($tzoutput == 'tzserver') {
2555  $to_gmt = false;
2556  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2557  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
2558  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
2559  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2560  $to_gmt = true;
2561  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2562 
2563  if (class_exists('DateTimeZone')) {
2564  $user_date_tz = new DateTimeZone($offsettzstring);
2565  $user_dt = new DateTime();
2566  $user_dt->setTimezone($user_date_tz);
2567  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2568  $offsettz = $user_dt->getOffset(); // should include dst ?
2569  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
2570  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2571  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2572  }
2573  }
2574  }
2575  }
2576  if (!is_object($outputlangs)) {
2577  $outputlangs = $langs;
2578  }
2579  if (!$format) {
2580  $format = 'daytextshort';
2581  }
2582 
2583  // Do we have to reduce the length of date (year on 2 chars) to save space.
2584  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2585  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2586  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2587  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2588  if ($formatwithoutreduce != $format) {
2589  $format = $formatwithoutreduce;
2590  $reduceformat = 1;
2591  } // so format 'dayreduceformat' is processed like day
2592 
2593  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2594  // TODO Add format daysmallyear and dayhoursmallyear
2595  if ($format == 'day') {
2596  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2597  } elseif ($format == 'hour') {
2598  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2599  } elseif ($format == 'hourduration') {
2600  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2601  } elseif ($format == 'daytext') {
2602  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2603  } elseif ($format == 'daytextshort') {
2604  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2605  } elseif ($format == 'dayhour') {
2606  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2607  } elseif ($format == 'dayhoursec') {
2608  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2609  } elseif ($format == 'dayhourtext') {
2610  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2611  } elseif ($format == 'dayhourtextshort') {
2612  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2613  } elseif ($format == 'dayhourlog') {
2614  // Format not sensitive to language
2615  $format = '%Y%m%d%H%M%S';
2616  } elseif ($format == 'dayhourlogsmall') {
2617  // Format not sensitive to language
2618  $format = '%Y%m%d%H%M';
2619  } elseif ($format == 'dayhourldap') {
2620  $format = '%Y%m%d%H%M%SZ';
2621  } elseif ($format == 'dayhourxcard') {
2622  $format = '%Y%m%dT%H%M%SZ';
2623  } elseif ($format == 'dayxcard') {
2624  $format = '%Y%m%d';
2625  } elseif ($format == 'dayrfc') {
2626  $format = '%Y-%m-%d'; // DATE_RFC3339
2627  } elseif ($format == 'dayhourrfc') {
2628  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2629  } elseif ($format == 'standard') {
2630  $format = '%Y-%m-%d %H:%M:%S';
2631  }
2632 
2633  if ($reduceformat) {
2634  $format = str_replace('%Y', '%y', $format);
2635  $format = str_replace('yyyy', 'yy', $format);
2636  }
2637 
2638  // Clean format
2639  if (preg_match('/%b/i', $format)) { // There is some text to translate
2640  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2641  $format = str_replace('%b', '__b__', $format);
2642  $format = str_replace('%B', '__B__', $format);
2643  }
2644  if (preg_match('/%a/i', $format)) { // There is some text to translate
2645  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2646  $format = str_replace('%a', '__a__', $format);
2647  $format = str_replace('%A', '__A__', $format);
2648  }
2649 
2650  // Analyze date
2651  $reg = array();
2652  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
2653  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2654  return '';
2655  } 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
2656  // This part of code should not be used anymore.
2657  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2658  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2659  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2660  $syear = (!empty($reg[1]) ? $reg[1] : '');
2661  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2662  $sday = (!empty($reg[3]) ? $reg[3] : '');
2663  $shour = (!empty($reg[4]) ? $reg[4] : '');
2664  $smin = (!empty($reg[5]) ? $reg[5] : '');
2665  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2666 
2667  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2668 
2669  if ($to_gmt) {
2670  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2671  } else {
2672  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2673  }
2674  $dtts = new DateTime();
2675  $dtts->setTimestamp($time);
2676  $dtts->setTimezone($tzo);
2677  $newformat = str_replace(
2678  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2679  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2680  $format);
2681  $ret = $dtts->format($newformat);
2682  $ret = str_replace(
2683  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2684  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2685  $ret
2686  );
2687  } else {
2688  // Date is a timestamps
2689  if ($time < 100000000000) { // Protection against bad date values
2690  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2691 
2692  if ($to_gmt) {
2693  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2694  } else {
2695  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2696  }
2697  $dtts = new DateTime();
2698  $dtts->setTimestamp($timetouse);
2699  $dtts->setTimezone($tzo);
2700  $newformat = str_replace(
2701  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2702  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2703  $format);
2704  $ret = $dtts->format($newformat);
2705  $ret = str_replace(
2706  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2707  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2708  $ret
2709  );
2710  //var_dump($ret);exit;
2711  } else {
2712  $ret = 'Bad value '.$time.' for date';
2713  }
2714  }
2715 
2716  if (preg_match('/__b__/i', $format)) {
2717  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2718 
2719  if ($to_gmt) {
2720  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2721  } else {
2722  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2723  }
2724  $dtts = new DateTime();
2725  $dtts->setTimestamp($timetouse);
2726  $dtts->setTimezone($tzo);
2727  $month = $dtts->format("m");
2728  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2729  if ($encodetooutput) {
2730  $monthtext = $outputlangs->transnoentities('Month'.$month);
2731  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2732  } else {
2733  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2734  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2735  }
2736  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2737  $ret = str_replace('__b__', $monthtextshort, $ret);
2738  $ret = str_replace('__B__', $monthtext, $ret);
2739  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2740  //return $ret;
2741  }
2742  if (preg_match('/__a__/i', $format)) {
2743  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2744  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2745 
2746  if ($to_gmt) {
2747  $tzo = new DateTimeZone('UTC');
2748  } else {
2749  $tzo = new DateTimeZone(date_default_timezone_get());
2750  }
2751  $dtts = new DateTime();
2752  $dtts->setTimestamp($timetouse);
2753  $dtts->setTimezone($tzo);
2754  $w = $dtts->format("w");
2755  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2756 
2757  $ret = str_replace('__A__', $dayweek, $ret);
2758  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2759  }
2760 
2761  return $ret;
2762 }
2763 
2764 
2785 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2786 {
2787  $datetimeobj = new DateTime();
2788  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2789  if ($forcetimezone) {
2790  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2791  }
2792  $arrayinfo = array(
2793  'year'=>((int) date_format($datetimeobj, 'Y')),
2794  'mon'=>((int) date_format($datetimeobj, 'm')),
2795  'mday'=>((int) date_format($datetimeobj, 'd')),
2796  'wday'=>((int) date_format($datetimeobj, 'w')),
2797  'yday'=>((int) date_format($datetimeobj, 'z')),
2798  'hours'=>((int) date_format($datetimeobj, 'H')),
2799  'minutes'=>((int) date_format($datetimeobj, 'i')),
2800  'seconds'=>((int) date_format($datetimeobj, 's')),
2801  '0'=>$timestamp
2802  );
2803 
2804  return $arrayinfo;
2805 }
2806 
2828 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2829 {
2830  global $conf;
2831  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2832 
2833  if ($gm === 'auto') {
2834  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2835  }
2836  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2837 
2838  // Clean parameters
2839  if ($hour == -1 || empty($hour)) {
2840  $hour = 0;
2841  }
2842  if ($minute == -1 || empty($minute)) {
2843  $minute = 0;
2844  }
2845  if ($second == -1 || empty($second)) {
2846  $second = 0;
2847  }
2848 
2849  // Check parameters
2850  if ($check) {
2851  if (!$month || !$day) {
2852  return '';
2853  }
2854  if ($day > 31) {
2855  return '';
2856  }
2857  if ($month > 12) {
2858  return '';
2859  }
2860  if ($hour < 0 || $hour > 24) {
2861  return '';
2862  }
2863  if ($minute < 0 || $minute > 60) {
2864  return '';
2865  }
2866  if ($second < 0 || $second > 60) {
2867  return '';
2868  }
2869  }
2870 
2871  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
2872  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
2873  $localtz = new DateTimeZone($default_timezone);
2874  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
2875  // We use dol_tz_string first because it is more reliable.
2876  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2877  try {
2878  $localtz = new DateTimeZone($default_timezone);
2879  } catch (Exception $e) {
2880  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2881  $default_timezone = @date_default_timezone_get();
2882  }
2883  } elseif (strrpos($gm, "tz,") !== false) {
2884  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2885  try {
2886  $localtz = new DateTimeZone($timezone);
2887  } catch (Exception $e) {
2888  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2889  }
2890  }
2891 
2892  if (empty($localtz)) {
2893  $localtz = new DateTimeZone('UTC');
2894  }
2895  //var_dump($localtz);
2896  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2897  $dt = new DateTime('now', $localtz);
2898  $dt->setDate((int) $year, (int) $month, (int) $day);
2899  $dt->setTime((int) $hour, (int) $minute, (int) $second);
2900  $date = $dt->getTimestamp(); // should include daylight saving time
2901  //var_dump($date);
2902  return $date;
2903 }
2904 
2905 
2916 function dol_now($mode = 'auto')
2917 {
2918  $ret = 0;
2919 
2920  if ($mode === 'auto') {
2921  $mode = 'gmt';
2922  }
2923 
2924  if ($mode == 'gmt') {
2925  $ret = time(); // Time for now at greenwich.
2926  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
2927  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2928  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2929  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2930  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
2931  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2932  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
2933  // $ret=dol_now('gmt')+($tzsecond*3600);
2934  //}
2935  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
2936  // Time for now with user timezone added
2937  //print 'time: '.time();
2938  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
2939  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
2940  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
2941  }
2942 
2943  return $ret;
2944 }
2945 
2946 
2955 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2956 {
2957  global $conf, $langs;
2958  $level = 1024;
2959 
2960  if (!empty($conf->dol_optimize_smallscreen)) {
2961  $shortunit = 1;
2962  }
2963 
2964  // Set value text
2965  if (empty($shortvalue) || $size < ($level * 10)) {
2966  $ret = $size;
2967  $textunitshort = $langs->trans("b");
2968  $textunitlong = $langs->trans("Bytes");
2969  } else {
2970  $ret = round($size / $level, 0);
2971  $textunitshort = $langs->trans("Kb");
2972  $textunitlong = $langs->trans("KiloBytes");
2973  }
2974  // Use long or short text unit
2975  if (empty($shortunit)) {
2976  $ret .= ' '.$textunitlong;
2977  } else {
2978  $ret .= ' '.$textunitshort;
2979  }
2980 
2981  return $ret;
2982 }
2983 
2993 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
2994 {
2995  global $langs;
2996 
2997  if (empty($url)) {
2998  return '';
2999  }
3000 
3001  $link = '<a href="';
3002  if (!preg_match('/^http/i', $url)) {
3003  $link .= 'http://';
3004  }
3005  $link .= $url;
3006  $link .= '"';
3007  if ($target) {
3008  $link .= ' target="'.$target.'"';
3009  }
3010  $link .= '>';
3011  if (!preg_match('/^http/i', $url)) {
3012  $link .= 'http://';
3013  }
3014  $link .= dol_trunc($url, $max);
3015  $link .= '</a>';
3016  return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
3017 }
3018 
3031 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3032 {
3033  global $conf, $user, $langs, $hookmanager;
3034 
3035  $newemail = dol_escape_htmltag($email);
3036 
3037  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
3038  $withpicto = 0;
3039  }
3040 
3041  if (empty($email)) {
3042  return '&nbsp;';
3043  }
3044 
3045  if (!empty($addlink)) {
3046  $newemail = '<a style="text-overflow: ellipsis;" href="';
3047  if (!preg_match('/^mailto:/i', $email)) {
3048  $newemail .= 'mailto:';
3049  }
3050  $newemail .= $email;
3051  $newemail .= '">';
3052  $newemail .= dol_trunc($email, $max);
3053  $newemail .= '</a>';
3054  if ($showinvalid && !isValidEmail($email)) {
3055  $langs->load("errors");
3056  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3057  }
3058 
3059  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3060  $type = 'AC_EMAIL';
3061  $link = '';
3062  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
3063  $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>';
3064  }
3065  if ($link) {
3066  $newemail = '<div>'.$newemail.' '.$link.'</div>';
3067  }
3068  }
3069  } else {
3070  if ($showinvalid && !isValidEmail($email)) {
3071  $langs->load("errors");
3072  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3073  }
3074  }
3075 
3076  //$rep = '<div class="nospan" style="margin-right: 10px">';
3077  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3078  //$rep .= '</div>';
3079  if ($hookmanager) {
3080  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3081 
3082  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3083  if ($reshook > 0) {
3084  $rep = '';
3085  }
3086  $rep .= $hookmanager->resPrint;
3087  }
3088 
3089  return $rep;
3090 }
3091 
3098 {
3099  global $conf, $db;
3100 
3101  $socialnetworks = array();
3102  // Enable caching of array
3103  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3104  $cachekey = 'socialnetworks_' . $conf->entity;
3105  $dataretrieved = dol_getcache($cachekey);
3106  if (!is_null($dataretrieved)) {
3107  $socialnetworks = $dataretrieved;
3108  } else {
3109  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3110  $sql .= " WHERE entity=".$conf->entity;
3111  $resql = $db->query($sql);
3112  if ($resql) {
3113  while ($obj = $db->fetch_object($resql)) {
3114  $socialnetworks[$obj->code] = array(
3115  'rowid' => $obj->rowid,
3116  'label' => $obj->label,
3117  'url' => $obj->url,
3118  'icon' => $obj->icon,
3119  'active' => $obj->active,
3120  );
3121  }
3122  }
3123  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3124  }
3125 
3126  return $socialnetworks;
3127 }
3128 
3139 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3140 {
3141  global $conf, $user, $langs;
3142 
3143  $htmllink = $value;
3144 
3145  if (empty($value)) {
3146  return '&nbsp;';
3147  }
3148 
3149  if (!empty($type)) {
3150  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3151  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3152  $htmllink .= '<span class="fa paddingright '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3153  if ($type == 'skype') {
3154  $htmllink .= dol_escape_htmltag($value);
3155  $htmllink .= '&nbsp;';
3156  $htmllink .= '<a href="skype:';
3157  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3158  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3159  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3160  $htmllink .= '</a><a href="skype:';
3161  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3162  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3163  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3164  $htmllink .= '</a>';
3165  if (($cid || $socid) && isModEnabled('agenda') && $user->rights->agenda->myactions->create) {
3166  $addlink = 'AC_SKYPE';
3167  $link = '';
3168  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3169  $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>';
3170  }
3171  $htmllink .= ($link ? ' '.$link : '');
3172  }
3173  } else {
3174  if (!empty($dictsocialnetworks[$type]['url'])) {
3175  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3176  if ($tmpvirginurl) {
3177  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3178  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3179 
3180  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3181  if ($tmpvirginurl3) {
3182  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3183  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3184  }
3185 
3186  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3187  if ($tmpvirginurl2) {
3188  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3189  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3190  }
3191  }
3192  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3193  if (preg_match('/^https?:\/\//i', $link)) {
3194  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3195  } else {
3196  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3197  }
3198  } else {
3199  $htmllink .= dol_escape_htmltag($value);
3200  }
3201  }
3202  $htmllink .= '</div>';
3203  } else {
3204  $langs->load("errors");
3205  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3206  }
3207  return $htmllink;
3208 }
3209 
3220 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3221 {
3222  global $mysoc;
3223 
3224  if (empty($profID) || empty($profIDtype)) {
3225  return '';
3226  }
3227  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3228  $newProfID = $profID;
3229  $id = substr($profIDtype, -1);
3230  $ret = '';
3231  if (strtoupper($countrycode) == 'FR') {
3232  // France
3233  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3234  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);
3235  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);
3236  }
3237  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3238  else $ret = $newProfID;
3239  return $ret;
3240 }
3241 
3256 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3257 {
3258  global $conf, $user, $langs, $mysoc, $hookmanager;
3259 
3260  // Clean phone parameter
3261  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3262  if (empty($phone)) {
3263  return '';
3264  }
3265  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3266  $separ = $conf->global->MAIN_PHONE_SEPAR;
3267  }
3268  if (empty($countrycode) && is_object($mysoc)) {
3269  $countrycode = $mysoc->country_code;
3270  }
3271 
3272  // Short format for small screens
3273  if ($conf->dol_optimize_smallscreen) {
3274  $separ = '';
3275  }
3276 
3277  $newphone = $phone;
3278  if (strtoupper($countrycode) == "FR") {
3279  // France
3280  if (dol_strlen($phone) == 10) {
3281  $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);
3282  } elseif (dol_strlen($phone) == 7) {
3283  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3284  } elseif (dol_strlen($phone) == 9) {
3285  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3286  } elseif (dol_strlen($phone) == 11) {
3287  $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);
3288  } elseif (dol_strlen($phone) == 12) {
3289  $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);
3290  } elseif (dol_strlen($phone) == 13) {
3291  $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);
3292  }
3293  } elseif (strtoupper($countrycode) == "CA") {
3294  if (dol_strlen($phone) == 10) {
3295  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3296  }
3297  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3298  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3299  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3300  }
3301  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3302  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3303  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3304  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3305  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3306  }
3307  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3308  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3309  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3310  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3311  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3312  }
3313  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3314  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3315  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3316  }
3317  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3318  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3319  $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);
3320  }
3321  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3322  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3323  $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);
3324  }
3325  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3326  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3327  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3328  }
3329  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3330  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3331  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3332  }
3333  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3334  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3335  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3336  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3337  $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);
3338  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3339  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3340  }
3341  } elseif (strtoupper($countrycode) == "ML") {//Mali
3342  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3343  $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);
3344  }
3345  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3346  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3347  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3348  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3349  $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);
3350  }
3351  } elseif (strtoupper($countrycode) == "MU") {
3352  //Maurice
3353  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3354  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3355  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3356  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3357  }
3358  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3359  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3360  $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);
3361  }
3362  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3363  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3364  $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);
3365  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3366  $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);
3367  }
3368  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3369  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3370  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3371  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3372  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3373  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3374  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3375  }
3376  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3377  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3378  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3379  }
3380  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3381  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3382  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3383  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3384  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3385  }
3386  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3387  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3388  $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);
3389  }
3390  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3391  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3392  $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);
3393  }
3394  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3395  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3396  $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);
3397  }
3398  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3399  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3400  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3401  }
3402  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3403  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3404  $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);
3405  }
3406  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3407  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3408  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3409  }
3410  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3411  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3412  $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);
3413  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3414  $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);
3415  }
3416  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3417  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3418  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3419  }
3420  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3421  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3422  $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);
3423  }
3424  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3425  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3426  $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);
3427  }
3428  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3429  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3430  $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);
3431  }
3432  } elseif (strtoupper($countrycode) == "IT") {//Italie
3433  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3434  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3435  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3436  $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);
3437  }
3438  } elseif (strtoupper($countrycode) == "AU") {
3439  //Australie
3440  if (dol_strlen($phone) == 12) {
3441  //ex: +61_A_BCDE_FGHI
3442  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3443  }
3444  } elseif (strtoupper($countrycode) == "LU") {
3445  // Luxembourg
3446  if (dol_strlen($phone) == 10) {// fixe 6 chiffres +352_AA_BB_CC
3447  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
3448  } elseif (dol_strlen($phone) == 11) {// fixe 7 chiffres +352_AA_BB_CC_D
3449  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 1);
3450  } elseif (dol_strlen($phone) == 12) {// fixe 8 chiffres +352_AA_BB_CC_DD
3451  $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);
3452  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
3453  $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);
3454  }
3455  }
3456  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3457  if ($conf->browser->layout == 'phone' || (isModEnabled('clicktodial') && !empty($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS))) { // If phone or option for, we use link of phone
3458  $newphoneform = $newphone;
3459  $newphone = '<a href="tel:'.$phone.'"';
3460  $newphone .= '>'.$newphoneform.'</a>';
3461  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3462  if (empty($user->clicktodial_loaded)) {
3463  $user->fetch_clicktodial();
3464  }
3465 
3466  // Define urlmask
3467  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3468  if (!empty($conf->global->CLICKTODIAL_URL)) {
3469  $urlmask = $conf->global->CLICKTODIAL_URL;
3470  }
3471  if (!empty($user->clicktodial_url)) {
3472  $urlmask = $user->clicktodial_url;
3473  }
3474 
3475  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3476  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3477  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3478  // This line is for backward compatibility
3479  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3480  // Thoose lines are for substitution
3481  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3482  '__PHONETO__'=>urlencode($phone),
3483  '__LOGIN__'=>$clicktodial_login,
3484  '__PASS__'=>$clicktodial_password);
3485  $url = make_substitutions($url, $substitarray);
3486  $newphonesav = $newphone;
3487  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3488  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3489  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3490  $newphone .= '>'.$newphonesav.'</a>';
3491  } else {
3492  // Old method
3493  $newphone = '<a href="'.$url.'"';
3494  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3495  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3496  }
3497  $newphone .= '>'.$newphonesav.'</a>';
3498  }
3499  }
3500 
3501  //if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
3502  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3503  $type = 'AC_TEL';
3504  $link = '';
3505  if ($addlink == 'AC_FAX') {
3506  $type = 'AC_FAX';
3507  }
3508  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3509  $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>';
3510  }
3511  if ($link) {
3512  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3513  }
3514  }
3515  }
3516 
3517  if (empty($titlealt)) {
3518  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3519  }
3520  $rep = '';
3521 
3522  if ($hookmanager) {
3523  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3524  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3525  $rep .= $hookmanager->resPrint;
3526  }
3527  if (empty($reshook)) {
3528  $picto = '';
3529  if ($withpicto) {
3530  if ($withpicto == 'fax') {
3531  $picto = 'phoning_fax';
3532  } elseif ($withpicto == 'phone') {
3533  $picto = 'phoning';
3534  } elseif ($withpicto == 'mobile') {
3535  $picto = 'phoning_mobile';
3536  } else {
3537  $picto = '';
3538  }
3539  }
3540  if ($adddivfloat) {
3541  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3542  } else {
3543  $rep .= '<span style="margin-right: 10px;">';
3544  }
3545  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3546  if ($adddivfloat) {
3547  $rep .= '</div>';
3548  } else {
3549  $rep .= '</span>';
3550  }
3551  }
3552 
3553  return $rep;
3554 }
3555 
3563 function dol_print_ip($ip, $mode = 0)
3564 {
3565  global $conf, $langs;
3566 
3567  $ret = '';
3568 
3569  if (empty($mode)) {
3570  $ret .= $ip;
3571  }
3572 
3573  if ($mode != 2) {
3574  $countrycode = dolGetCountryCodeFromIp($ip);
3575  if ($countrycode) { // If success, countrycode is us, fr, ...
3576  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3577  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3578  } else {
3579  $ret .= ' ('.$countrycode.')';
3580  }
3581  } else {
3582  // Nothing
3583  }
3584  }
3585 
3586  return $ret;
3587 }
3588 
3598 {
3599  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3600  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3601  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3602  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3603  } else {
3604  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3605  }
3606  } else {
3607  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3608  }
3609  } else {
3610  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3611  }
3612  return $ip;
3613 }
3614 
3623 function isHTTPS()
3624 {
3625  $isSecure = false;
3626  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3627  $isSecure = true;
3628  } 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') {
3629  $isSecure = true;
3630  }
3631  return $isSecure;
3632 }
3633 
3641 {
3642  global $conf;
3643 
3644  $countrycode = '';
3645 
3646  if (!empty($conf->geoipmaxmind->enabled)) {
3647  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3648  //$ip='24.24.24.24';
3649  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3650  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3651  $geoip = new DolGeoIP('country', $datafile);
3652  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3653  $countrycode = $geoip->getCountryCodeFromIP($ip);
3654  }
3655 
3656  return $countrycode;
3657 }
3658 
3659 
3667 {
3668  global $conf, $langs, $user;
3669 
3670  //$ret=$user->xxx;
3671  $ret = '';
3672  if (!empty($conf->geoipmaxmind->enabled)) {
3673  $ip = getUserRemoteIP();
3674  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3675  //$ip='24.24.24.24';
3676  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3677  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3678  $geoip = new DolGeoIP('country', $datafile);
3679  $countrycode = $geoip->getCountryCodeFromIP($ip);
3680  $ret = $countrycode;
3681  }
3682  return $ret;
3683 }
3684 
3697 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3698 {
3699  global $conf, $user, $langs, $hookmanager;
3700 
3701  $out = '';
3702 
3703  if ($address) {
3704  if ($hookmanager) {
3705  $parameters = array('element' => $element, 'id' => $id);
3706  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3707  $out .= $hookmanager->resPrint;
3708  }
3709  if (empty($reshook)) {
3710  if (empty($charfornl)) {
3711  $out .= nl2br($address);
3712  } else {
3713  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3714  }
3715 
3716  // TODO Remove this block, we can add this using the hook now
3717  $showgmap = $showomap = 0;
3718  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3719  $showgmap = 1;
3720  }
3721  if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3722  $showgmap = 1;
3723  }
3724  if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3725  $showgmap = 1;
3726  }
3727  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3728  $showomap = 1;
3729  }
3730  if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3731  $showomap = 1;
3732  }
3733  if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3734  $showomap = 1;
3735  }
3736  if ($showgmap) {
3737  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3738  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3739  }
3740  if ($showomap) {
3741  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3742  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3743  }
3744  }
3745  }
3746  if ($noprint) {
3747  return $out;
3748  } else {
3749  print $out;
3750  }
3751 }
3752 
3753 
3763 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3764 {
3765  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3766  return true;
3767  }
3768  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3769  return true;
3770  }
3771  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3772  return true;
3773  }
3774 
3775  return false;
3776 }
3777 
3786 function isValidMXRecord($domain)
3787 {
3788  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3789  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3790  return 0;
3791  }
3792  if (function_exists('getmxrr')) {
3793  $mxhosts = array();
3794  $weight = array();
3795  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3796  if (count($mxhosts) > 1) {
3797  return 1;
3798  }
3799  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3800  return 1;
3801  }
3802 
3803  return 0;
3804  }
3805  }
3806 
3807  // function idn_to_ascii or checkdnsrr or getmxrr does not exists
3808  return -1;
3809 }
3810 
3818 function isValidPhone($phone)
3819 {
3820  return true;
3821 }
3822 
3823 
3833 function dolGetFirstLetters($s, $nbofchar = 1)
3834 {
3835  $ret = '';
3836  $tmparray = explode(' ', $s);
3837  foreach ($tmparray as $tmps) {
3838  $ret .= dol_substr($tmps, 0, $nbofchar);
3839  }
3840 
3841  return $ret;
3842 }
3843 
3844 
3852 function dol_strlen($string, $stringencoding = 'UTF-8')
3853 {
3854  if (is_null($string)) {
3855  return 0;
3856  }
3857 
3858  if (function_exists('mb_strlen')) {
3859  return mb_strlen($string, $stringencoding);
3860  } else {
3861  return strlen($string);
3862  }
3863 }
3864 
3875 function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
3876 {
3877  global $langs;
3878 
3879  if (empty($stringencoding)) {
3880  $stringencoding = $langs->charset_output;
3881  }
3882 
3883  $ret = '';
3884  if (empty($trunconbytes)) {
3885  if (function_exists('mb_substr')) {
3886  $ret = mb_substr($string, $start, $length, $stringencoding);
3887  } else {
3888  $ret = substr($string, $start, $length);
3889  }
3890  } else {
3891  if (function_exists('mb_strcut')) {
3892  $ret = mb_strcut($string, $start, $length, $stringencoding);
3893  } else {
3894  $ret = substr($string, $start, $length);
3895  }
3896  }
3897  return $ret;
3898 }
3899 
3900 
3914 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
3915 {
3916  global $conf;
3917 
3918  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
3919  return $string;
3920  }
3921 
3922  if (empty($stringencoding)) {
3923  $stringencoding = 'UTF-8';
3924  }
3925  // reduce for small screen
3926  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
3927  $size = round($size / 3);
3928  }
3929 
3930  // We go always here
3931  if ($trunc == 'right') {
3932  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3933  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3934  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3935  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
3936  } else {
3937  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
3938  return $string;
3939  }
3940  } elseif ($trunc == 'middle') {
3941  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3942  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3943  $size1 = round($size / 2);
3944  $size2 = round($size / 2);
3945  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
3946  } else {
3947  return $string;
3948  }
3949  } elseif ($trunc == 'left') {
3950  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3951  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3952  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3953  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
3954  } else {
3955  return $string;
3956  }
3957  } elseif ($trunc == 'wrap') {
3958  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3959  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3960  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
3961  } else {
3962  return $string;
3963  }
3964  } else {
3965  return 'BadParam3CallingDolTrunc';
3966  }
3967 }
3968 
3989 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
3990 {
3991  global $conf, $langs;
3992 
3993  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
3994  $url = DOL_URL_ROOT;
3995  $theme = isset($conf->theme) ? $conf->theme : null;
3996  $path = 'theme/'.$theme;
3997  // Define fullpathpicto to use into src
3998  if ($pictoisfullpath) {
3999  // Clean parameters
4000  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4001  $picto .= '.png';
4002  }
4003  $fullpathpicto = $picto;
4004  $reg = array();
4005  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4006  $morecss .= ($morecss ? ' ' : '').$reg[1];
4007  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4008  }
4009  } else {
4010  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
4011  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4012 
4013  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
4014  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
4015  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4016  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4017 
4018  $pictowithouttextarray = explode('_', $pictowithouttext);
4019  $marginleftonlyshort = 0;
4020 
4021  if (!empty($pictowithouttextarray[1])) {
4022  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4023  $fakey = 'fa-'.$pictowithouttextarray[0];
4024  $fa = empty($pictowithouttextarray[1]) ? 'fa' : $pictowithouttextarray[1];
4025  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4026  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4027  } else {
4028  $fakey = 'fa-'.$pictowithouttext;
4029  $fa = 'fa';
4030  $facolor = '';
4031  $fasize = '';
4032  }
4033 
4034  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4035  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4036  $morestyle = '';
4037  $reg = array();
4038  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4039  $morecss .= ($morecss ? ' ' : '').$reg[1];
4040  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4041  }
4042  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4043  $morestyle = $reg[1];
4044  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4045  }
4046  $moreatt = trim($moreatt);
4047 
4048  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4049  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4050  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4051  $enabledisablehtml .= $titlealt;
4052  }*/
4053  $enabledisablehtml .= '</span>';
4054 
4055  return $enabledisablehtml;
4056  }
4057 
4058  if (empty($srconly) && in_array($pictowithouttext, array(
4059  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4060  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
4061  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
4062  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4063  'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cubes',
4064  'currency', 'multicurrency',
4065  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4066  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4067  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
4068  'generate', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4069  'help', 'holiday',
4070  'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'knowledgemanagement',
4071  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4072  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4073  'off', 'on', 'order',
4074  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4075  'stock', 'resize', 'service', 'stats', 'trip',
4076  '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',
4077  'github', 'google', 'jabber', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4078  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4079  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4080  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4081  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4082  'technic', 'ticket',
4083  'error', 'warning',
4084  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4085  'shapes', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4086  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
4087  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4088  'conferenceorbooth', 'eventorganization',
4089  'stamp', 'signature'
4090  ))) {
4091  $fakey = $pictowithouttext;
4092  $facolor = '';
4093  $fasize = '';
4094  $fa = 'fas';
4095  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'))) {
4096  $fa = 'far';
4097  }
4098  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4099  $fa = 'fab';
4100  }
4101 
4102  $arrayconvpictotofa = array(
4103  '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',
4104  'bank_account'=>'university',
4105  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
4106  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
4107  'bom'=>'shapes',
4108  'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time',
4109  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
4110  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
4111  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4112  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4113  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4114  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4115  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4116  'generic'=>'file', 'holiday'=>'umbrella-beach',
4117  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4118  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4119  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4120  'sign-out'=>'sign-out-alt',
4121  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4122  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4123  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4124  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4125  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4126  'other'=>'square',
4127  'playdisabled'=>'play', 'pdf'=>'file-pdf', 'poll'=>'check-double', 'pos'=>'cash-register', 'preview'=>'binoculars', 'project'=>'project-diagram', 'projectpub'=>'project-diagram', 'projecttask'=>'tasks', 'propal'=>'file-signature', 'proposal'=>'file-signature',
4128  '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',
4129  'recent' => 'question', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4130  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4131  'refresh'=>'redo', 'region'=>'map-marked', 'replacement'=>'exchange-alt', 'resource'=>'laptop-house', 'recurring'=>'history',
4132  'service'=>'concierge-bell',
4133  'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4134  'supplier'=>'building', 'technic'=>'cogs',
4135  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4136  'title_agenda'=>'calendar-alt',
4137  'uncheck'=>'times', 'uparrow'=>'share', 'url'=>'external-link-alt', 'vat'=>'money-check-alt', 'vcard'=>'address-card',
4138  'jabber'=>'comment-o',
4139  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4140  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4141  );
4142  if ($pictowithouttext == 'off') {
4143  $fakey = 'fa-square';
4144  $fasize = '1.3em';
4145  } elseif ($pictowithouttext == 'on') {
4146  $fakey = 'fa-check-square';
4147  $fasize = '1.3em';
4148  } elseif ($pictowithouttext == 'listlight') {
4149  $fakey = 'fa-download';
4150  $marginleftonlyshort = 1;
4151  } elseif ($pictowithouttext == 'printer') {
4152  $fakey = 'fa-print';
4153  $fasize = '1.2em';
4154  } elseif ($pictowithouttext == 'note') {
4155  $fakey = 'fa-sticky-note';
4156  $marginleftonlyshort = 1;
4157  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4158  $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');
4159  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4160  if (preg_match('/selected/', $pictowithouttext)) {
4161  $facolor = '#888';
4162  }
4163  $marginleftonlyshort = 1;
4164  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4165  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4166  } else {
4167  $fakey = 'fa-'.$pictowithouttext;
4168  }
4169 
4170  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4171  $morecss .= ' em092';
4172  }
4173  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4174  $morecss .= ' em088';
4175  }
4176  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4177  $morecss .= ' em080';
4178  }
4179 
4180  // Define $marginleftonlyshort
4181  $arrayconvpictotomarginleftonly = array(
4182  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4183  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4184  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4185  );
4186  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4187  $marginleftonlyshort = 0;
4188  }
4189 
4190  // Add CSS
4191  $arrayconvpictotomorcess = array(
4192  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4193  'bank_account'=>'infobox-bank_account',
4194  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4195  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4196  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4197  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4198  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4199  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4200  'incoterm'=>'infobox-supplier_proposal',
4201  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4202  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4203  'order'=>'infobox-commande',
4204  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4205  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_red'=>'font-status8',
4206  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4207  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4208  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4209  'propal'=>'infobox-propal', 'proposal'=>'infobox-propal','private'=>'infobox-project',
4210  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4211  'resource'=>'infobox-action',
4212  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4213  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4214  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4215  'vat'=>'infobox-bank_account',
4216  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4217  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4218  );
4219  if (!empty($arrayconvpictotomorcess[$pictowithouttext])) {
4220  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4221  }
4222 
4223  // Define $color
4224  $arrayconvpictotocolor = array(
4225  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4226  'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4227  'dynamicprice'=>'#a69944',
4228  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4229  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4230  'lock'=>'#ddd', 'lot'=>'#a69944',
4231  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4232  'other'=>'#ddd', 'world'=>'#986c6a',
4233  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4234  //'shipment'=>'#a69944',
4235  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4236  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4237  'website'=>'#304', 'workstation'=>'#a69944'
4238  );
4239  if (isset($arrayconvpictotocolor[$pictowithouttext])) {
4240  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4241  }
4242 
4243  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4244  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4245  $morestyle = '';
4246  $reg = array();
4247  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4248  $morecss .= ($morecss ? ' ' : '').$reg[1];
4249  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4250  }
4251  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4252  $morestyle = $reg[1];
4253  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4254  }
4255  $moreatt = trim($moreatt);
4256 
4257  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4258  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4259  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4260  $enabledisablehtml .= $titlealt;
4261  }*/
4262  $enabledisablehtml .= '</span>';
4263 
4264  return $enabledisablehtml;
4265  }
4266 
4267  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4268  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4269  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4270  $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
4271  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4272  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4273  }
4274 
4275  // If we ask an image into $url/$mymodule/img (instead of default path)
4276  $regs = array();
4277  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4278  $picto = $regs[1];
4279  $path = $regs[2]; // $path is $mymodule
4280  }
4281 
4282  // Clean parameters
4283  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4284  $picto .= '.png';
4285  }
4286  // If alt path are defined, define url where img file is, according to physical path
4287  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4288  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4289  if ($type == 'main') {
4290  continue;
4291  }
4292  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4293  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4294  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4295  break;
4296  }
4297  }
4298 
4299  // $url is '' or '/custom', $path is current theme or
4300  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4301  }
4302 
4303  if ($srconly) {
4304  return $fullpathpicto;
4305  }
4306  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4307  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
4308 }
4309 
4323 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4324 {
4325  if (strpos($picto, '^') === 0) {
4326  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4327  } else {
4328  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4329  }
4330 }
4331 
4343 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4344 {
4345  global $conf;
4346 
4347  if (is_numeric($picto)) {
4348  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4349  //$picto = $leveltopicto[$picto];
4350  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4351  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4352  $picto .= '.png';
4353  }
4354 
4355  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4356 
4357  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4358 }
4359 
4371 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4372 {
4373  global $conf;
4374 
4375  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4376  $picto .= '.png';
4377  }
4378 
4379  if ($pictoisfullpath) {
4380  $path = $picto;
4381  } else {
4382  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4383 
4384  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4385  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4386 
4387  if (file_exists($themepath)) {
4388  $path = $themepath;
4389  }
4390  }
4391  }
4392 
4393  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4394 }
4395 
4408 function img_action($titlealt, $numaction, $picto = '')
4409 {
4410  global $langs;
4411 
4412  if (empty($titlealt) || $titlealt == 'default') {
4413  if ($numaction == '-1' || $numaction == 'ST_NO') {
4414  $numaction = -1;
4415  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4416  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4417  $numaction = 0;
4418  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4419  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4420  $numaction = 1;
4421  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4422  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4423  $numaction = 2;
4424  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4425  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4426  $numaction = 3;
4427  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4428  } else {
4429  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4430  $numaction = 0;
4431  }
4432  }
4433  if (!is_numeric($numaction)) {
4434  $numaction = 0;
4435  }
4436 
4437  return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png');
4438 }
4439 
4447 function img_pdf($titlealt = 'default', $size = 3)
4448 {
4449  global $langs;
4450 
4451  if ($titlealt == 'default') {
4452  $titlealt = $langs->trans('Show');
4453  }
4454 
4455  return img_picto($titlealt, 'pdf'.$size.'.png');
4456 }
4457 
4465 function img_edit_add($titlealt = 'default', $other = '')
4466 {
4467  global $langs;
4468 
4469  if ($titlealt == 'default') {
4470  $titlealt = $langs->trans('Add');
4471  }
4472 
4473  return img_picto($titlealt, 'edit_add.png', $other);
4474 }
4482 function img_edit_remove($titlealt = 'default', $other = '')
4483 {
4484  global $langs;
4485 
4486  if ($titlealt == 'default') {
4487  $titlealt = $langs->trans('Remove');
4488  }
4489 
4490  return img_picto($titlealt, 'edit_remove.png', $other);
4491 }
4492 
4501 function img_edit($titlealt = 'default', $float = 0, $other = '')
4502 {
4503  global $langs;
4504 
4505  if ($titlealt == 'default') {
4506  $titlealt = $langs->trans('Modify');
4507  }
4508 
4509  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4510 }
4511 
4520 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4521 {
4522  global $langs;
4523 
4524  if ($titlealt == 'default') {
4525  $titlealt = $langs->trans('View');
4526  }
4527 
4528  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4529 
4530  return img_picto($titlealt, 'eye', $moreatt);
4531 }
4532 
4541 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4542 {
4543  global $langs;
4544 
4545  if ($titlealt == 'default') {
4546  $titlealt = $langs->trans('Delete');
4547  }
4548 
4549  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4550 }
4551 
4559 function img_printer($titlealt = "default", $other = '')
4560 {
4561  global $langs;
4562  if ($titlealt == "default") {
4563  $titlealt = $langs->trans("Print");
4564  }
4565  return img_picto($titlealt, 'printer.png', $other);
4566 }
4567 
4575 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4576 {
4577  global $langs;
4578 
4579  if ($titlealt == 'default') {
4580  $titlealt = $langs->trans('Split');
4581  }
4582 
4583  return img_picto($titlealt, 'split.png', $other);
4584 }
4585 
4593 function img_help($usehelpcursor = 1, $usealttitle = 1)
4594 {
4595  global $langs;
4596 
4597  if ($usealttitle) {
4598  if (is_string($usealttitle)) {
4599  $usealttitle = dol_escape_htmltag($usealttitle);
4600  } else {
4601  $usealttitle = $langs->trans('Info');
4602  }
4603  }
4604 
4605  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4606 }
4607 
4614 function img_info($titlealt = 'default')
4615 {
4616  global $langs;
4617 
4618  if ($titlealt == 'default') {
4619  $titlealt = $langs->trans('Informations');
4620  }
4621 
4622  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4623 }
4624 
4633 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4634 {
4635  global $langs;
4636 
4637  if ($titlealt == 'default') {
4638  $titlealt = $langs->trans('Warning');
4639  }
4640 
4641  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4642  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4643 }
4644 
4651 function img_error($titlealt = 'default')
4652 {
4653  global $langs;
4654 
4655  if ($titlealt == 'default') {
4656  $titlealt = $langs->trans('Error');
4657  }
4658 
4659  return img_picto($titlealt, 'error.png');
4660 }
4661 
4669 function img_next($titlealt = 'default', $moreatt = '')
4670 {
4671  global $langs;
4672 
4673  if ($titlealt == 'default') {
4674  $titlealt = $langs->trans('Next');
4675  }
4676 
4677  //return img_picto($titlealt, 'next.png', $moreatt);
4678  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4679 }
4680 
4688 function img_previous($titlealt = 'default', $moreatt = '')
4689 {
4690  global $langs;
4691 
4692  if ($titlealt == 'default') {
4693  $titlealt = $langs->trans('Previous');
4694  }
4695 
4696  //return img_picto($titlealt, 'previous.png', $moreatt);
4697  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4698 }
4699 
4708 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4709 {
4710  global $langs;
4711 
4712  if ($titlealt == 'default') {
4713  $titlealt = $langs->trans('Down');
4714  }
4715 
4716  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4717 }
4718 
4727 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4728 {
4729  global $langs;
4730 
4731  if ($titlealt == 'default') {
4732  $titlealt = $langs->trans('Up');
4733  }
4734 
4735  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4736 }
4737 
4746 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4747 {
4748  global $langs;
4749 
4750  if ($titlealt == 'default') {
4751  $titlealt = $langs->trans('Left');
4752  }
4753 
4754  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4755 }
4756 
4765 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4766 {
4767  global $langs;
4768 
4769  if ($titlealt == 'default') {
4770  $titlealt = $langs->trans('Right');
4771  }
4772 
4773  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4774 }
4775 
4783 function img_allow($allow, $titlealt = 'default')
4784 {
4785  global $langs;
4786 
4787  if ($titlealt == 'default') {
4788  $titlealt = $langs->trans('Active');
4789  }
4790 
4791  if ($allow == 1) {
4792  return img_picto($titlealt, 'tick.png');
4793  }
4794 
4795  return '-';
4796 }
4797 
4805 function img_credit_card($brand, $morecss = null)
4806 {
4807  if (is_null($morecss)) {
4808  $morecss = 'fa-2x';
4809  }
4810 
4811  if ($brand == 'visa' || $brand == 'Visa') {
4812  $brand = 'cc-visa';
4813  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4814  $brand = 'cc-mastercard';
4815  } elseif ($brand == 'amex' || $brand == 'American Express') {
4816  $brand = 'cc-amex';
4817  } elseif ($brand == 'discover' || $brand == 'Discover') {
4818  $brand = 'cc-discover';
4819  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4820  $brand = 'cc-jcb';
4821  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4822  $brand = 'cc-diners-club';
4823  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4824  $brand = 'credit-card';
4825  }
4826 
4827  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4828 }
4829 
4838 function img_mime($file, $titlealt = '', $morecss = '')
4839 {
4840  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4841 
4842  $mimetype = dol_mimetype($file, '', 1);
4843  $mimeimg = dol_mimetype($file, '', 2);
4844  $mimefa = dol_mimetype($file, '', 4);
4845 
4846  if (empty($titlealt)) {
4847  $titlealt = 'Mime type: '.$mimetype;
4848  }
4849 
4850  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
4851  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
4852 }
4853 
4854 
4862 function img_search($titlealt = 'default', $other = '')
4863 {
4864  global $conf, $langs;
4865 
4866  if ($titlealt == 'default') {
4867  $titlealt = $langs->trans('Search');
4868  }
4869 
4870  $img = img_picto($titlealt, 'search.png', $other, false, 1);
4871 
4872  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
4873  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4874 
4875  return $input;
4876 }
4877 
4885 function img_searchclear($titlealt = 'default', $other = '')
4886 {
4887  global $conf, $langs;
4888 
4889  if ($titlealt == 'default') {
4890  $titlealt = $langs->trans('Search');
4891  }
4892 
4893  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
4894 
4895  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
4896  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4897 
4898  return $input;
4899 }
4900 
4912 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
4913 {
4914  global $conf, $langs;
4915 
4916  if ($infoonimgalt) {
4917  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
4918  } else {
4919  if (empty($conf->use_javascript_ajax)) {
4920  $textfordropdown = '';
4921  }
4922 
4923  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
4924  $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>');
4925 
4926  if ($textfordropdown) {
4927  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
4928  $tmpresult .= '<script type="text/javascript">
4929  jQuery(document).ready(function() {
4930  jQuery(".'.$class.'text").click(function() {
4931  console.log("toggle text");
4932  jQuery(".'.$class.'").toggle();
4933  });
4934  });
4935  </script>';
4936 
4937  $result = $tmpresult.$result;
4938  }
4939  }
4940 
4941  return $result;
4942 }
4943 
4944 
4956 function dol_print_error($db = '', $error = '', $errors = null)
4957 {
4958  global $conf, $langs, $argv;
4959  global $dolibarr_main_prod;
4960 
4961  $out = '';
4962  $syslog = '';
4963 
4964  // If error occurs before the $lang object was loaded
4965  if (!$langs) {
4966  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
4967  $langs = new Translate('', $conf);
4968  $langs->load("main");
4969  }
4970 
4971  // Load translation files required by the error messages
4972  $langs->loadLangs(array('main', 'errors'));
4973 
4974  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
4975  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
4976  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
4977  $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";
4978  }
4979  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
4980 
4981  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
4982  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
4983  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
4984  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
4985  }
4986  if (function_exists("phpversion")) {
4987  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
4988  }
4989  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
4990  if (function_exists("php_uname")) {
4991  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
4992  }
4993  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
4994  $out .= "<br>\n";
4995  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
4996  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
4997  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
4998  $out .= "<br>\n";
4999  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5000  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5001  } else // Mode CLI
5002  {
5003  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5004  $syslog .= "pid=".dol_getmypid();
5005  }
5006 
5007  if (!empty($conf->modules)) {
5008  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
5009  }
5010 
5011  if (is_object($db)) {
5012  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5013  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5014  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ? dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5015  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5016  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5017  $out .= "<br>\n";
5018  } else // Mode CLI
5019  {
5020  // No dol_escape_htmltag for output, we are in CLI mode
5021  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5022  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5023  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5024  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5025  }
5026  $syslog .= ", sql=".$db->lastquery();
5027  $syslog .= ", db_error=".$db->lasterror();
5028  }
5029 
5030  if ($error || $errors) {
5031  $langs->load("errors");
5032 
5033  // Merge all into $errors array
5034  if (is_array($error) && is_array($errors)) {
5035  $errors = array_merge($error, $errors);
5036  } elseif (is_array($error)) {
5037  $errors = $error;
5038  } elseif (is_array($errors)) {
5039  $errors = array_merge(array($error), $errors);
5040  } else {
5041  $errors = array_merge(array($error), array($errors));
5042  }
5043 
5044  foreach ($errors as $msg) {
5045  if (empty($msg)) {
5046  continue;
5047  }
5048  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5049  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5050  } else // Mode CLI
5051  {
5052  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5053  }
5054  $syslog .= ", msg=".$msg;
5055  }
5056  }
5057  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5058  xdebug_print_function_stack();
5059  $out .= '<b>XDebug informations:</b>'."<br>\n";
5060  $out .= 'File: '.xdebug_call_file()."<br>\n";
5061  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5062  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5063  $out .= "<br>\n";
5064  }
5065 
5066  // Return a http header with error code if possible
5067  if (!headers_sent()) {
5068  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5069  top_httphead();
5070  }
5071  http_response_code(500);
5072  }
5073 
5074  if (empty($dolibarr_main_prod)) {
5075  print $out;
5076  } else {
5077  if (empty($langs->defaultlang)) {
5078  $langs->setDefaultLang();
5079  }
5080  $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.
5081  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5082  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";
5083  print $langs->trans("DolibarrHasDetectedError").'. ';
5084  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5085  define("MAIN_CORE_ERROR", 1);
5086  }
5087 
5088  dol_syslog("Error ".$syslog, LOG_ERR);
5089 }
5090 
5101 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5102 {
5103  global $langs, $conf;
5104 
5105  if (empty($email)) {
5106  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
5107  }
5108 
5109  $langs->load("errors");
5110  $now = dol_now();
5111 
5112  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5113  print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S'));
5114  if ($errormessage) {
5115  print '<br><br>'.$errormessage;
5116  }
5117  if (is_array($errormessages) && count($errormessages)) {
5118  foreach ($errormessages as $mesgtoshow) {
5119  print '<br><br>'.$mesgtoshow;
5120  }
5121  }
5122  print '</div></div>';
5123 }
5124 
5141 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5142 {
5143  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5144 }
5145 
5164 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5165 {
5166  global $conf, $langs, $form;
5167  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5168 
5169  if ($moreattrib == 'class="right"') {
5170  $prefix .= 'right '; // For backward compatibility
5171  }
5172 
5173  $sortorder = strtoupper($sortorder);
5174  $out = '';
5175  $sortimg = '';
5176 
5177  $tag = 'th';
5178  if ($thead == 2) {
5179  $tag = 'div';
5180  }
5181 
5182  $tmpsortfield = explode(',', $sortfield);
5183  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5184  $tmpfield = explode(',', $field);
5185  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5186 
5187  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5188  $prefix = 'wrapcolumntitle '.$prefix;
5189  }
5190 
5191  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5192  // If field is used as sort criteria we use a specific css class liste_titre_sel
5193  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5194  $liste_titre = 'liste_titre';
5195  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5196  $liste_titre = 'liste_titre_sel';
5197  }
5198 
5199  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5200  //$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)).'"' : '');
5201  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5202  $tagstart .= '>';
5203 
5204  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5205  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5206  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5207  $options = preg_replace('/&+/i', '&', $options);
5208  if (!preg_match('/^&/', $options)) {
5209  $options = '&'.$options;
5210  }
5211 
5212  $sortordertouseinlink = '';
5213  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5214  if (preg_match('/^DESC/i', $sortorder)) {
5215  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5216  } else { // We reverse the var $sortordertouseinlink
5217  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5218  }
5219  } else { // We are on field that is the first current sorting criteria
5220  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5221  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5222  } else {
5223  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5224  }
5225  }
5226  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5227  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5228  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5229  $out .= '>';
5230  }
5231  if ($tooltip) {
5232  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
5233  if (preg_match('/:\w+$/', $tooltip)) {
5234  $tmptooltip = explode(':', $tooltip);
5235  } else {
5236  $tmptooltip = array($tooltip);
5237  }
5238  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5239  } else {
5240  $out .= $langs->trans($name);
5241  }
5242 
5243  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5244  $out .= '</a>';
5245  }
5246 
5247  if (empty($thead) && $field) { // If this is a sort field
5248  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5249  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5250  $options = preg_replace('/&+/i', '&', $options);
5251  if (!preg_match('/^&/', $options)) {
5252  $options = '&'.$options;
5253  }
5254 
5255  if (!$sortorder || $field1 != $sortfield1) {
5256  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5257  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5258  } else {
5259  if (preg_match('/^DESC/', $sortorder)) {
5260  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5261  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5262  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5263  }
5264  if (preg_match('/^ASC/', $sortorder)) {
5265  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5266  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5267  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5268  }
5269  }
5270  }
5271 
5272  $tagend = '</'.$tag.'>';
5273 
5274  $out = $tagstart.$sortimg.$out.$tagend;
5275 
5276  return $out;
5277 }
5278 
5287 function print_titre($title)
5288 {
5289  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5290 
5291  print '<div class="titre">'.$title.'</div>';
5292 }
5293 
5305 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5306 {
5307  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5308 }
5309 
5323 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5324 {
5325  global $conf;
5326 
5327  $return = '';
5328 
5329  if ($picto == 'setup') {
5330  $picto = 'generic';
5331  }
5332 
5333  $return .= "\n";
5334  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5335  $return .= '<tr class="titre">';
5336  if ($picto) {
5337  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5338  }
5339  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5340  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5341  $return .= '</td>';
5342  if (dol_strlen($morehtmlcenter)) {
5343  $return .= '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
5344  }
5345  if (dol_strlen($morehtmlright)) {
5346  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle">'.$morehtmlright.'</td>';
5347  }
5348  $return .= '</tr></table>'."\n";
5349 
5350  return $return;
5351 }
5352 
5376 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 = '')
5377 {
5378  global $conf, $langs;
5379 
5380  $savlimit = $limit;
5381  $savtotalnboflines = $totalnboflines;
5382  $totalnboflines = abs((int) $totalnboflines);
5383 
5384  if ($picto == 'setup') {
5385  $picto = 'title_setup.png';
5386  }
5387  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5388  $picto = 'title.gif';
5389  }
5390  if ($limit < 0) {
5391  $limit = $conf->liste_limit;
5392  }
5393  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5394  $nextpage = 1;
5395  } else {
5396  $nextpage = 0;
5397  }
5398  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
5399 
5400  print "\n";
5401  print "<!-- Begin title -->\n";
5402  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5403 
5404  // Left
5405 
5406  if ($picto && $titre) {
5407  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5408  }
5409  print '<td class="nobordernopadding valignmiddle col-title">';
5410  print '<div class="titre inline-block">'.$titre;
5411  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5412  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5413  }
5414  print '</div></td>';
5415 
5416  // Center
5417  if ($morehtmlcenter) {
5418  print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
5419  }
5420 
5421  // Right
5422  print '<td class="nobordernopadding valignmiddle right">';
5423  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5424  if ($sortfield) {
5425  $options .= "&sortfield=".urlencode($sortfield);
5426  }
5427  if ($sortorder) {
5428  $options .= "&sortorder=".urlencode($sortorder);
5429  }
5430  // Show navigation bar
5431  $pagelist = '';
5432  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5433  if ($totalnboflines) { // If we know total nb of lines
5434  // Define nb of extra page links before and after selected page + ... + first or last
5435  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5436 
5437  if ($limit > 0) {
5438  $nbpages = ceil($totalnboflines / $limit);
5439  } else {
5440  $nbpages = 1;
5441  }
5442  $cpt = ($page - $maxnbofpage);
5443  if ($cpt < 0) {
5444  $cpt = 0;
5445  }
5446 
5447  if ($cpt >= 1) {
5448  if (empty($pagenavastextinput)) {
5449  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5450  if ($cpt > 2) {
5451  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5452  } elseif ($cpt == 2) {
5453  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5454  }
5455  }
5456  }
5457 
5458  do {
5459  if ($pagenavastextinput) {
5460  if ($cpt == $page) {
5461  $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5462  $pagelist .= '/';
5463  }
5464  } else {
5465  if ($cpt == $page) {
5466  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5467  } else {
5468  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5469  }
5470  }
5471  $cpt++;
5472  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5473 
5474  if (empty($pagenavastextinput)) {
5475  if ($cpt < $nbpages) {
5476  if ($cpt < $nbpages - 2) {
5477  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5478  } elseif ($cpt == $nbpages - 2) {
5479  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5480  }
5481  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5482  }
5483  } else {
5484  //var_dump($page.' '.$cpt.' '.$nbpages);
5485  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5486  }
5487  } else {
5488  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5489  }
5490  }
5491 
5492  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5493  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
5494  }
5495 
5496  // js to autoselect page field on focus
5497  if ($pagenavastextinput) {
5498  print ajax_autoselect('.pageplusone');
5499  }
5500 
5501  print '</td>';
5502 
5503  print '</tr></table>'."\n";
5504  print "<!-- End title -->\n\n";
5505 }
5506 
5522 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '')
5523 {
5524  global $conf, $langs;
5525 
5526  print '<div class="pagination"><ul>';
5527  if ($beforearrows) {
5528  print '<li class="paginationbeforearrows">';
5529  print $beforearrows;
5530  print '</li>';
5531  }
5532  if ((int) $limit > 0 && empty($hideselectlimit)) {
5533  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5534  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5535  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5536  //$pagesizechoices.=',2:2';
5537  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5538  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5539  }
5540 
5541  print '<li class="pagination">';
5542  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5543  $tmpchoice = explode(',', $pagesizechoices);
5544  $tmpkey = $limit.':'.$limit;
5545  if (!in_array($tmpkey, $tmpchoice)) {
5546  $tmpchoice[] = $tmpkey;
5547  }
5548  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5549  if (!in_array($tmpkey, $tmpchoice)) {
5550  $tmpchoice[] = $tmpkey;
5551  }
5552  asort($tmpchoice, SORT_NUMERIC);
5553  foreach ($tmpchoice as $val) {
5554  $selected = '';
5555  $tmp = explode(':', $val);
5556  $key = $tmp[0];
5557  $val = $tmp[1];
5558  if ($key != '' && $val != '') {
5559  if ((int) $key == (int) $limit) {
5560  $selected = ' selected="selected"';
5561  }
5562  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5563  }
5564  }
5565  print '</select>';
5566  if ($conf->use_javascript_ajax) {
5567  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5568  <script>
5569  jQuery(document).ready(function () {
5570  jQuery(".selectlimit").change(function() {
5571  console.log("Change limit. Send submit");
5572  $(this).parents(\'form:first\').submit();
5573  });
5574  });
5575  </script>
5576  ';
5577  }
5578  print '</li>';
5579  }
5580  if ($page > 0) {
5581  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>';
5582  }
5583  if ($betweenarrows) {
5584  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5585  print $betweenarrows;
5586  print '<!--</div>-->';
5587  }
5588  if ($nextpage > 0) {
5589  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>';
5590  }
5591  if ($afterarrows) {
5592  print '<li class="paginationafterarrows">';
5593  print $afterarrows;
5594  print '</li>';
5595  }
5596  print '</ul></div>'."\n";
5597 }
5598 
5599 
5611 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5612 {
5613  $morelabel = '';
5614 
5615  if (preg_match('/%/', $rate)) {
5616  $rate = str_replace('%', '', $rate);
5617  $addpercent = true;
5618  }
5619  $reg = array();
5620  if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
5621  $morelabel = ' ('.$reg[1].')';
5622  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5623  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5624  }
5625  if (preg_match('/\*/', $rate)) {
5626  $rate = str_replace('*', '', $rate);
5627  $info_bits |= 1;
5628  }
5629 
5630  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5631  if (!preg_match('/\//', $rate)) {
5632  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5633  } else {
5634  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5635  $ret = $rate.($addpercent ? '%' : '');
5636  }
5637  if (($info_bits & 1) && $usestarfornpr >= 0) {
5638  $ret .= ' *';
5639  }
5640  $ret .= $morelabel;
5641  return $ret;
5642 }
5643 
5644 
5660 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5661 {
5662  global $langs, $conf;
5663 
5664  // Clean parameters
5665  if (empty($amount)) {
5666  $amount = 0; // To have a numeric value if amount not defined or = ''
5667  }
5668  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5669  if ($rounding < 0) {
5670  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5671  }
5672  $nbdecimal = $rounding;
5673 
5674  if ($outlangs === 'none') {
5675  // Use international separators
5676  $dec = '.';
5677  $thousand = '';
5678  } else {
5679  // Output separators by default (french)
5680  $dec = ',';
5681  $thousand = ' ';
5682 
5683  // If $outlangs not forced, we use use language
5684  if (!is_object($outlangs)) {
5685  $outlangs = $langs;
5686  }
5687 
5688  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5689  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5690  }
5691  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5692  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5693  }
5694  if ($thousand == 'None') {
5695  $thousand = '';
5696  } elseif ($thousand == 'Space') {
5697  $thousand = ' ';
5698  }
5699  }
5700  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5701 
5702  //print "amount=".$amount."-";
5703  $amount = str_replace(',', '.', $amount); // should be useless
5704  //print $amount."-";
5705  $datas = explode('.', $amount);
5706  $decpart = isset($datas[1]) ? $datas[1] : '';
5707  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5708  //print "decpart=".$decpart."<br>";
5709  $end = '';
5710 
5711  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5712  if (dol_strlen($decpart) > $nbdecimal) {
5713  $nbdecimal = dol_strlen($decpart);
5714  }
5715  // Si on depasse max
5716  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5717  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5718  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5719  // Si un affichage est tronque, on montre des ...
5720  $end = '...';
5721  }
5722  }
5723 
5724  // If force rounding
5725  if ((string) $forcerounding != '-1') {
5726  if ($forcerounding == 'MU') {
5727  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5728  } elseif ($forcerounding == 'MT') {
5729  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_TOT;
5730  } elseif ($forcerounding >= 0) {
5731  $nbdecimal = $forcerounding;
5732  }
5733  }
5734 
5735  // Format number
5736  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5737  if ($form) {
5738  $output = preg_replace('/\s/', '&nbsp;', $output);
5739  $output = preg_replace('/\'/', '&#039;', $output);
5740  }
5741  // Add symbol of currency if requested
5742  $cursymbolbefore = $cursymbolafter = '';
5743  if ($currency_code && is_object($outlangs)) {
5744  if ($currency_code == 'auto') {
5745  $currency_code = $conf->currency;
5746  }
5747 
5748  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC');
5749  $listoflanguagesbefore = array('nl_NL');
5750  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5751  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5752  } else {
5753  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5754  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5755  }
5756  }
5757  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5758 
5759  return $output;
5760 }
5761 
5786 function price2num($amount, $rounding = '', $option = 0)
5787 {
5788  global $langs, $conf;
5789 
5790  // Clean parameters
5791  if (is_null($amount)) {
5792  $amount = '';
5793  }
5794 
5795  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5796  // Numbers must be '1234.56'
5797  // Decimal delimiter for PHP and database SQL requests must be '.'
5798  $dec = ',';
5799  $thousand = ' ';
5800  if (is_null($langs)) { // $langs is not defined, we use english values.
5801  $dec = '.';
5802  $thousand = ',';
5803  } else {
5804  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5805  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5806  }
5807  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5808  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5809  }
5810  }
5811  if ($thousand == 'None') {
5812  $thousand = '';
5813  } elseif ($thousand == 'Space') {
5814  $thousand = ' ';
5815  }
5816  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5817 
5818  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5819  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5820  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5821  if (!is_numeric($amount)) {
5822  $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
5823  }
5824 
5825  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
5826  $amount = str_replace($thousand, '', $amount);
5827  }
5828 
5829  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5830  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
5831  // So if number was already a good number, it is converted into local Dolibarr setup.
5832  if (is_numeric($amount)) {
5833  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5834  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5835  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5836  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5837  $amount = number_format($amount, $nbofdec, $dec, $thousand);
5838  }
5839  //print "QQ".$amount."<br>\n";
5840 
5841  // Now make replace (the main goal of function)
5842  if ($thousand != ',' && $thousand != '.') {
5843  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5844  }
5845 
5846  $amount = str_replace(' ', '', $amount); // To avoid spaces
5847  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5848  $amount = str_replace($dec, '.', $amount);
5849 
5850  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5851  }
5852  //print ' XX'.$amount.' '.$rounding;
5853 
5854  // Now, $amount is a real PHP float number. We make a rounding if required.
5855  if ($rounding) {
5856  $nbofdectoround = '';
5857  if ($rounding == 'MU') {
5858  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5859  } elseif ($rounding == 'MT') {
5860  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
5861  } elseif ($rounding == 'MS') {
5862  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
5863  } elseif ($rounding == 'CU') {
5864  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
5865  } elseif ($rounding == 'CT') {
5866  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
5867  } elseif (is_numeric($rounding)) {
5868  $nbofdectoround = (int) $rounding;
5869  }
5870 
5871  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
5872  if (dol_strlen($nbofdectoround)) {
5873  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
5874  } else {
5875  return 'ErrorBadParameterProvidedToFunction';
5876  }
5877  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
5878 
5879  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5880  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
5881  if (is_numeric($amount)) {
5882  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5883  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5884  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5885  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5886  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
5887  }
5888  //print "TT".$amount.'<br>';
5889 
5890  // Always make replace because each math function (like round) replace
5891  // with local values and we want a number that has a SQL string format x.y
5892  if ($thousand != ',' && $thousand != '.') {
5893  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5894  }
5895 
5896  $amount = str_replace(' ', '', $amount); // To avoid spaces
5897  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5898  $amount = str_replace($dec, '.', $amount);
5899 
5900  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5901  }
5902 
5903  return $amount;
5904 }
5905 
5918 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
5919 {
5920  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
5921 
5922  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
5923  $dimension = $dimension * 1000000;
5924  $unit = $unit - 6;
5925  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
5926  $dimension = $dimension * 1000;
5927  $unit = $unit - 3;
5928  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
5929  $dimension = $dimension / 1000000;
5930  $unit = $unit + 6;
5931  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
5932  $dimension = $dimension / 1000;
5933  $unit = $unit + 3;
5934  }
5935  // Special case when we want output unit into pound or ounce
5936  /* TODO
5937  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
5938  {
5939  $dimension = // convert dimension from standard unit into ounce or pound
5940  $unit = $forceunitoutput;
5941  }
5942  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
5943  {
5944  $dimension = // convert dimension from standard unit into ounce or pound
5945  $unit = $forceunitoutput;
5946  }*/
5947 
5948  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
5949  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
5950 
5951  return $ret;
5952 }
5953 
5954 
5967 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
5968 {
5969  global $db, $conf, $mysoc;
5970 
5971  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
5972  $thirdparty_seller = $mysoc;
5973  }
5974 
5975  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);
5976 
5977  $vatratecleaned = $vatrate;
5978  $reg = array();
5979  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
5980  $vatratecleaned = trim($reg[1]);
5981  $vatratecode = $reg[2];
5982  }
5983 
5984  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
5985  {
5986  return 0;
5987  }*/
5988 
5989  // Some test to guess with no need to make database access
5990  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
5991  if ($local == 1) {
5992  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
5993  return 0;
5994  }
5995  if ($thirdparty_seller->id == $mysoc->id) {
5996  if (!$thirdparty_buyer->localtax1_assuj) {
5997  return 0;
5998  }
5999  } else {
6000  if (!$thirdparty_seller->localtax1_assuj) {
6001  return 0;
6002  }
6003  }
6004  }
6005 
6006  if ($local == 2) {
6007  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6008  if (!$mysoc->localtax2_assuj) {
6009  return 0; // If main vat is 0, IRPF may be different than 0.
6010  }
6011  if ($thirdparty_seller->id == $mysoc->id) {
6012  if (!$thirdparty_buyer->localtax2_assuj) {
6013  return 0;
6014  }
6015  } else {
6016  if (!$thirdparty_seller->localtax2_assuj) {
6017  return 0;
6018  }
6019  }
6020  }
6021  } else {
6022  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6023  return 0;
6024  }
6025  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6026  return 0;
6027  }
6028  }
6029 
6030  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6031  if (in_array($mysoc->country_code, array('ES'))) {
6032  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6033  }
6034 
6035  // Search local taxes
6036  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
6037  if ($local == 1) {
6038  if ($thirdparty_seller != $mysoc) {
6039  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6040  return $thirdparty_seller->localtax1_value;
6041  }
6042  } else { // i am the seller
6043  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6044  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6045  }
6046  }
6047  }
6048  if ($local == 2) {
6049  if ($thirdparty_seller != $mysoc) {
6050  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6051  // TODO We should also return value defined on thirdparty only if defined
6052  return $thirdparty_seller->localtax2_value;
6053  }
6054  } else { // i am the seller
6055  if (in_array($mysoc->country_code, array('ES'))) {
6056  return $thirdparty_buyer->localtax2_value;
6057  } else {
6058  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6059  }
6060  }
6061  }
6062  }
6063 
6064  // By default, search value of local tax on line of common tax
6065  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6066  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6067  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6068  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6069  if (!empty($vatratecode)) {
6070  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6071  } else {
6072  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
6073  }
6074 
6075  $resql = $db->query($sql);
6076 
6077  if ($resql) {
6078  $obj = $db->fetch_object($resql);
6079  if ($obj) {
6080  if ($local == 1) {
6081  return $obj->localtax1;
6082  } elseif ($local == 2) {
6083  return $obj->localtax2;
6084  }
6085  }
6086  }
6087 
6088  return 0;
6089 }
6090 
6091 
6100 function isOnlyOneLocalTax($local)
6101 {
6102  $tax = get_localtax_by_third($local);
6103 
6104  $valors = explode(":", $tax);
6105 
6106  if (count($valors) > 1) {
6107  return false;
6108  } else {
6109  return true;
6110  }
6111 }
6112 
6119 function get_localtax_by_third($local)
6120 {
6121  global $db, $mysoc;
6122  $sql = "SELECT t.localtax1, t.localtax2 ";
6123  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
6124  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux=(";
6125  $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";
6126  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1";
6127  $sql .= " )";
6128 
6129  $resql = $db->query($sql);
6130  if ($resql) {
6131  $obj = $db->fetch_object($resql);
6132  if ($local == 1) {
6133  return $obj->localtax1;
6134  } elseif ($local == 2) {
6135  return $obj->localtax2;
6136  }
6137  }
6138 
6139  return 0;
6140 }
6141 
6142 
6154 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6155 {
6156  global $db, $mysoc;
6157 
6158  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6159 
6160  // Search local taxes
6161  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6162  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6163  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6164  if ($firstparamisid) {
6165  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6166  } else {
6167  $vatratecleaned = $vatrate;
6168  $vatratecode = '';
6169  $reg = array();
6170  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6171  $vatratecleaned = $reg[1];
6172  $vatratecode = $reg[2];
6173  }
6174 
6175  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6176  /*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 ??
6177  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6178  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6179  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6180  if ($vatratecode) {
6181  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6182  }
6183  }
6184 
6185  $resql = $db->query($sql);
6186  if ($resql) {
6187  $obj = $db->fetch_object($resql);
6188  if ($obj) {
6189  return array(
6190  'rowid'=>$obj->rowid,
6191  'code'=>$obj->code,
6192  'rate'=>$obj->rate,
6193  'localtax1'=>$obj->localtax1,
6194  'localtax1_type'=>$obj->localtax1_type,
6195  'localtax2'=>$obj->localtax2,
6196  'localtax2_type'=>$obj->localtax2_type,
6197  'npr'=>$obj->npr,
6198  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6199  'accountancy_code_buy'=>$obj->accountancy_code_buy
6200  );
6201  } else {
6202  return array();
6203  }
6204  } else {
6205  dol_print_error($db);
6206  }
6207 
6208  return array();
6209 }
6210 
6227 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6228 {
6229  global $db, $mysoc;
6230 
6231  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6232 
6233  // Search local taxes
6234  $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";
6235  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6236  if ($firstparamisid) {
6237  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6238  } else {
6239  $vatratecleaned = $vatrate;
6240  $vatratecode = '';
6241  $reg = array();
6242  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6243  $vatratecleaned = $reg[1];
6244  $vatratecode = $reg[2];
6245  }
6246 
6247  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6248  if ($mysoc->country_code == 'ES') {
6249  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ??
6250  } else {
6251  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape(empty($seller->country_code) ? $mysoc->country_code : $seller->country_code)."'";
6252  }
6253  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6254  if ($vatratecode) {
6255  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6256  }
6257  }
6258 
6259  $resql = $db->query($sql);
6260  if ($resql) {
6261  $obj = $db->fetch_object($resql);
6262 
6263  if ($obj) {
6264  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6265 
6266  if ($local == 1) {
6267  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6268  } elseif ($local == 2) {
6269  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6270  } else {
6271  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);
6272  }
6273  }
6274  }
6275 
6276  return array();
6277 }
6278 
6289 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6290 {
6291  global $db, $conf, $mysoc;
6292 
6293  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6294 
6295  $ret = 0;
6296  $found = 0;
6297 
6298  if ($idprod > 0) {
6299  // Load product
6300  $product = new Product($db);
6301  $product->fetch($idprod);
6302 
6303  if ($mysoc->country_code == $thirdpartytouse->country_code) {
6304  // If country to consider is ours
6305  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6306  $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
6307  if ($result > 0) {
6308  $ret = $product->vatrate_supplier;
6309  if ($product->default_vat_code_supplier) {
6310  $ret .= ' ('.$product->default_vat_code_supplier.')';
6311  }
6312  $found = 1;
6313  }
6314  }
6315  if (!$found) {
6316  $ret = $product->tva_tx; // Default sales vat of product
6317  if ($product->default_vat_code) {
6318  $ret .= ' ('.$product->default_vat_code.')';
6319  }
6320  $found = 1;
6321  }
6322  } else {
6323  // TODO Read default product vat according to product and another countrycode.
6324  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6325  }
6326  }
6327 
6328  if (!$found) {
6329  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6330  // If vat of product for the country not found or not defined, we return the first rate found (sorting on use_default, then on higher vat of country).
6331  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6332  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6333  $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
6334  $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
6335  $sql .= $db->plimit(1);
6336 
6337  $resql = $db->query($sql);
6338  if ($resql) {
6339  $obj = $db->fetch_object($resql);
6340  if ($obj) {
6341  $ret = $obj->vat_rate;
6342  if ($obj->default_vat_code) {
6343  $ret .= ' ('.$obj->default_vat_code.')';
6344  }
6345  }
6346  $db->free($resql);
6347  } else {
6348  dol_print_error($db);
6349  }
6350  } else {
6351  // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
6352  // '1.23'
6353  // or '1.23 (CODE)'
6354  $defaulttx = '';
6355  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6356  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6357  }
6358  /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6359  $defaultcode = $reg[1];
6360  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6361  }*/
6362 
6363  $ret = $defaulttx;
6364  }
6365  }
6366 
6367  dol_syslog("get_product_vat_for_country: ret=".$ret);
6368  return $ret;
6369 }
6370 
6380 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6381 {
6382  global $db, $mysoc;
6383 
6384  if (!class_exists('Product')) {
6385  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6386  }
6387 
6388  $ret = 0;
6389  $found = 0;
6390 
6391  if ($idprod > 0) {
6392  // Load product
6393  $product = new Product($db);
6394  $result = $product->fetch($idprod);
6395 
6396  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6397  /* Not defined yet, so we don't use this
6398  if ($local==1) $ret=$product->localtax1_tx;
6399  elseif ($local==2) $ret=$product->localtax2_tx;
6400  $found=1;
6401  */
6402  } else {
6403  // TODO Read default product vat according to product and another countrycode.
6404  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6405  }
6406  }
6407 
6408  if (!$found) {
6409  // If vat of product for the country not found or not defined, we return higher vat of country.
6410  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6411  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6412  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6413  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6414  $sql .= $db->plimit(1);
6415 
6416  $resql = $db->query($sql);
6417  if ($resql) {
6418  $obj = $db->fetch_object($resql);
6419  if ($obj) {
6420  if ($local == 1) {
6421  $ret = $obj->localtax1;
6422  } elseif ($local == 2) {
6423  $ret = $obj->localtax2;
6424  }
6425  }
6426  } else {
6427  dol_print_error($db);
6428  }
6429  }
6430 
6431  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6432  return $ret;
6433 }
6434 
6451 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6452 {
6453  global $conf;
6454 
6455  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6456 
6457  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6458  $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;
6459 
6460  $seller_country_code = $thirdparty_seller->country_code;
6461  $seller_in_cee = isInEEC($thirdparty_seller);
6462 
6463  $buyer_country_code = $thirdparty_buyer->country_code;
6464  $buyer_in_cee = isInEEC($thirdparty_buyer);
6465 
6466  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 : ''));
6467 
6468  // 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)
6469  // we use the buyer VAT.
6470  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6471  if ($seller_in_cee && $buyer_in_cee) {
6472  $isacompany = $thirdparty_buyer->isACompany();
6473  if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) {
6474  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6475  if (!isValidVATID($thirdparty_buyer)) {
6476  $isacompany = 0;
6477  }
6478  }
6479 
6480  if (!$isacompany) {
6481  //print 'VATRULE 0';
6482  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6483  }
6484  }
6485  }
6486 
6487  // If seller does not use VAT
6488  if (!$seller_use_vat) {
6489  //print 'VATRULE 1';
6490  return 0;
6491  }
6492 
6493  // 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.
6494 
6495  // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
6496  if (($seller_country_code == $buyer_country_code)
6497  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6498  //print 'VATRULE 2';
6499  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6500  }
6501 
6502  // 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.
6503  // 'VATRULE 3' - Not supported
6504 
6505  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
6506  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6507  if (($seller_in_cee && $buyer_in_cee)) {
6508  $isacompany = $thirdparty_buyer->isACompany();
6509  if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) {
6510  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6511  if (!isValidVATID($thirdparty_buyer)) {
6512  $isacompany = 0;
6513  }
6514  }
6515 
6516  if (!$isacompany) {
6517  //print 'VATRULE 4';
6518  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6519  } else {
6520  //print 'VATRULE 5';
6521  return 0;
6522  }
6523  }
6524 
6525  // Si (vendeur dans Communaute europeene et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6526  // I don't see any use case that need this rule.
6527  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6528  $isacompany = $thirdparty_buyer->isACompany();
6529  if (!$isacompany) {
6530  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6531  //print 'VATRULE extra';
6532  }
6533  }
6534 
6535  // Sinon la TVA proposee par defaut=0. Fin de regle.
6536  // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
6537  //print 'VATRULE 6';
6538  return 0;
6539 }
6540 
6541 
6552 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6553 {
6554  global $db;
6555 
6556  if ($idprodfournprice > 0) {
6557  if (!class_exists('ProductFournisseur')) {
6558  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6559  }
6560  $prodprice = new ProductFournisseur($db);
6561  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6562  return $prodprice->fourn_tva_npr;
6563  } elseif ($idprod > 0) {
6564  if (!class_exists('Product')) {
6565  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6566  }
6567  $prod = new Product($db);
6568  $prod->fetch($idprod);
6569  return $prod->tva_npr;
6570  }
6571 
6572  return 0;
6573 }
6574 
6588 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6589 {
6590  global $mysoc;
6591 
6592  if (!is_object($thirdparty_seller)) {
6593  return -1;
6594  }
6595  if (!is_object($thirdparty_buyer)) {
6596  return -1;
6597  }
6598 
6599  if ($local == 1) { // Localtax 1
6600  if ($mysoc->country_code == 'ES') {
6601  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6602  return 0;
6603  }
6604  } else {
6605  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6606  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6607  return 0;
6608  }
6609  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6610  return 0;
6611  }
6612  }
6613  } elseif ($local == 2) { //I Localtax 2
6614  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6615  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6616  return 0;
6617  }
6618  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6619  return 0;
6620  }
6621  }
6622 
6623  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6624  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6625  }
6626 
6627  return 0;
6628 }
6629 
6638 function yn($yesno, $case = 1, $color = 0)
6639 {
6640  global $langs;
6641 
6642  $result = 'unknown';
6643  $classname = '';
6644  if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') { // A mettre avant test sur no a cause du == 0
6645  $result = $langs->trans('yes');
6646  if ($case == 1 || $case == 3) {
6647  $result = $langs->trans("Yes");
6648  }
6649  if ($case == 2) {
6650  $result = '<input type="checkbox" value="1" checked disabled>';
6651  }
6652  if ($case == 3) {
6653  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6654  }
6655 
6656  $classname = 'ok';
6657  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6658  $result = $langs->trans("no");
6659  if ($case == 1 || $case == 3) {
6660  $result = $langs->trans("No");
6661  }
6662  if ($case == 2) {
6663  $result = '<input type="checkbox" value="0" disabled>';
6664  }
6665  if ($case == 3) {
6666  $result = '<input type="checkbox" value="0" disabled> '.$result;
6667  }
6668 
6669  if ($color == 2) {
6670  $classname = 'ok';
6671  } else {
6672  $classname = 'error';
6673  }
6674  }
6675  if ($color) {
6676  return '<span class="'.$classname.'">'.$result.'</span>';
6677  }
6678  return $result;
6679 }
6680 
6696 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6697 {
6698  global $conf;
6699 
6700  if (empty($modulepart) && !empty($object->module)) {
6701  $modulepart = $object->module;
6702  }
6703 
6704  $path = '';
6705 
6706  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6707  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
6708  $arrayforoldpath[] = 'product';
6709  }
6710  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6711  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6712  if (empty($alpha)) {
6713  $num = preg_replace('/([^0-9])/i', '', $num);
6714  } else {
6715  $num = preg_replace('/^.*\-/i', '', $num);
6716  }
6717  $num = substr("000".$num, -$level);
6718  if ($level == 1) {
6719  $path = substr($num, 0, 1);
6720  }
6721  if ($level == 2) {
6722  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6723  }
6724  if ($level == 3) {
6725  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6726  }
6727  } else {
6728  // We will enhance here a common way of forging path for document storage.
6729  // In a future, we may distribute directories on several levels depending on setup and object.
6730  // Here, $object->id, $object->ref and $modulepart are required.
6731  //var_dump($modulepart);
6732  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6733  }
6734 
6735  if (empty($withoutslash) && !empty($path)) {
6736  $path .= '/';
6737  }
6738 
6739  return $path;
6740 }
6741 
6750 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6751 {
6752  global $conf;
6753 
6754  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6755 
6756  $dir_osencoded = dol_osencode($dir);
6757  if (@is_dir($dir_osencoded)) {
6758  return 0;
6759  }
6760 
6761  $nberr = 0;
6762  $nbcreated = 0;
6763 
6764  $ccdir = '';
6765  if (!empty($dataroot)) {
6766  // Remove data root from loop
6767  $dir = str_replace($dataroot.'/', '', $dir);
6768  $ccdir = $dataroot.'/';
6769  }
6770 
6771  $cdir = explode("/", $dir);
6772  $num = count($cdir);
6773  for ($i = 0; $i < $num; $i++) {
6774  if ($i > 0) {
6775  $ccdir .= '/'.$cdir[$i];
6776  } else {
6777  $ccdir .= $cdir[$i];
6778  }
6779  if (preg_match("/^.:$/", $ccdir, $regs)) {
6780  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6781  }
6782 
6783  // Attention, le is_dir() peut echouer bien que le rep existe.
6784  // (ex selon config de open_basedir)
6785  if ($ccdir) {
6786  $ccdir_osencoded = dol_osencode($ccdir);
6787  if (!@is_dir($ccdir_osencoded)) {
6788  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6789 
6790  umask(0);
6791  $dirmaskdec = octdec((string) $newmask);
6792  if (empty($newmask)) {
6793  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6794  }
6795  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
6796  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
6797  // Si le is_dir a renvoye une fausse info, alors on passe ici.
6798  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
6799  $nberr++;
6800  } else {
6801  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
6802  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
6803  $nbcreated++;
6804  }
6805  } else {
6806  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
6807  }
6808  }
6809  }
6810  return ($nberr ? -$nberr : $nbcreated);
6811 }
6812 
6813 
6819 function picto_required()
6820 {
6821  return '<span class="fieldrequired">*</span>';
6822 }
6823 
6824 
6841 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
6842 {
6843  if (is_null($stringtoclean)) {
6844  return '';
6845  }
6846 
6847  if ($removelinefeed == 2) {
6848  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
6849  }
6850  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
6851 
6852  // 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)
6853  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
6854 
6855  $temp = str_replace('< ', '__ltspace__', $temp);
6856 
6857  if ($strip_tags) {
6858  $temp = strip_tags($temp);
6859  } else {
6860  // 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).
6861  $pattern = "/<[^<>]+>/";
6862  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
6863  // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
6864  // pass 2 - $temp after pass 2: 0000-021
6865  $tempbis = $temp;
6866  do {
6867  $temp = $tempbis;
6868  $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
6869  $tempbis = preg_replace($pattern, '', $tempbis);
6870  //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
6871  } while ($tempbis != $temp);
6872 
6873  $temp = $tempbis;
6874 
6875  // 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).
6876  $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
6877  }
6878 
6879  $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
6880 
6881  // Remove also carriage returns
6882  if ($removelinefeed == 1) {
6883  $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
6884  }
6885 
6886  // And double quotes
6887  if ($removedoublespaces) {
6888  while (strpos($temp, " ")) {
6889  $temp = str_replace(" ", " ", $temp);
6890  }
6891  }
6892 
6893  $temp = str_replace('__ltspace__', '< ', $temp);
6894 
6895  return trim($temp);
6896 }
6897 
6912 function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array())
6913 {
6914  if (empty($allowed_tags)) {
6915  $allowed_tags = array(
6916  "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link",
6917  "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"
6918  );
6919  }
6920  $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
6921  if ($allowiframe) {
6922  $allowed_tags[] = "iframe";
6923  }
6924 
6925  $allowed_tags_string = join("><", $allowed_tags);
6926  $allowed_tags_string = '<'.$allowed_tags_string.'>';
6927 
6928  $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
6929 
6930  $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
6931 
6932  //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
6933  $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
6934 
6935  $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
6936  $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
6937 
6938  $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired </>, so may changes string obfuscated with </> that pass the injection detection into a harmfull string
6939 
6940  if ($cleanalsosomestyles) { // Clean for remaining html tags
6941  $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
6942  }
6943  if ($removeclassattribute) { // Clean for remaining html tags
6944  $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
6945  }
6946 
6947  // Remove 'javascript:' that we should not find into a text with
6948  // 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)).
6949  if ($cleanalsojavascript) {
6950  $temp = preg_replace('/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*:/i', '', $temp);
6951  }
6952 
6953  $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
6954 
6955  $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
6956 
6957 
6958  return $temp;
6959 }
6960 
6961 
6973 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"))
6974 {
6975  if (class_exists('DOMDocument') && !empty($stringtoclean)) {
6976  $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
6977 
6978  $dom = new DOMDocument(null, 'UTF-8');
6979  $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
6980 
6981  if (is_object($dom)) {
6982  for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
6983  for ($attrs = $els->item($i)->attributes, $ii = $attrs->length - 1; $ii >= 0; $ii--) {
6984  //var_dump($attrs->item($ii));
6985  if (!empty($attrs->item($ii)->name)) {
6986  if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
6987  // Delete attribute if not into allowed_attributes
6988  $els->item($i)->removeAttribute($attrs->item($ii)->name);
6989  } elseif (in_array($attrs->item($ii)->name, array('style'))) {
6990  // If attribute is 'style'
6991  $valuetoclean = $attrs->item($ii)->value;
6992 
6993  if (isset($valuetoclean)) {
6994  do {
6995  $oldvaluetoclean = $valuetoclean;
6996  $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
6997  $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
6998  if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
6999  $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
7000  $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
7001  $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
7002  }
7003 
7004  // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
7005  $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
7006  $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
7007  } while ($oldvaluetoclean != $valuetoclean);
7008  }
7009 
7010  $attrs->item($ii)->value = $valuetoclean;
7011  }
7012  }
7013  }
7014  }
7015  }
7016 
7017  $return = $dom->saveHTML();
7018  //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
7019 
7020  $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
7021  $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
7022  $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
7023  return trim($return);
7024  } else {
7025  return $stringtoclean;
7026  }
7027 }
7028 
7040 function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
7041 {
7042  $temp = $stringtoclean;
7043  foreach ($disallowed_tags as $tagtoremove) {
7044  $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
7045  $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
7046  }
7047 
7048  if ($cleanalsosomestyles) {
7049  $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
7050  }
7051 
7052  return $temp;
7053 }
7054 
7055 
7065 function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
7066 {
7067  if ($nboflines == 1) {
7068  if (dol_textishtml($text)) {
7069  $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
7070  $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
7071  } else {
7072  $firstline = preg_replace('/[\n\r].*/', '', $text);
7073  }
7074  return $firstline.((strlen($firstline) != strlen($text)) ? '...' : '');
7075  } else {
7076  $ishtml = 0;
7077  if (dol_textishtml($text)) {
7078  $text = preg_replace('/\n/', '', $text);
7079  $ishtml = 1;
7080  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7081  } else {
7082  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7083  }
7084 
7085  $text = strtr($text, $repTable);
7086  if ($charset == 'UTF-8') {
7087  $pattern = '/(<br[^>]*>)/Uu';
7088  } else {
7089  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7090  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7091  }
7092  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7093 
7094  $firstline = '';
7095  $i = 0;
7096  $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
7097  while (($i < $nba) && ($i < ($nboflines * 2))) {
7098  if ($i % 2 == 0) {
7099  $firstline .= $a[$i];
7100  } elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) {
7101  $firstline .= ($ishtml ? "<br>\n" : "\n");
7102  }
7103  $i++;
7104  }
7105  unset($a);
7106  return $firstline.(($i < $nba) ? '...' : '');
7107  }
7108 }
7109 
7110 
7121 function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
7122 {
7123  if (!$nl2brmode) {
7124  return nl2br($stringtoencode, $forxml);
7125  } else {
7126  $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
7127  return $ret;
7128  }
7129 }
7130 
7139 function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
7140 {
7141  global $conf;
7142 
7143  if (empty($nouseofiframesandbox) && !empty($conf->global->MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS)) {
7144  // TODO using sandbox on inline html content is not possible yet with current browsers
7145  //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
7146  //$s .= $stringtoencode;
7147  //$s .= '</body></html></iframe>';
7148  return $stringtoencode;
7149  } else {
7150  $out = $stringtoencode;
7151 
7152  do {
7153  $oldstringtoclean = $out;
7154 
7155  if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') {
7156  try {
7157  $dom = new DOMDocument;
7158  // Add a trick to solve pb with text without parent tag
7159  // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up without the trick into '<h1>Foo<p>bar</p></h1>'
7160  // like 'abc' that wrongly ends up without the tric into with '<p>abc</p>'
7161  $out = '<div class="tricktoremove">'.$out.'</div>';
7162 
7163  $dom->loadHTML($out, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
7164  $out = trim($dom->saveHTML());
7165 
7166  // Remove the trick added to solve pb with text without parent tag
7167  $out = preg_replace('/^<div class="tricktoremove">/', '', $out);
7168  $out = preg_replace('/<\/div>$/', '', $out);
7169  } catch (Exception $e) {
7170  // If error, invalid HTML string with no way to clean it
7171  //print $e->getMessage();
7172  $out = 'InvalidHTMLString';
7173  }
7174  }
7175 
7176  // Clean some html entities that are useless so text is cleaner
7177  $out = preg_replace('/&(tab|newline);/i', ' ', $out);
7178 
7179  // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are
7180  // encoded using text entities) so we can then exclude all numeric entities.
7181  $out = preg_replace('/&#39;/i', '&apos;', $out);
7182 
7183  // 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).
7184  // 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
7185  // using a non coventionnel way to be encoded, to not have them sanitized just after)
7186  $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', function ($m) {
7187  return realCharForNumericEntities($m); }, $out);
7188 
7189 
7190  // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
7191  $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'.
7192 
7193  // Keep only some html tags and remove also some 'javascript:' strings
7194  $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
7195 
7196  // We should also exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
7197  if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) {
7198  // 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.
7200  }
7201 
7202  // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
7203  $out = preg_replace('/&apos;/i', "&#39;", $out);
7204  } while ($oldstringtoclean != $out);
7205 
7206  // Check the limit of external links in a Rich text content. We count '<img' and 'url('
7207  $reg = array();
7208  preg_match_all('/(<img|url\‍()/i', $out, $reg);
7209  if (count($reg[0]) > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
7210  $out = 'TooManyLinksIntoHTMLString';
7211  }
7212 
7213  return $out;
7214  }
7215 }
7216 
7234 function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
7235 {
7236  if (is_null($stringtoencode)) {
7237  return '';
7238  }
7239 
7240  $newstring = $stringtoencode;
7241  if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
7242  $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.
7243  if ($removelasteolbr) {
7244  $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
7245  }
7246  $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__'));
7247  $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
7248  $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"'));
7249  } else {
7250  if ($removelasteolbr) {
7251  $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
7252  }
7253  $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
7254  }
7255  // Other substitutions that htmlentities does not do
7256  //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
7257  return $newstring;
7258 }
7259 
7267 function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
7268 {
7269  $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7270  $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
7271  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
7272  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
7273  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
7274  return $ret;
7275 }
7276 
7283 function dol_htmlcleanlastbr($stringtodecode)
7284 {
7285  $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
7286  $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
7287  return $ret;
7288 }
7289 
7299 function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
7300 {
7301  $newstring = $a;
7302  if ($keepsomeentities) {
7303  $newstring = strtr($newstring, array('&amp;'=>'__andamp__', '&lt;'=>'__andlt__', '&gt;'=>'__andgt__', '"'=>'__dquot__'));
7304  }
7305  $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
7306  if ($keepsomeentities) {
7307  $newstring = strtr($newstring, array('__andamp__'=>'&amp;', '__andlt__'=>'&lt;', '__andgt__'=>'&gt;', '__dquot__'=>'"'));
7308  }
7309  return $newstring;
7310 }
7311 
7322 function dol_htmlentities($string, $flags = ENT_QUOTES|ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
7323 {
7324  return htmlentities($string, $flags, $encoding, $double_encode);
7325 }
7326 
7336 function dol_string_is_good_iso($s, $clean = 0)
7337 {
7338  $len = dol_strlen($s);
7339  $out = '';
7340  $ok = 1;
7341  for ($scursor = 0; $scursor < $len; $scursor++) {
7342  $ordchar = ord($s[$scursor]);
7343  //print $scursor.'-'.$ordchar.'<br>';
7344  if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
7345  $ok = 0;
7346  break;
7347  } elseif ($ordchar > 126 && $ordchar < 160) {
7348  $ok = 0;
7349  break;
7350  } elseif ($clean) {
7351  $out .= $s[$scursor];
7352  }
7353  }
7354  if ($clean) {
7355  return $out;
7356  }
7357  return $ok;
7358 }
7359 
7368 function dol_nboflines($s, $maxchar = 0)
7369 {
7370  if ($s == '') {
7371  return 0;
7372  }
7373  $arraystring = explode("\n", $s);
7374  $nb = count($arraystring);
7375 
7376  return $nb;
7377 }
7378 
7379 
7389 function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
7390 {
7391  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7392  if (dol_textishtml($text)) {
7393  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7394  }
7395 
7396  $text = strtr($text, $repTable);
7397  if ($charset == 'UTF-8') {
7398  $pattern = '/(<br[^>]*>)/Uu';
7399  } else {
7400  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7401  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7402  }
7403  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7404 
7405  $nblines = (int) floor((count($a) + 1) / 2);
7406  // count possible auto line breaks
7407  if ($maxlinesize) {
7408  foreach ($a as $line) {
7409  if (dol_strlen($line) > $maxlinesize) {
7410  //$line_dec = html_entity_decode(strip_tags($line));
7411  $line_dec = html_entity_decode($line);
7412  if (dol_strlen($line_dec) > $maxlinesize) {
7413  $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
7414  $nblines += substr_count($line_dec, '\n');
7415  }
7416  }
7417  }
7418  }
7419 
7420  unset($a);
7421  return $nblines;
7422 }
7423 
7432 function dol_textishtml($msg, $option = 0)
7433 {
7434  if (is_null($msg)) {
7435  return false;
7436  }
7437 
7438  if ($option == 1) {
7439  if (preg_match('/<html/i', $msg)) {
7440  return true;
7441  } elseif (preg_match('/<body/i', $msg)) {
7442  return true;
7443  } elseif (preg_match('/<\/textarea/i', $msg)) {
7444  return true;
7445  } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) {
7446  return true;
7447  } elseif (preg_match('/<br/i', $msg)) {
7448  return true;
7449  }
7450  return false;
7451  } else {
7452  // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
7453  $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
7454  if (preg_match('/<html/i', $msg)) {
7455  return true;
7456  } elseif (preg_match('/<body/i', $msg)) {
7457  return true;
7458  } elseif (preg_match('/<\/textarea/i', $msg)) {
7459  return true;
7460  } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) {
7461  return true;
7462  } elseif (preg_match('/<br\/>/i', $msg)) {
7463  return true;
7464  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg)) {
7465  return true;
7466  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
7467  return true;
7468  } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
7469  return true; // must accept <img src="http://example.com/aaa.png" />
7470  } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
7471  return true; // must accept <a href="http://example.com/aaa.png" />
7472  } elseif (preg_match('/<h[0-9]>/i', $msg)) {
7473  return true;
7474  } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
7475  // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
7476  return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
7477  } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
7478  return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
7479  }
7480 
7481  return false;
7482  }
7483 }
7484 
7499 function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
7500 {
7501  if (!empty($invert)) {
7502  $tmp = $text1;
7503  $text1 = $text2;
7504  $text2 = $tmp;
7505  }
7506 
7507  $ret = '';
7508  $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
7509  $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
7510  $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
7511  return $ret;
7512 }
7513 
7514 
7515 
7526 function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
7527 {
7528  global $db, $conf, $mysoc, $user, $extrafields;
7529 
7530  $substitutionarray = array();
7531 
7532  if (empty($exclude) || !in_array('user', $exclude)) {
7533  // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
7534  // this will include signature content first and then replace var found into content of signature
7535  //var_dump($onlykey);
7536  $emailsendersignature = $user->signature; // dy default, we use the signature of current user. We must complete substitution with signature in c_email_senderprofile of array after calling getCommonSubstitutionArray()
7537  $usersignature = $user->signature;
7538  $substitutionarray = array_merge($substitutionarray, array(
7539  '__SENDEREMAIL_SIGNATURE__' => (string) ((empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
7540  '__USER_SIGNATURE__' => (string) (($usersignature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
7541  ));
7542 
7543  if (is_object($user)) {
7544  $substitutionarray = array_merge($substitutionarray, array(
7545  '__USER_ID__' => (string) $user->id,
7546  '__USER_LOGIN__' => (string) $user->login,
7547  '__USER_EMAIL__' => (string) $user->email,
7548  '__USER_PHONE__' => (string) dol_print_phone($user->office_phone),
7549  '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile),
7550  '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile),
7551  '__USER_FAX__' => (string) $user->office_fax,
7552  '__USER_LASTNAME__' => (string) $user->lastname,
7553  '__USER_FIRSTNAME__' => (string) $user->firstname,
7554  '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
7555  '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
7556  '__USER_JOB__' => (string) $user->job,
7557  '__USER_REMOTE_IP__' => (string) getUserRemoteIP()
7558  ));
7559  }
7560  }
7561  if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) {
7562  $substitutionarray = array_merge($substitutionarray, array(
7563  '__MYCOMPANY_NAME__' => $mysoc->name,
7564  '__MYCOMPANY_EMAIL__' => $mysoc->email,
7565  '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone),
7566  '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax),
7567  '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
7568  '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
7569  '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
7570  '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
7571  '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
7572  '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
7573  '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
7574  '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
7575  '__MYCOMPANY_ADDRESS__' => $mysoc->address,
7576  '__MYCOMPANY_ZIP__' => $mysoc->zip,
7577  '__MYCOMPANY_TOWN__' => $mysoc->town,
7578  '__MYCOMPANY_COUNTRY__' => $mysoc->country,
7579  '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
7580  '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
7581  '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
7582  ));
7583  }
7584 
7585  if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) {
7586  if ($onlykey) {
7587  $substitutionarray['__ID__'] = '__ID__';
7588  $substitutionarray['__REF__'] = '__REF__';
7589  $substitutionarray['__NEWREF__'] = '__NEWREF__';
7590  $substitutionarray['__LABEL__'] = '__LABEL__';
7591  $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
7592  $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
7593  $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
7594  $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
7595  $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
7596 
7597  if (isModEnabled("societe")) { // Most objects are concerned
7598  $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
7599  $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
7600  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
7601  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
7602  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
7603  $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
7604  $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
7605  $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
7606  $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
7607  $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
7608  $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
7609  $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
7610  $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
7611  $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
7612  $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
7613  $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
7614  $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
7615  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
7616  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
7617  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
7618  }
7619  if (isModEnabled('adherent') && (!is_object($object) || $object->element == 'adherent')) {
7620  $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
7621  $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
7622  $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
7623  $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
7624  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
7625  /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
7626  $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
7627  }
7628  // add variables subtitutions ticket
7629  if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket')) {
7630  $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
7631  $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
7632  $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
7633  $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
7634  $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
7635  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
7636  $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
7637  $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
7638  $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
7639  }
7640 
7641  if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature')) {
7642  $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
7643  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
7644  $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
7645  }
7646  if (isModEnabled('project')) { // Most objects
7647  $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
7648  $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
7649  $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
7650  /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
7651  $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
7652  }
7653  if (isModEnabled('contrat') && (!is_object($object) || $object->element == 'contract')) {
7654  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
7655  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
7656  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
7657  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
7658  }
7659  if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal')) {
7660  $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
7661  }
7662  if (isModEnabled("ficheinter") && (!is_object($object) || $object->element == 'fichinter')) {
7663  $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
7664  }
7665  $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
7666  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
7667  $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
7668  $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
7669  $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
7670  $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
7671  $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
7672 
7673  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
7674  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
7675  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
7676  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
7677  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
7678 
7679  if (isModEnabled("expedition") && (!is_object($object) || $object->element == 'shipping')) {
7680  $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
7681  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
7682  }
7683  if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
7684  $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment';
7685  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
7686  }
7687  } else {
7688  $substitutionarray['__ID__'] = $object->id;
7689  $substitutionarray['__REF__'] = $object->ref;
7690  $substitutionarray['__NEWREF__'] = $object->newref;
7691  $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
7692  $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7693  $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7694  $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
7695  $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
7696  if ($object->element == "shipping") {
7697  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', 0, $outputlangs) : '');
7698  } else {
7699  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7700  }
7701  $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%d") : '');
7702  $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%A") : '');
7703  $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%m") : '');
7704  $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%b") : '');
7705  $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%Y") : '');
7706  $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%H") : '');
7707  $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%M") : '');
7708  $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%S") : '');
7709 
7710  // For backward compatibility
7711  $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7712  $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7713  $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7714  $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 : '')) : '');
7715 
7716  if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
7717  $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
7718 
7719  $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
7720  if (method_exists($object, 'getCivilityLabel')) {
7721  $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
7722  }
7723  $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
7724  $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
7725  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
7726  if (method_exists($object, 'getFullName')) {
7727  $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
7728  }
7729  $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
7730  $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
7731  $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
7732  $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
7733  $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
7734  $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
7735  $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
7736  $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
7737  $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
7738  $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
7739  $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
7740  $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
7741  $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
7742  $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
7743  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
7744  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
7745  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
7746  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
7747  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
7748  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
7749  }
7750 
7751  if (is_object($object) && $object->element == 'societe') {
7752  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
7753  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
7754  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
7755  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
7756  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
7757  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
7758  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
7759  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
7760  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
7761  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
7762  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
7763  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
7764  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
7765  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
7766  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
7767  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
7768  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
7769  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
7770  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
7771  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
7772  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
7773  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
7774  } elseif (is_object($object->thirdparty)) {
7775  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
7776  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
7777  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
7778  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
7779  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
7780  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
7781  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
7782  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
7783  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
7784  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
7785  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
7786  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
7787  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
7788  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
7789  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
7790  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
7791  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
7792  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
7793  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
7794  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
7795  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
7796  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
7797  }
7798 
7799  if (is_object($object) && $object->element == 'recruitmentcandidature') {
7800  $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
7801  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
7802  $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
7803  }
7804 
7805  if (is_object($object->project)) {
7806  $substitutionarray['__PROJECT_ID__'] = (is_object($object->project) ? $object->project->id : '');
7807  $substitutionarray['__PROJECT_REF__'] = (is_object($object->project) ? $object->project->ref : '');
7808  $substitutionarray['__PROJECT_NAME__'] = (is_object($object->project) ? $object->project->title : '');
7809  }
7810  if (is_object($object->projet)) { // Deprecated, for backward compatibility
7811  $substitutionarray['__PROJECT_ID__'] = (is_object($object->projet) ? $object->projet->id : '');
7812  $substitutionarray['__PROJECT_REF__'] = (is_object($object->projet) ? $object->projet->ref : '');
7813  $substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet) ? $object->projet->title : '');
7814  }
7815  if (is_object($object) && $object->element == 'project') {
7816  $substitutionarray['__PROJECT_NAME__'] = $object->title;
7817  }
7818 
7819  if (is_object($object) && $object->element == 'shipping') {
7820  $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
7821  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
7822  }
7823  if (is_object($object) && $object->element == 'reception') {
7824  $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
7825  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
7826  }
7827 
7828  if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
7829  $dateplannedstart = '';
7830  $datenextexpiration = '';
7831  foreach ($object->lines as $line) {
7832  if ($line->date_start > $dateplannedstart) {
7833  $dateplannedstart = $line->date_start;
7834  }
7835  if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
7836  $datenextexpiration = $line->date_end;
7837  }
7838  }
7839  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
7840  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
7841  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
7842  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
7843  }
7844  // add substition variable for ticket
7845  if (is_object($object) && $object->element == 'ticket') {
7846  $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
7847  $substitutionarray['__REF__'] = $object->ref;
7848  $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
7849  $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
7850  $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
7851  $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
7852  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
7853  $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
7854  $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
7855  $userstat = new User($db);
7856  if ($object->fk_user_assign > 0) {
7857  $userstat->fetch($object->fk_user_assign);
7858  $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7859  }
7860 
7861  if ($object->fk_user_create > 0) {
7862  $userstat->fetch($object->fk_user_create);
7863  $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7864  }
7865  }
7866 
7867  // Create dynamic tags for __EXTRAFIELD_FIELD__
7868  if ($object->table_element && $object->id > 0) {
7869  if (!is_object($extrafields)) {
7870  $extrafields = new ExtraFields($db);
7871  }
7872  $extrafields->fetch_name_optionals_label($object->table_element, true);
7873 
7874  if ($object->fetch_optionals() > 0) {
7875  if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
7876  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
7877  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7878  if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
7879  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
7880  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
7881  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
7882  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
7883  $datetime = $object->array_options['options_'.$key];
7884  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
7885  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
7886  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
7887  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
7888  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
7889  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
7890  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
7891  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7892  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]);
7893  }
7894  }
7895  }
7896  }
7897  }
7898 
7899  // Complete substitution array with the url to make online payment
7900  $paymenturl = '';
7901  if (empty($substitutionarray['__REF__'])) {
7902  $paymenturl = '';
7903  } else {
7904  // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
7905  require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7906  $outputlangs->loadLangs(array('paypal', 'other'));
7907  $typeforonlinepayment = 'free';
7908  if (is_object($object) && $object->element == 'commande') {
7909  $typeforonlinepayment = 'order';
7910  }
7911  if (is_object($object) && $object->element == 'facture') {
7912  $typeforonlinepayment = 'invoice';
7913  }
7914  if (is_object($object) && $object->element == 'member') {
7915  $typeforonlinepayment = 'member';
7916  }
7917  if (is_object($object) && $object->element == 'contrat') {
7918  $typeforonlinepayment = 'contract';
7919  }
7920  if (is_object($object) && $object->element == 'fichinter') {
7921  $typeforonlinepayment = 'ficheinter';
7922  }
7923  $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']);
7924  $paymenturl = $url;
7925  }
7926 
7927  if ($object->id > 0) {
7928  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
7929  $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
7930 
7931  if (is_object($object) && $object->element == 'propal') {
7932  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
7933  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
7934  }
7935  if (is_object($object) && $object->element == 'fichinter') {
7936  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
7937  $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref);
7938  }
7939  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
7940  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
7941  } else {
7942  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
7943  }
7944  if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') {
7945  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
7946  } else {
7947  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
7948  }
7949  if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') {
7950  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
7951  } else {
7952  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
7953  }
7954  if (!empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'contrat') {
7955  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
7956  } else {
7957  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
7958  }
7959  if (!empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'fichinter') {
7960  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
7961  } else {
7962  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
7963  }
7964  if (!empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'supplier_proposal') {
7965  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
7966  } else {
7967  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
7968  }
7969 
7970  if (is_object($object) && $object->element == 'propal') {
7971  $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
7972  }
7973  if (is_object($object) && $object->element == 'commande') {
7974  $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
7975  }
7976  if (is_object($object) && $object->element == 'facture') {
7977  $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
7978  }
7979  if (is_object($object) && $object->element == 'contrat') {
7980  $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
7981  }
7982  if (is_object($object) && $object->element == 'fichinter') {
7983  $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
7984  }
7985  if (is_object($object) && $object->element == 'supplier_proposal') {
7986  $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
7987  }
7988  if (is_object($object) && $object->element == 'shipping') {
7989  $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
7990  }
7991  }
7992 
7993  if (is_object($object) && $object->element == 'action') {
7994  $substitutionarray['__EVENT_LABEL__'] = $object->label;
7995  $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y');
7996  $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S');
7997  }
7998  }
7999  }
8000  if (empty($exclude) || !in_array('objectamount', $exclude)) {
8001  include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
8002 
8003  $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
8004  $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
8005 
8006  $already_payed_all = 0;
8007  if (is_object($object) && ($object instanceof Facture)) {
8008  $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
8009  }
8010 
8011  $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
8012 
8013  $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
8014  $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
8015  $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
8016 
8017  $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? $object->total_ttc - $already_payed_all : '';
8018 
8019  $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8020  $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)) : '';
8021  $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)) : '';
8022 
8023  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8024  $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
8025  }
8026  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8027  $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
8028  }
8029 
8030  // Amount keys formated in a currency
8031  $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8032  $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8033  $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) : '';
8034  $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)) : '';
8035  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8036  $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8037  }
8038  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8039  $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8040  }
8041 
8042  $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
8043  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
8044  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
8045  // TODO Add other keys for foreign multicurrency
8046 
8047  // For backward compatibility
8048  if ($onlykey != 2) {
8049  $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
8050  $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
8051  $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8052  }
8053  }
8054 
8055  //var_dump($substitutionarray['__AMOUNT_FORMATED__']);
8056  if (empty($exclude) || !in_array('date', $exclude)) {
8057  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8058 
8059  $now = dol_now();
8060 
8061  $tmp = dol_getdate($now, true);
8062  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8063  $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
8064  $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8065  $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
8066 
8067  $daytext = $outputlangs->trans('Day'.$tmp['wday']);
8068 
8069  $substitutionarray = array_merge($substitutionarray, array(
8070  '__NOW_TMS__' => (int) $now,
8071  '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 0, $outputlangs),
8072  '__DAY__' => (string) $tmp['mday'],
8073  '__DAY_TEXT__' => $daytext, // Monday
8074  '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
8075  '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
8076  '__MONTH__' => (string) $tmp['mon'],
8077  '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
8078  '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
8079  '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
8080  '__YEAR__' => (string) $tmp['year'],
8081  '__PREVIOUS_DAY__' => (string) $tmp2['day'],
8082  '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
8083  '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
8084  '__NEXT_DAY__' => (string) $tmp4['day'],
8085  '__NEXT_MONTH__' => (string) $tmp5['month'],
8086  '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
8087  ));
8088  }
8089 
8090  if (isModEnabled('multicompany')) {
8091  $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
8092  }
8093  if (empty($exclude) || !in_array('system', $exclude)) {
8094  $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
8095  $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
8096  $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
8097  $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
8098  }
8099 
8100  return $substitutionarray;
8101 }
8102 
8119 function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
8120 {
8121  global $conf, $langs;
8122 
8123  if (!is_array($substitutionarray)) {
8124  return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
8125  }
8126 
8127  if (empty($outputlangs)) {
8128  $outputlangs = $langs;
8129  }
8130 
8131  // Is initial text HTML or simple text ?
8132  $msgishtml = 0;
8133  if (dol_textishtml($text, 1)) {
8134  $msgishtml = 1;
8135  }
8136 
8137  // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
8138  if (is_object($outputlangs)) {
8139  $reg = array();
8140  while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
8141  // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
8142  $tmp = explode('|', $reg[1]);
8143  if (!empty($tmp[1])) {
8144  $outputlangs->load($tmp[1]);
8145  }
8146 
8147  $value = $outputlangs->transnoentitiesnoconv($reg[1]);
8148 
8149  if (empty($converttextinhtmlifnecessary)) {
8150  // convert $newval into HTML is necessary
8151  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8152  } else {
8153  if (! $msgishtml) {
8154  $valueishtml = dol_textishtml($value, 1);
8155  //var_dump("valueishtml=".$valueishtml);
8156 
8157  if ($valueishtml) {
8158  $text = dol_htmlentitiesbr($text);
8159  $msgishtml = 1;
8160  }
8161  } else {
8162  $value = dol_nl2br("$value");
8163  }
8164 
8165  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
8166  }
8167  }
8168  }
8169 
8170  // Make substitution for constant keys.
8171  // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
8172  $reg = array();
8173  while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
8174  $keyfound = $reg[1];
8175  if (isASecretKey($keyfound)) {
8176  $value = '*****forbidden*****';
8177  } else {
8178  $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
8179  }
8180 
8181  if (empty($converttextinhtmlifnecessary)) {
8182  // convert $newval into HTML is necessary
8183  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8184  } else {
8185  if (! $msgishtml) {
8186  $valueishtml = dol_textishtml($value, 1);
8187 
8188  if ($valueishtml) {
8189  $text = dol_htmlentitiesbr($text);
8190  $msgishtml = 1;
8191  }
8192  } else {
8193  $value = dol_nl2br("$value");
8194  }
8195 
8196  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
8197  }
8198  }
8199 
8200  // Make substitution for array $substitutionarray
8201  foreach ($substitutionarray as $key => $value) {
8202  if (!isset($value)) {
8203  continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
8204  }
8205 
8206  if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) {
8207  $value = ''; // Protection
8208  }
8209 
8210  if (empty($converttextinhtmlifnecessary)) {
8211  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8212  } else {
8213  if (! $msgishtml) {
8214  $valueishtml = dol_textishtml($value, 1);
8215 
8216  if ($valueishtml) {
8217  $text = dol_htmlentitiesbr($text);
8218  $msgishtml = 1;
8219  }
8220  } else {
8221  $value = dol_nl2br("$value");
8222  }
8223  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8224  }
8225  }
8226 
8227  return $text;
8228 }
8229 
8242 function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
8243 {
8244  global $conf, $user;
8245 
8246  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8247 
8248  // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
8249 
8250  // Check if there is external substitution to do, requested by plugins
8251  $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
8252 
8253  foreach ($dirsubstitutions as $reldir) {
8254  $dir = dol_buildpath($reldir, 0);
8255 
8256  // Check if directory exists
8257  if (!dol_is_dir($dir)) {
8258  continue;
8259  }
8260 
8261  $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
8262  foreach ($substitfiles as $substitfile) {
8263  $reg = array();
8264  if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
8265  $module = $reg[1];
8266 
8267  dol_syslog("Library ".$substitfile['name']." found into ".$dir);
8268  // Include the user's functions file
8269  require_once $dir.$substitfile['name'];
8270  // Call the user's function, and only if it is defined
8271  $function_name = $module."_".$callfunc;
8272  if (function_exists($function_name)) {
8273  $function_name($substitutionarray, $outputlangs, $object, $parameters);
8274  }
8275  }
8276  }
8277  }
8278  if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) {
8279  // to list all tags in odt template
8280  $tags = '';
8281  foreach ($substitutionarray as $key => $value) {
8282  $tags .= '{'.$key.'} => '.$value."\n";
8283  }
8284  $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
8285  }
8286 }
8287 
8297 function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
8298 {
8299  print get_date_range($date_start, $date_end, $format, $outputlangs);
8300 }
8301 
8312 function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
8313 {
8314  global $langs;
8315 
8316  $out = '';
8317 
8318  if (!is_object($outputlangs)) {
8319  $outputlangs = $langs;
8320  }
8321 
8322  if ($date_start && $date_end) {
8323  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8324  }
8325  if ($date_start && !$date_end) {
8326  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8327  }
8328  if (!$date_start && $date_end) {
8329  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8330  }
8331 
8332  return $out;
8333 }
8334 
8343 function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
8344 {
8345  global $conf;
8346 
8347  $ret = '';
8348  // If order not defined, we use the setup
8349  if ($nameorder < 0) {
8350  $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
8351  }
8352  if ($nameorder == 1) {
8353  $ret .= $firstname;
8354  if ($firstname && $lastname) {
8355  $ret .= ' ';
8356  }
8357  $ret .= $lastname;
8358  } elseif ($nameorder == 2 || $nameorder == 3) {
8359  $ret .= $firstname;
8360  if (empty($ret) && $nameorder == 3) {
8361  $ret .= $lastname;
8362  }
8363  } else { // 0, 4 or 5
8364  $ret .= $lastname;
8365  if (empty($ret) && $nameorder == 5) {
8366  $ret .= $firstname;
8367  }
8368  if ($nameorder == 0) {
8369  if ($firstname && $lastname) {
8370  $ret .= ' ';
8371  }
8372  $ret .= $firstname;
8373  }
8374  }
8375  return $ret;
8376 }
8377 
8378 
8389 function setEventMessage($mesgs, $style = 'mesgs')
8390 {
8391  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
8392  if (!is_array($mesgs)) {
8393  // If mesgs is a string
8394  if ($mesgs) {
8395  $_SESSION['dol_events'][$style][] = $mesgs;
8396  }
8397  } else {
8398  // If mesgs is an array
8399  foreach ($mesgs as $mesg) {
8400  if ($mesg) {
8401  $_SESSION['dol_events'][$style][] = $mesg;
8402  }
8403  }
8404  }
8405 }
8406 
8418 function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '')
8419 {
8420  if (empty($mesg) && empty($mesgs)) {
8421  dol_syslog("Try to add a message in stack with empty message", LOG_WARNING);
8422  } else {
8423  if ($messagekey) {
8424  // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
8425  // TODO
8426  $mesg .= '';
8427  }
8428  if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
8429  if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
8430  dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages');
8431  }
8432  if (empty($mesgs)) {
8433  setEventMessage($mesg, $style);
8434  } else {
8435  if (!empty($mesg) && !in_array($mesg, $mesgs)) {
8436  setEventMessage($mesg, $style); // Add message string if not already into array
8437  }
8438  setEventMessage($mesgs, $style);
8439  }
8440  }
8441  }
8442 }
8443 
8453 function dol_htmloutput_events($disabledoutputofmessages = 0)
8454 {
8455  // Show mesgs
8456  if (isset($_SESSION['dol_events']['mesgs'])) {
8457  if (empty($disabledoutputofmessages)) {
8458  dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
8459  }
8460  unset($_SESSION['dol_events']['mesgs']);
8461  }
8462  // Show errors
8463  if (isset($_SESSION['dol_events']['errors'])) {
8464  if (empty($disabledoutputofmessages)) {
8465  dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
8466  }
8467  unset($_SESSION['dol_events']['errors']);
8468  }
8469 
8470  // Show warnings
8471  if (isset($_SESSION['dol_events']['warnings'])) {
8472  if (empty($disabledoutputofmessages)) {
8473  dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
8474  }
8475  unset($_SESSION['dol_events']['warnings']);
8476  }
8477 }
8478 
8493 function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
8494 {
8495  global $conf, $langs;
8496 
8497  $ret = 0;
8498  $return = '';
8499  $out = '';
8500  $divstart = $divend = '';
8501 
8502  // If inline message with no format, we add it.
8503  if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
8504  $divstart = '<div class="'.$style.' clearboth">';
8505  $divend = '</div>';
8506  }
8507 
8508  if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
8509  $langs->load("errors");
8510  $out .= $divstart;
8511  if (is_array($mesgarray) && count($mesgarray)) {
8512  foreach ($mesgarray as $message) {
8513  $ret++;
8514  $out .= $langs->trans($message);
8515  if ($ret < count($mesgarray)) {
8516  $out .= "<br>\n";
8517  }
8518  }
8519  }
8520  if ($mesgstring) {
8521  $ret++;
8522  $out .= $langs->trans($mesgstring);
8523  }
8524  $out .= $divend;
8525  }
8526 
8527  if ($out) {
8528  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) {
8529  $return = '<script>
8530  $(document).ready(function() {
8531  var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").'
8532  if (block) {
8533  $.dolEventValid("","'.dol_escape_js($out).'");
8534  } else {
8535  /* jnotify(message, preset of message type, keepmessage) */
8536  $.jnotify("'.dol_escape_js($out).'",
8537  "'.($style == "ok" ? 3000 : $style).'",
8538  '.($style == "ok" ? "false" : "true").',
8539  { remove: function (){} } );
8540  }
8541  });
8542  </script>';
8543  } else {
8544  $return = $out;
8545  }
8546  }
8547 
8548  return $return;
8549 }
8550 
8562 function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8563 {
8564  return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8565 }
8566 
8580 function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
8581 {
8582  if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
8583  return;
8584  }
8585 
8586  $iserror = 0;
8587  $iswarning = 0;
8588  if (is_array($mesgarray)) {
8589  foreach ($mesgarray as $val) {
8590  if ($val && preg_match('/class="error"/i', $val)) {
8591  $iserror++;
8592  break;
8593  }
8594  if ($val && preg_match('/class="warning"/i', $val)) {
8595  $iswarning++;
8596  break;
8597  }
8598  }
8599  } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
8600  $iserror++;
8601  } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
8602  $iswarning++;
8603  }
8604  if ($style == 'error') {
8605  $iserror++;
8606  }
8607  if ($style == 'warning') {
8608  $iswarning++;
8609  }
8610 
8611  if ($iserror || $iswarning) {
8612  // Remove div from texts
8613  $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
8614  $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
8615  $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
8616  // Remove div from texts array
8617  if (is_array($mesgarray)) {
8618  $newmesgarray = array();
8619  foreach ($mesgarray as $val) {
8620  if (is_string($val)) {
8621  $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
8622  $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
8623  $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
8624  $newmesgarray[] = $tmpmesgstring;
8625  } else {
8626  dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
8627  }
8628  }
8629  $mesgarray = $newmesgarray;
8630  }
8631  print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
8632  } else {
8633  print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
8634  }
8635 }
8636 
8648 function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8649 {
8650  dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8651 }
8652 
8667 function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
8668 {
8669  // Clean parameters
8670  $order = strtolower($order);
8671 
8672  if (is_array($array)) {
8673  $sizearray = count($array);
8674  if ($sizearray > 0) {
8675  $temp = array();
8676  foreach (array_keys($array) as $key) {
8677  if (is_object($array[$key])) {
8678  $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
8679  } else {
8680  $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
8681  }
8682  if ($natsort == -1) {
8683  $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
8684  }
8685  }
8686 
8687  if (empty($natsort) || $natsort == -1) {
8688  if ($order == 'asc') {
8689  asort($temp);
8690  } else {
8691  arsort($temp);
8692  }
8693  } else {
8694  if ($case_sensitive) {
8695  natsort($temp);
8696  } else {
8697  natcasesort($temp); // natecasesort is not sensible to case
8698  }
8699  if ($order != 'asc') {
8700  $temp = array_reverse($temp, true);
8701  }
8702  }
8703 
8704  $sorted = array();
8705 
8706  foreach (array_keys($temp) as $key) {
8707  (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
8708  }
8709 
8710  return $sorted;
8711  }
8712  }
8713  return $array;
8714 }
8715 
8716 
8723 function utf8_check($str)
8724 {
8725  $str = (string) $str; // Sometimes string is an int.
8726 
8727  // We must use here a binary strlen function (so not dol_strlen)
8728  $strLength = dol_strlen($str);
8729  for ($i = 0; $i < $strLength; $i++) {
8730  if (ord($str[$i]) < 0x80) {
8731  continue; // 0bbbbbbb
8732  } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
8733  $n = 1; // 110bbbbb
8734  } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
8735  $n = 2; // 1110bbbb
8736  } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
8737  $n = 3; // 11110bbb
8738  } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
8739  $n = 4; // 111110bb
8740  } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
8741  $n = 5; // 1111110b
8742  } else {
8743  return false; // Does not match any model
8744  }
8745  for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
8746  if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
8747  return false;
8748  }
8749  }
8750  }
8751  return true;
8752 }
8753 
8760 function ascii_check($str)
8761 {
8762  if (function_exists('mb_check_encoding')) {
8763  //if (mb_detect_encoding($str, 'ASCII', true) return false;
8764  if (!mb_check_encoding($str, 'ASCII')) {
8765  return false;
8766  }
8767  } else {
8768  if (preg_match('/[^\x00-\x7f]/', $str)) {
8769  return false; // Contains a byte > 7f
8770  }
8771  }
8772 
8773  return true;
8774 }
8775 
8776 
8784 function dol_osencode($str)
8785 {
8786  global $conf;
8787 
8788  $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
8789  if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
8790  $tmp = 'iso-8859-1'; // By default for windows
8791  }
8792  if (empty($tmp)) {
8793  $tmp = 'utf-8'; // By default for other
8794  }
8795  if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) {
8796  $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING;
8797  }
8798 
8799  if ($tmp == 'iso-8859-1') {
8800  return utf8_decode($str);
8801  }
8802  return $str;
8803 }
8804 
8805 
8820 function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
8821 {
8822  global $cache_codes;
8823 
8824  // If key empty
8825  if ($key == '') {
8826  return '';
8827  }
8828 
8829  // Check in cache
8830  if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
8831  return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
8832  }
8833 
8834  dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
8835 
8836  $sql = "SELECT ".$fieldid." as valuetoget";
8837  $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
8838  $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
8839  if (!empty($entityfilter)) {
8840  $sql .= " AND entity IN (".getEntity($tablename).")";
8841  }
8842  if ($filters) {
8843  $sql .= $filters;
8844  }
8845 
8846  $resql = $db->query($sql);
8847  if ($resql) {
8848  $obj = $db->fetch_object($resql);
8849  if ($obj) {
8850  $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
8851  } else {
8852  $cache_codes[$tablename][$key][$fieldid] = '';
8853  }
8854  $db->free($resql);
8855  return $cache_codes[$tablename][$key][$fieldid];
8856  } else {
8857  return -1;
8858  }
8859 }
8860 
8867 function verifCond($strToEvaluate)
8868 {
8869  global $user, $conf, $langs;
8870  global $leftmenu;
8871  global $rights; // To export to dol_eval function
8872 
8873  //print $strToEvaluate."<br>\n";
8874  $rights = true;
8875  if (isset($strToEvaluate) && $strToEvaluate !== '') {
8876  //var_dump($strToEvaluate);
8877  $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
8878  $rights = $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
8879  //var_dump($rights);
8880  }
8881  return $rights;
8882 }
8883 
8894 function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
8895 {
8896  // Only global variables can be changed by eval function and returned to caller
8897  global $db, $langs, $user, $conf, $website, $websitepage;
8898  global $action, $mainmenu, $leftmenu;
8899  global $rights;
8900  global $object;
8901  global $mysoc;
8902 
8903  global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
8904  global $soc; // For backward compatibility
8905 
8906  // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
8907  if ($onlysimplestring == '1') {
8908  // We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
8909  // We must accept: '$conf->barcode->enabled || preg_match(\'/^AAA/\',$leftmenu)'
8910  // We must accept: '$user->rights->cabinetmed->read && !$object->canvas=="patient@cabinetmed"'
8911  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@', '/').']/i', $s)) {
8912  if ($returnvalue) {
8913  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8914  } else {
8915  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8916  return '';
8917  }
8918  // TODO
8919  // We can exclude all parenthesis ( that are not '($db' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match(' and 'isModEnabled('
8920  // ...
8921  }
8922  } elseif ($onlysimplestring == '2') {
8923  // 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"
8924  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@;[]', '/').']/i', $s)) {
8925  if ($returnvalue) {
8926  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8927  } else {
8928  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8929  return '';
8930  }
8931  }
8932  }
8933  if (strpos($s, '::') !== false) {
8934  if ($returnvalue) {
8935  return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
8936  } else {
8937  dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s);
8938  return '';
8939  }
8940  }
8941  if (strpos($s, '`') !== false) {
8942  if ($returnvalue) {
8943  return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
8944  } else {
8945  dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s);
8946  return '';
8947  }
8948  }
8949  if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
8950  if ($returnvalue) {
8951  return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
8952  } else {
8953  dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s);
8954  return '';
8955  }
8956  }
8957 
8958  // We block use of php exec or php file functions
8959  $forbiddenphpstrings = array('$$');
8960  $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST'));
8961 
8962  $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen", "eval", "dol_eval", "executeCLI", "verifCond", "base64_decode");
8963  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
8964  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
8965 
8966  $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
8967 
8968  do {
8969  $oldstringtoclean = $s;
8970  $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
8971  $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
8972  //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
8973  } while ($oldstringtoclean != $s);
8974 
8975  if (strpos($s, '__forbiddenstring__') !== false) {
8976  dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
8977  if ($returnvalue) {
8978  return 'Bad string syntax to evaluate: '.$s;
8979  } else {
8980  dol_syslog('Bad string syntax to evaluate: '.$s);
8981  return '';
8982  }
8983  }
8984 
8985  //print $s."<br>\n";
8986  if ($returnvalue) {
8987  if ($hideerrors) {
8988  return @eval('return '.$s.';');
8989  } else {
8990  return eval('return '.$s.';');
8991  }
8992  } else {
8993  if ($hideerrors) {
8994  @eval($s);
8995  } else {
8996  eval($s);
8997  }
8998  }
8999 }
9000 
9007 function dol_validElement($element)
9008 {
9009  return (trim($element) != '');
9010 }
9011 
9020 function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
9021 {
9022  if (empty($codelang)) {
9023  return '';
9024  }
9025 
9026  if ($codelang == 'auto') {
9027  return '<span class="fa fa-language"></span>';
9028  }
9029 
9030  $langtocountryflag = array(
9031  'ar_AR' => '',
9032  'ca_ES' => 'catalonia',
9033  'da_DA' => 'dk',
9034  'fr_CA' => 'mq',
9035  'sv_SV' => 'se',
9036  'sw_SW' => 'unknown',
9037  'AQ' => 'unknown',
9038  'CW' => 'unknown',
9039  'IM' => 'unknown',
9040  'JE' => 'unknown',
9041  'MF' => 'unknown',
9042  'BL' => 'unknown',
9043  'SX' => 'unknown'
9044  );
9045 
9046  if (isset($langtocountryflag[$codelang])) {
9047  $flagImage = $langtocountryflag[$codelang];
9048  } else {
9049  $tmparray = explode('_', $codelang);
9050  $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
9051  }
9052 
9053  return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
9054 }
9055 
9063 function getLanguageCodeFromCountryCode($countrycode)
9064 {
9065  global $mysoc;
9066 
9067  if (empty($countrycode)) {
9068  return null;
9069  }
9070 
9071  if (strtoupper($countrycode) == 'MQ') {
9072  return 'fr_CA';
9073  }
9074  if (strtoupper($countrycode) == 'SE') {
9075  return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
9076  }
9077  if (strtoupper($countrycode) == 'CH') {
9078  if ($mysoc->country_code == 'FR') {
9079  return 'fr_CH';
9080  }
9081  if ($mysoc->country_code == 'DE') {
9082  return 'de_CH';
9083  }
9084  if ($mysoc->country_code == 'IT') {
9085  return 'it_CH';
9086  }
9087  }
9088 
9089  // Locale list taken from:
9090  // http://stackoverflow.com/questions/3191664/
9091  // list-of-all-locales-and-their-short-codes
9092  $locales = array(
9093  'af-ZA',
9094  'am-ET',
9095  'ar-AE',
9096  'ar-BH',
9097  'ar-DZ',
9098  'ar-EG',
9099  'ar-IQ',
9100  'ar-JO',
9101  'ar-KW',
9102  'ar-LB',
9103  'ar-LY',
9104  'ar-MA',
9105  'ar-OM',
9106  'ar-QA',
9107  'ar-SA',
9108  'ar-SY',
9109  'ar-TN',
9110  'ar-YE',
9111  //'as-IN', // Moved after en-IN
9112  'ba-RU',
9113  'be-BY',
9114  'bg-BG',
9115  'bn-BD',
9116  //'bn-IN', // Moved after en-IN
9117  'bo-CN',
9118  'br-FR',
9119  'ca-ES',
9120  'co-FR',
9121  'cs-CZ',
9122  'cy-GB',
9123  'da-DK',
9124  'de-AT',
9125  'de-CH',
9126  'de-DE',
9127  'de-LI',
9128  'de-LU',
9129  'dv-MV',
9130  'el-GR',
9131  'en-AU',
9132  'en-BZ',
9133  'en-CA',
9134  'en-GB',
9135  'en-IE',
9136  'en-IN',
9137  'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
9138  'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
9139  'en-JM',
9140  'en-MY',
9141  'en-NZ',
9142  'en-PH',
9143  'en-SG',
9144  'en-TT',
9145  'en-US',
9146  'en-ZA',
9147  'en-ZW',
9148  'es-AR',
9149  'es-BO',
9150  'es-CL',
9151  'es-CO',
9152  'es-CR',
9153  'es-DO',
9154  'es-EC',
9155  'es-ES',
9156  'es-GT',
9157  'es-HN',
9158  'es-MX',
9159  'es-NI',
9160  'es-PA',
9161  'es-PE',
9162  'es-PR',
9163  'es-PY',
9164  'es-SV',
9165  'es-US',
9166  'es-UY',
9167  'es-VE',
9168  'et-EE',
9169  'eu-ES',
9170  'fa-IR',
9171  'fi-FI',
9172  'fo-FO',
9173  'fr-BE',
9174  'fr-CA',
9175  'fr-CH',
9176  'fr-FR',
9177  'fr-LU',
9178  'fr-MC',
9179  'fy-NL',
9180  'ga-IE',
9181  'gd-GB',
9182  'gl-ES',
9183  'gu-IN',
9184  'he-IL',
9185  'hi-IN',
9186  'hr-BA',
9187  'hr-HR',
9188  'hu-HU',
9189  'hy-AM',
9190  'id-ID',
9191  'ig-NG',
9192  'ii-CN',
9193  'is-IS',
9194  'it-CH',
9195  'it-IT',
9196  'ja-JP',
9197  'ka-GE',
9198  'kk-KZ',
9199  'kl-GL',
9200  'km-KH',
9201  'kn-IN',
9202  'ko-KR',
9203  'ky-KG',
9204  'lb-LU',
9205  'lo-LA',
9206  'lt-LT',
9207  'lv-LV',
9208  'mi-NZ',
9209  'mk-MK',
9210  'ml-IN',
9211  'mn-MN',
9212  'mr-IN',
9213  'ms-BN',
9214  'ms-MY',
9215  'mt-MT',
9216  'nb-NO',
9217  'ne-NP',
9218  'nl-BE',
9219  'nl-NL',
9220  'nn-NO',
9221  'oc-FR',
9222  'or-IN',
9223  'pa-IN',
9224  'pl-PL',
9225  'ps-AF',
9226  'pt-BR',
9227  'pt-PT',
9228  'rm-CH',
9229  'ro-MD',
9230  'ro-RO',
9231  'ru-RU',
9232  'rw-RW',
9233  'sa-IN',
9234  'se-FI',
9235  'se-NO',
9236  'se-SE',
9237  'si-LK',
9238  'sk-SK',
9239  'sl-SI',
9240  'sq-AL',
9241  'sv-FI',
9242  'sv-SE',
9243  'sw-KE',
9244  'ta-IN',
9245  'te-IN',
9246  'th-TH',
9247  'tk-TM',
9248  'tn-ZA',
9249  'tr-TR',
9250  'tt-RU',
9251  'ug-CN',
9252  'uk-UA',
9253  'ur-PK',
9254  'vi-VN',
9255  'wo-SN',
9256  'xh-ZA',
9257  'yo-NG',
9258  'zh-CN',
9259  'zh-HK',
9260  'zh-MO',
9261  'zh-SG',
9262  'zh-TW',
9263  'zu-ZA',
9264  );
9265 
9266  $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
9267  if (in_array($buildprimarykeytotest, $locales)) {
9268  return strtolower($countrycode).'_'.strtoupper($countrycode);
9269  }
9270 
9271  if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
9272  foreach ($locales as $locale) {
9273  $locale_language = locale_get_primary_language($locale);
9274  $locale_region = locale_get_region($locale);
9275  if (strtoupper($countrycode) == $locale_region) {
9276  //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
9277  return strtolower($locale_language).'_'.strtoupper($locale_region);
9278  }
9279  }
9280  } else {
9281  dol_syslog("Warning Exention php-intl is not available", LOG_WARNING);
9282  }
9283 
9284  return null;
9285 }
9286 
9317 function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
9318 {
9319  global $hookmanager, $db;
9320 
9321  if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
9322  foreach ($conf->modules_parts['tabs'][$type] as $value) {
9323  $values = explode(':', $value);
9324 
9325  $reg = array();
9326  if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
9327  if (count($values) == 6) {
9328  // new declaration with permissions:
9329  // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9330  // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9331  if ($values[0] != $type) {
9332  continue;
9333  }
9334 
9335  if (verifCond($values[4])) {
9336  if ($values[3]) {
9337  if ($filterorigmodule) { // If a filter of module origin has been requested
9338  if (strpos($values[3], '@')) { // This is an external module
9339  if ($filterorigmodule != 'external') {
9340  continue;
9341  }
9342  } else { // This looks a core module
9343  if ($filterorigmodule != 'core') {
9344  continue;
9345  }
9346  }
9347  }
9348  $langs->load($values[3]);
9349  }
9350  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9351  // If label is "SUBSTITUION_..."
9352  $substitutionarray = array();
9353  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9354  $label = make_substitutions($reg[1], $substitutionarray);
9355  } else {
9356  // If label is "Label,Class,File,Method", we call the method to show content inside the badge
9357  $labeltemp = explode(',', $values[2]);
9358  $label = $langs->trans($labeltemp[0]);
9359 
9360  if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
9361  dol_include_once($labeltemp[2]);
9362  $classtoload = $labeltemp[1];
9363  if (class_exists($classtoload)) {
9364  $obj = new $classtoload($db);
9365  $function = $labeltemp[3];
9366  if ($obj && $function && method_exists($obj, $function)) {
9367  $nbrec = $obj->$function($object->id, $obj);
9368  $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
9369  }
9370  }
9371  }
9372  }
9373 
9374  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
9375  $head[$h][1] = $label;
9376  $head[$h][2] = str_replace('+', '', $values[1]);
9377  $h++;
9378  }
9379  } elseif (count($values) == 5) { // case deprecated
9380  dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
9381 
9382  if ($values[0] != $type) {
9383  continue;
9384  }
9385  if ($values[3]) {
9386  if ($filterorigmodule) { // If a filter of module origin has been requested
9387  if (strpos($values[3], '@')) { // This is an external module
9388  if ($filterorigmodule != 'external') {
9389  continue;
9390  }
9391  } else { // This looks a core module
9392  if ($filterorigmodule != 'core') {
9393  continue;
9394  }
9395  }
9396  }
9397  $langs->load($values[3]);
9398  }
9399  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9400  $substitutionarray = array();
9401  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9402  $label = make_substitutions($reg[1], $substitutionarray);
9403  } else {
9404  $label = $langs->trans($values[2]);
9405  }
9406 
9407  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
9408  $head[$h][1] = $label;
9409  $head[$h][2] = str_replace('+', '', $values[1]);
9410  $h++;
9411  }
9412  } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
9413  if ($values[0] != $type) {
9414  continue;
9415  }
9416  $tabname = str_replace('-', '', $values[1]);
9417  foreach ($head as $key => $val) {
9418  $condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
9419  //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
9420  if ($head[$key][2] == $tabname && $condition) {
9421  unset($head[$key]);
9422  break;
9423  }
9424  }
9425  }
9426  }
9427  }
9428 
9429  // No need to make a return $head. Var is modified as a reference
9430  if (!empty($hookmanager)) {
9431  $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
9432  $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters);
9433  if ($reshook > 0) { // Hook ask to replace completely the array
9434  $head = $hookmanager->resArray;
9435  } else { // Hook
9436  $head = array_merge($head, $hookmanager->resArray);
9437  }
9438  $h = count($head);
9439  }
9440 }
9441 
9453 function printCommonFooter($zone = 'private')
9454 {
9455  global $conf, $hookmanager, $user, $debugbar;
9456  global $action;
9457  global $micro_start_time;
9458 
9459  if ($zone == 'private') {
9460  print "\n".'<!-- Common footer for private page -->'."\n";
9461  } else {
9462  print "\n".'<!-- Common footer for public page -->'."\n";
9463  }
9464 
9465  // A div to store page_y POST parameter so we can read it using javascript
9466  print "\n<!-- A div to store page_y POST parameter -->\n";
9467  print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
9468 
9469  $parameters = array();
9470  $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
9471  if (empty($reshook)) {
9472  if (!empty($conf->global->MAIN_HTML_FOOTER)) {
9473  print $conf->global->MAIN_HTML_FOOTER."\n";
9474  }
9475 
9476  print "\n";
9477  if (!empty($conf->use_javascript_ajax)) {
9478  print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and madatory fields, tuning info, ... -->\n";
9479  print '<script>'."\n";
9480  print 'jQuery(document).ready(function() {'."\n";
9481 
9482  if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
9483  print "\n";
9484  print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
9485  print 'jQuery("li.menuhider").click(function(event) {';
9486  print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
9487  print ' console.log("We click on .menuhider");'."\n";
9488  print ' $("body").toggleClass("sidebar-collapse")'."\n";
9489  print '});'."\n";
9490  }
9491 
9492  // Management of focus and mandatory for fields
9493  if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"])))) {
9494  print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
9495  $relativepathstring = $_SERVER["PHP_SELF"];
9496  // Clean $relativepathstring
9497  if (constant('DOL_URL_ROOT')) {
9498  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
9499  }
9500  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
9501  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
9502  //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
9503  if (!empty($user->default_values[$relativepathstring]['focus'])) {
9504  foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
9505  $qualified = 0;
9506  if ($defkey != '_noquery_') {
9507  $tmpqueryarraytohave = explode('&', $defkey);
9508  $foundintru = 0;
9509  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9510  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9511  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9512  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9513  $foundintru = 1;
9514  }
9515  }
9516  if (!$foundintru) {
9517  $qualified = 1;
9518  }
9519  //var_dump($defkey.'-'.$qualified);
9520  } else {
9521  $qualified = 1;
9522  }
9523 
9524  if ($qualified) {
9525  foreach ($defval as $paramkey => $paramval) {
9526  // Set focus on field
9527  print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
9528  print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
9529  print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
9530  }
9531  }
9532  }
9533  }
9534  if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
9535  foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
9536  $qualified = 0;
9537  if ($defkey != '_noquery_') {
9538  $tmpqueryarraytohave = explode('&', $defkey);
9539  $foundintru = 0;
9540  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9541  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9542  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9543  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9544  $foundintru = 1;
9545  }
9546  }
9547  if (!$foundintru) {
9548  $qualified = 1;
9549  }
9550  //var_dump($defkey.'-'.$qualified);
9551  } else {
9552  $qualified = 1;
9553  }
9554 
9555  if ($qualified) {
9556  foreach ($defval as $paramkey => $paramval) {
9557  // Add property 'required' on input
9558  print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9559  print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9560  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";
9561  print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9562  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
9563  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
9564 
9565  // Add 'field required' class on closest td for all input elements : input, textarea and select
9566  print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
9567  }
9568  }
9569  }
9570  }
9571  }
9572 
9573  print '});'."\n";
9574 
9575  // End of tuning
9576  if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) {
9577  print "\n";
9578  print "/* JS CODE TO ENABLE to add memory info */\n";
9579  print 'window.console && console.log("';
9580  if (!empty($conf->global->MEMCACHED_SERVER)) {
9581  print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
9582  }
9583  print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off');
9584  if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
9585  $micro_end_time = microtime(true);
9586  print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
9587  }
9588 
9589  if (function_exists("memory_get_usage")) {
9590  print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
9591  }
9592  if (function_exists("memory_get_peak_usage")) {
9593  print ' - Real mem peak: '.memory_get_peak_usage(true);
9594  }
9595  if (function_exists("zend_loader_file_encoded")) {
9596  print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
9597  }
9598  print '");'."\n";
9599  }
9600 
9601  print "\n".'</script>'."\n";
9602 
9603  // Google Analytics
9604  // TODO Add a hook here
9605  if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID)) {
9606  $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID);
9607  foreach ($tmptagarray as $tmptag) {
9608  print "\n";
9609  print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
9610  print "
9611  <!-- Global site tag (gtag.js) - Google Analytics -->
9612  <script async src=\"https://www.googletagmanager.com/gtag/js?id=".trim($tmptag)."\"></script>
9613  <script>
9614  window.dataLayer = window.dataLayer || [];
9615  function gtag(){dataLayer.push(arguments);}
9616  gtag('js', new Date());
9617 
9618  gtag('config', '".trim($tmptag)."');
9619  </script>";
9620  print "\n";
9621  }
9622  }
9623  }
9624 
9625  // Add Xdebug coverage of code
9626  if (defined('XDEBUGCOVERAGE')) {
9627  print_r(xdebug_get_code_coverage());
9628  }
9629 
9630  // Add DebugBar data
9631  if (!empty($user->rights->debugbar->read) && is_object($debugbar)) {
9632  $debugbar['time']->stopMeasure('pageaftermaster');
9633  print '<!-- Output debugbar data -->'."\n";
9634  $renderer = $debugbar->getRenderer();
9635  print $debugbar->getRenderer()->render();
9636  } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
9637  print "\n";
9638  print "<!-- Start of log output\n";
9639  //print '<div class="hidden">'."\n";
9640  foreach ($conf->logbuffer as $logline) {
9641  print $logline."<br>\n";
9642  }
9643  //print '</div>'."\n";
9644  print "End of log output -->\n";
9645  }
9646  }
9647 }
9648 
9658 function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
9659 {
9660  if ($a = explode($delimiter, $string)) {
9661  $ka = array();
9662  foreach ($a as $s) { // each part
9663  if ($s) {
9664  if ($pos = strpos($s, $kv)) { // key/value delimiter
9665  $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
9666  } else { // key delimiter not found
9667  $ka[] = trim($s);
9668  }
9669  }
9670  }
9671  return $ka;
9672  }
9673  return array();
9674 }
9675 
9676 
9683 function dol_set_focus($selector)
9684 {
9685  print "\n".'<!-- Set focus onto a specific field -->'."\n";
9686  print '<script>jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
9687 }
9688 
9689 
9697 function dol_getmypid()
9698 {
9699  if (!function_exists('getmypid')) {
9700  return mt_rand(99900000, 99965535);
9701  } else {
9702  return getmypid(); // May be a number on 64 bits (depending on OS)
9703  }
9704 }
9705 
9706 
9724 function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
9725 {
9726  global $db, $langs;
9727 
9728  $value = trim($value);
9729 
9730  if ($mode == 0) {
9731  $value = preg_replace('/\*/', '%', $value); // Replace * with %
9732  }
9733  if ($mode == 1) {
9734  $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
9735  }
9736 
9737  $value = preg_replace('/\s*\|\s*/', '|', $value);
9738 
9739  $crits = explode(' ', $value);
9740  $res = '';
9741  if (!is_array($fields)) {
9742  $fields = array($fields);
9743  }
9744 
9745  $j = 0;
9746  foreach ($crits as $crit) {
9747  $crit = trim($crit);
9748  $i = 0;
9749  $i2 = 0;
9750  $newres = '';
9751  foreach ($fields as $field) {
9752  if ($mode == 1) {
9753  $operator = '=';
9754  $newcrit = preg_replace('/([<>=]+)/', '', $crit);
9755 
9756  $reg = array();
9757  preg_match('/([<>=]+)/', $crit, $reg);
9758  if (!empty($reg[1])) {
9759  $operator = $reg[1];
9760  }
9761  if ($newcrit != '') {
9762  $numnewcrit = price2num($newcrit);
9763  if (is_numeric($numnewcrit)) {
9764  $newres .= ($i2 > 0 ? ' OR ' : '').$field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
9765  } else {
9766  $newres .= ($i2 > 0 ? ' OR ' : '').'1 = 2'; // force false
9767  }
9768  $i2++; // a criteria was added to string
9769  }
9770  } elseif ($mode == 2 || $mode == -2) {
9771  $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
9772  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
9773  $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
9774  if ($mode == -2) {
9775  $newres .= ' OR '.$field.' IS NULL';
9776  }
9777  $i2++; // a criteria was added to string
9778  } elseif ($mode == 3 || $mode == -3) {
9779  $tmparray = explode(',', $crit);
9780  if (count($tmparray)) {
9781  $listofcodes = '';
9782  foreach ($tmparray as $val) {
9783  $val = trim($val);
9784  if ($val) {
9785  $listofcodes .= ($listofcodes ? ',' : '');
9786  $listofcodes .= "'".$db->escape($val)."'";
9787  }
9788  }
9789  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
9790  $i2++; // a criteria was added to string
9791  }
9792  if ($mode == -3) {
9793  $newres .= ' OR '.$field.' IS NULL';
9794  }
9795  } elseif ($mode == 4) {
9796  $tmparray = explode(',', $crit);
9797  if (count($tmparray)) {
9798  $listofcodes = '';
9799  foreach ($tmparray as $val) {
9800  $val = trim($val);
9801  if ($val) {
9802  $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
9803  $newres .= ' OR '.$field." = '".$db->escape($val)."'";
9804  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
9805  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
9806  $newres .= ')';
9807  $i2++;
9808  }
9809  }
9810  }
9811  } else // $mode=0
9812  {
9813  $tmpcrits = explode('|', $crit);
9814  $i3 = 0;
9815  foreach ($tmpcrits as $tmpcrit) {
9816  if ($tmpcrit !== '0' && empty($tmpcrit)) {
9817  continue;
9818  }
9819 
9820  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
9821 
9822  if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
9823  $newres .= $field." = ".(is_numeric(trim($tmpcrit)) ? ((float) trim($tmpcrit)) : '0');
9824  } else {
9825  $tmpcrit = trim($tmpcrit);
9826  $tmpcrit2 = $tmpcrit;
9827  $tmpbefore = '%';
9828  $tmpafter = '%';
9829  if (preg_match('/^!/', $tmpcrit)) {
9830  $newres .= $field." NOT LIKE '"; // ! as exclude character
9831  $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
9832  } else $newres .= $field." LIKE '";
9833 
9834  if (preg_match('/^[\^\$]/', $tmpcrit)) {
9835  $tmpbefore = '';
9836  $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
9837  }
9838  if (preg_match('/[\^\$]$/', $tmpcrit)) {
9839  $tmpafter = '';
9840  $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
9841  }
9842  $newres .= $tmpbefore;
9843  $newres .= $db->escape($tmpcrit2);
9844  $newres .= $tmpafter;
9845  $newres .= "'";
9846  if ($tmpcrit2 == '') {
9847  $newres .= " OR ".$field." IS NULL";
9848  }
9849  }
9850 
9851  $i3++;
9852  }
9853  $i2++; // a criteria was added to string
9854  }
9855  $i++;
9856  }
9857  if ($newres) {
9858  $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
9859  }
9860  $j++;
9861  }
9862  $res = ($nofirstand ? "" : " AND ")."(".$res.")";
9863  //print 'xx'.$res.'yy';
9864  return $res;
9865 }
9866 
9873 function showDirectDownloadLink($object)
9874 {
9875  global $conf, $langs;
9876 
9877  $out = '';
9878  $url = $object->getLastMainDocLink($object->element);
9879 
9880  $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
9881  if ($url) {
9882  $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
9883  $out .= ajax_autoselect("directdownloadlink", 0);
9884  } else {
9885  $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
9886  }
9887 
9888  return $out;
9889 }
9890 
9899 function getImageFileNameForSize($file, $extName, $extImgTarget = '')
9900 {
9901  $dirName = dirname($file);
9902  if ($dirName == '.') {
9903  $dirName = '';
9904  }
9905 
9906  $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
9907  $fileName = basename($fileName);
9908 
9909  if (empty($extImgTarget)) {
9910  $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
9911  }
9912  if (empty($extImgTarget)) {
9913  $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
9914  }
9915  if (empty($extImgTarget)) {
9916  $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
9917  }
9918  if (empty($extImgTarget)) {
9919  $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
9920  }
9921  if (empty($extImgTarget)) {
9922  $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
9923  }
9924  if (empty($extImgTarget)) {
9925  $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
9926  }
9927 
9928  if (!$extImgTarget) {
9929  return $file;
9930  }
9931 
9932  $subdir = '';
9933  if ($extName) {
9934  $subdir = 'thumbs/';
9935  }
9936 
9937  return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
9938 }
9939 
9940 
9950 function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
9951 {
9952  global $conf, $langs;
9953 
9954  if (empty($conf->use_javascript_ajax)) {
9955  return '';
9956  }
9957 
9958  $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
9959 
9960  if ($alldata == 1) {
9961  if ($isAllowedForPreview) {
9962  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));
9963  } else {
9964  return array();
9965  }
9966  }
9967 
9968  // old behavior, return a string
9969  if ($isAllowedForPreview) {
9970  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')).'\')';
9971  } else {
9972  return '';
9973  }
9974 }
9975 
9976 
9985 function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
9986 {
9987  global $langs;
9988  $out = '<script>
9989  jQuery(document).ready(function () {
9990  jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
9991  });
9992  </script>';
9993  if ($addlink) {
9994  if ($textonlink === 'image') {
9995  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
9996  } else {
9997  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
9998  }
9999  }
10000  return $out;
10001 }
10002 
10010 function dolIsAllowedForPreview($file)
10011 {
10012  global $conf;
10013 
10014  // Check .noexe extension in filename
10015  if (preg_match('/\.noexe$/i', $file)) {
10016  return 0;
10017  }
10018 
10019  // Check mime types
10020  $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
10021  if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) {
10022  $mime_preview[] = 'svg+xml';
10023  }
10024  //$mime_preview[]='vnd.oasis.opendocument.presentation';
10025  //$mime_preview[]='archive';
10026  $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
10027  if ($num_mime !== false) {
10028  return 1;
10029  }
10030 
10031  // By default, not allowed for preview
10032  return 0;
10033 }
10034 
10035 
10045 function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
10046 {
10047  $mime = $default;
10048  $imgmime = 'other.png';
10049  $famime = 'file-o';
10050  $srclang = '';
10051 
10052  $tmpfile = preg_replace('/\.noexe$/', '', $file);
10053 
10054  // Plain text files
10055  if (preg_match('/\.txt$/i', $tmpfile)) {
10056  $mime = 'text/plain';
10057  $imgmime = 'text.png';
10058  $famime = 'file-text-o';
10059  }
10060  if (preg_match('/\.rtx$/i', $tmpfile)) {
10061  $mime = 'text/richtext';
10062  $imgmime = 'text.png';
10063  $famime = 'file-text-o';
10064  }
10065  if (preg_match('/\.csv$/i', $tmpfile)) {
10066  $mime = 'text/csv';
10067  $imgmime = 'text.png';
10068  $famime = 'file-text-o';
10069  }
10070  if (preg_match('/\.tsv$/i', $tmpfile)) {
10071  $mime = 'text/tab-separated-values';
10072  $imgmime = 'text.png';
10073  $famime = 'file-text-o';
10074  }
10075  if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
10076  $mime = 'text/plain';
10077  $imgmime = 'text.png';
10078  $famime = 'file-text-o';
10079  }
10080  if (preg_match('/\.ini$/i', $tmpfile)) {
10081  $mime = 'text/plain';
10082  $imgmime = 'text.png';
10083  $srclang = 'ini';
10084  $famime = 'file-text-o';
10085  }
10086  if (preg_match('/\.md$/i', $tmpfile)) {
10087  $mime = 'text/plain';
10088  $imgmime = 'text.png';
10089  $srclang = 'md';
10090  $famime = 'file-text-o';
10091  }
10092  if (preg_match('/\.css$/i', $tmpfile)) {
10093  $mime = 'text/css';
10094  $imgmime = 'css.png';
10095  $srclang = 'css';
10096  $famime = 'file-text-o';
10097  }
10098  if (preg_match('/\.lang$/i', $tmpfile)) {
10099  $mime = 'text/plain';
10100  $imgmime = 'text.png';
10101  $srclang = 'lang';
10102  $famime = 'file-text-o';
10103  }
10104  // Certificate files
10105  if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) {
10106  $mime = 'text/plain';
10107  $imgmime = 'text.png';
10108  $famime = 'file-text-o';
10109  }
10110  // XML based (HTML/XML/XAML)
10111  if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) {
10112  $mime = 'text/html';
10113  $imgmime = 'html.png';
10114  $srclang = 'html';
10115  $famime = 'file-text-o';
10116  }
10117  if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
10118  $mime = 'text/xml';
10119  $imgmime = 'other.png';
10120  $srclang = 'xml';
10121  $famime = 'file-text-o';
10122  }
10123  if (preg_match('/\.xaml$/i', $tmpfile)) {
10124  $mime = 'text/xml';
10125  $imgmime = 'other.png';
10126  $srclang = 'xaml';
10127  $famime = 'file-text-o';
10128  }
10129  // Languages
10130  if (preg_match('/\.bas$/i', $tmpfile)) {
10131  $mime = 'text/plain';
10132  $imgmime = 'text.png';
10133  $srclang = 'bas';
10134  $famime = 'file-code-o';
10135  }
10136  if (preg_match('/\.(c)$/i', $tmpfile)) {
10137  $mime = 'text/plain';
10138  $imgmime = 'text.png';
10139  $srclang = 'c';
10140  $famime = 'file-code-o';
10141  }
10142  if (preg_match('/\.(cpp)$/i', $tmpfile)) {
10143  $mime = 'text/plain';
10144  $imgmime = 'text.png';
10145  $srclang = 'cpp';
10146  $famime = 'file-code-o';
10147  }
10148  if (preg_match('/\.cs$/i', $tmpfile)) {
10149  $mime = 'text/plain';
10150  $imgmime = 'text.png';
10151  $srclang = 'cs';
10152  $famime = 'file-code-o';
10153  }
10154  if (preg_match('/\.(h)$/i', $tmpfile)) {
10155  $mime = 'text/plain';
10156  $imgmime = 'text.png';
10157  $srclang = 'h';
10158  $famime = 'file-code-o';
10159  }
10160  if (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
10161  $mime = 'text/plain';
10162  $imgmime = 'text.png';
10163  $srclang = 'java';
10164  $famime = 'file-code-o';
10165  }
10166  if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
10167  $mime = 'text/plain';
10168  $imgmime = 'php.png';
10169  $srclang = 'php';
10170  $famime = 'file-code-o';
10171  }
10172  if (preg_match('/\.phtml$/i', $tmpfile)) {
10173  $mime = 'text/plain';
10174  $imgmime = 'php.png';
10175  $srclang = 'php';
10176  $famime = 'file-code-o';
10177  }
10178  if (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
10179  $mime = 'text/plain';
10180  $imgmime = 'pl.png';
10181  $srclang = 'perl';
10182  $famime = 'file-code-o';
10183  }
10184  if (preg_match('/\.sql$/i', $tmpfile)) {
10185  $mime = 'text/plain';
10186  $imgmime = 'text.png';
10187  $srclang = 'sql';
10188  $famime = 'file-code-o';
10189  }
10190  if (preg_match('/\.js$/i', $tmpfile)) {
10191  $mime = 'text/x-javascript';
10192  $imgmime = 'jscript.png';
10193  $srclang = 'js';
10194  $famime = 'file-code-o';
10195  }
10196  // Open office
10197  if (preg_match('/\.odp$/i', $tmpfile)) {
10198  $mime = 'application/vnd.oasis.opendocument.presentation';
10199  $imgmime = 'ooffice.png';
10200  $famime = 'file-powerpoint-o';
10201  }
10202  if (preg_match('/\.ods$/i', $tmpfile)) {
10203  $mime = 'application/vnd.oasis.opendocument.spreadsheet';
10204  $imgmime = 'ooffice.png';
10205  $famime = 'file-excel-o';
10206  }
10207  if (preg_match('/\.odt$/i', $tmpfile)) {
10208  $mime = 'application/vnd.oasis.opendocument.text';
10209  $imgmime = 'ooffice.png';
10210  $famime = 'file-word-o';
10211  }
10212  // MS Office
10213  if (preg_match('/\.mdb$/i', $tmpfile)) {
10214  $mime = 'application/msaccess';
10215  $imgmime = 'mdb.png';
10216  $famime = 'file-o';
10217  }
10218  if (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
10219  $mime = 'application/msword';
10220  $imgmime = 'doc.png';
10221  $famime = 'file-word-o';
10222  }
10223  if (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
10224  $mime = 'application/msword';
10225  $imgmime = 'doc.png';
10226  $famime = 'file-word-o';
10227  }
10228  if (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
10229  $mime = 'application/vnd.ms-excel';
10230  $imgmime = 'xls.png';
10231  $famime = 'file-excel-o';
10232  }
10233  if (preg_match('/\.xla(m)?$/i', $tmpfile)) {
10234  $mime = 'application/vnd.ms-excel';
10235  $imgmime = 'xls.png';
10236  $famime = 'file-excel-o';
10237  }
10238  if (preg_match('/\.xls$/i', $tmpfile)) {
10239  $mime = 'application/vnd.ms-excel';
10240  $imgmime = 'xls.png';
10241  $famime = 'file-excel-o';
10242  }
10243  if (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
10244  $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
10245  $imgmime = 'xls.png';
10246  $famime = 'file-excel-o';
10247  }
10248  if (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
10249  $mime = 'application/vnd.ms-powerpoint';
10250  $imgmime = 'ppt.png';
10251  $famime = 'file-powerpoint-o';
10252  }
10253  if (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
10254  $mime = 'application/x-mspowerpoint';
10255  $imgmime = 'ppt.png';
10256  $famime = 'file-powerpoint-o';
10257  }
10258  // Other
10259  if (preg_match('/\.pdf$/i', $tmpfile)) {
10260  $mime = 'application/pdf';
10261  $imgmime = 'pdf.png';
10262  $famime = 'file-pdf-o';
10263  }
10264  // Scripts
10265  if (preg_match('/\.bat$/i', $tmpfile)) {
10266  $mime = 'text/x-bat';
10267  $imgmime = 'script.png';
10268  $srclang = 'dos';
10269  $famime = 'file-code-o';
10270  }
10271  if (preg_match('/\.sh$/i', $tmpfile)) {
10272  $mime = 'text/x-sh';
10273  $imgmime = 'script.png';
10274  $srclang = 'bash';
10275  $famime = 'file-code-o';
10276  }
10277  if (preg_match('/\.ksh$/i', $tmpfile)) {
10278  $mime = 'text/x-ksh';
10279  $imgmime = 'script.png';
10280  $srclang = 'bash';
10281  $famime = 'file-code-o';
10282  }
10283  if (preg_match('/\.bash$/i', $tmpfile)) {
10284  $mime = 'text/x-bash';
10285  $imgmime = 'script.png';
10286  $srclang = 'bash';
10287  $famime = 'file-code-o';
10288  }
10289  // Images
10290  if (preg_match('/\.ico$/i', $tmpfile)) {
10291  $mime = 'image/x-icon';
10292  $imgmime = 'image.png';
10293  $famime = 'file-image-o';
10294  }
10295  if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
10296  $mime = 'image/jpeg';
10297  $imgmime = 'image.png';
10298  $famime = 'file-image-o';
10299  }
10300  if (preg_match('/\.png$/i', $tmpfile)) {
10301  $mime = 'image/png';
10302  $imgmime = 'image.png';
10303  $famime = 'file-image-o';
10304  }
10305  if (preg_match('/\.gif$/i', $tmpfile)) {
10306  $mime = 'image/gif';
10307  $imgmime = 'image.png';
10308  $famime = 'file-image-o';
10309  }
10310  if (preg_match('/\.bmp$/i', $tmpfile)) {
10311  $mime = 'image/bmp';
10312  $imgmime = 'image.png';
10313  $famime = 'file-image-o';
10314  }
10315  if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
10316  $mime = 'image/tiff';
10317  $imgmime = 'image.png';
10318  $famime = 'file-image-o';
10319  }
10320  if (preg_match('/\.svg$/i', $tmpfile)) {
10321  $mime = 'image/svg+xml';
10322  $imgmime = 'image.png';
10323  $famime = 'file-image-o';
10324  }
10325  if (preg_match('/\.webp$/i', $tmpfile)) {
10326  $mime = 'image/webp';
10327  $imgmime = 'image.png';
10328  $famime = 'file-image-o';
10329  }
10330  // Calendar
10331  if (preg_match('/\.vcs$/i', $tmpfile)) {
10332  $mime = 'text/calendar';
10333  $imgmime = 'other.png';
10334  $famime = 'file-text-o';
10335  }
10336  if (preg_match('/\.ics$/i', $tmpfile)) {
10337  $mime = 'text/calendar';
10338  $imgmime = 'other.png';
10339  $famime = 'file-text-o';
10340  }
10341  // Other
10342  if (preg_match('/\.torrent$/i', $tmpfile)) {
10343  $mime = 'application/x-bittorrent';
10344  $imgmime = 'other.png';
10345  $famime = 'file-o';
10346  }
10347  // Audio
10348  if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) {
10349  $mime = 'audio';
10350  $imgmime = 'audio.png';
10351  $famime = 'file-audio-o';
10352  }
10353  // Video
10354  if (preg_match('/\.mp4$/i', $tmpfile)) {
10355  $mime = 'video/mp4';
10356  $imgmime = 'video.png';
10357  $famime = 'file-video-o';
10358  }
10359  if (preg_match('/\.ogv$/i', $tmpfile)) {
10360  $mime = 'video/ogg';
10361  $imgmime = 'video.png';
10362  $famime = 'file-video-o';
10363  }
10364  if (preg_match('/\.webm$/i', $tmpfile)) {
10365  $mime = 'video/webm';
10366  $imgmime = 'video.png';
10367  $famime = 'file-video-o';
10368  }
10369  if (preg_match('/\.avi$/i', $tmpfile)) {
10370  $mime = 'video/x-msvideo';
10371  $imgmime = 'video.png';
10372  $famime = 'file-video-o';
10373  }
10374  if (preg_match('/\.divx$/i', $tmpfile)) {
10375  $mime = 'video/divx';
10376  $imgmime = 'video.png';
10377  $famime = 'file-video-o';
10378  }
10379  if (preg_match('/\.xvid$/i', $tmpfile)) {
10380  $mime = 'video/xvid';
10381  $imgmime = 'video.png';
10382  $famime = 'file-video-o';
10383  }
10384  if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
10385  $mime = 'video';
10386  $imgmime = 'video.png';
10387  $famime = 'file-video-o';
10388  }
10389  // Archive
10390  if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) {
10391  $mime = 'archive';
10392  $imgmime = 'archive.png';
10393  $famime = 'file-archive-o';
10394  } // application/xxx where zzz is zip, ...
10395  // Exe
10396  if (preg_match('/\.(exe|com)$/i', $tmpfile)) {
10397  $mime = 'application/octet-stream';
10398  $imgmime = 'other.png';
10399  $famime = 'file-o';
10400  }
10401  // Lib
10402  if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) {
10403  $mime = 'library';
10404  $imgmime = 'library.png';
10405  $famime = 'file-o';
10406  }
10407  // Err
10408  if (preg_match('/\.err$/i', $tmpfile)) {
10409  $mime = 'error';
10410  $imgmime = 'error.png';
10411  $famime = 'file-text-o';
10412  }
10413 
10414  // Return string
10415  if ($mode == 1) {
10416  $tmp = explode('/', $mime);
10417  return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
10418  }
10419  if ($mode == 2) {
10420  return $imgmime;
10421  }
10422  if ($mode == 3) {
10423  return $srclang;
10424  }
10425  if ($mode == 4) {
10426  return $famime;
10427  }
10428  return $mime;
10429 }
10430 
10442 function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
10443 {
10444  global $conf, $db;
10445 
10446  $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
10447 
10448  $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
10449 
10450  if (is_null($dictvalues)) {
10451  $dictvalues = array();
10452 
10453  $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
10454  if ($checkentity) {
10455  $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
10456  }
10457 
10458  $resql = $db->query($sql);
10459  if ($resql) {
10460  while ($obj = $db->fetch_object($resql)) {
10461  $dictvalues[$obj->{$rowidfield}] = $obj; // $obj is stdClass
10462  }
10463  } else {
10464  dol_print_error($db);
10465  }
10466 
10467  $conf->cache['dictvalues_'.$tablename] = $dictvalues;
10468  }
10469 
10470  if (!empty($dictvalues[$id])) {
10471  // Found
10472  $tmp = $dictvalues[$id];
10473  return (property_exists($tmp, $field) ? $tmp->$field : '');
10474  } else {
10475  // Not found
10476  return '';
10477  }
10478 }
10479 
10486 function colorIsLight($stringcolor)
10487 {
10488  $stringcolor = str_replace('#', '', $stringcolor);
10489  $res = -1;
10490  if (!empty($stringcolor)) {
10491  $res = 0;
10492  $tmp = explode(',', $stringcolor);
10493  if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
10494  $r = $tmp[0];
10495  $g = $tmp[1];
10496  $b = $tmp[2];
10497  } else {
10498  $hexr = $stringcolor[0].$stringcolor[1];
10499  $hexg = $stringcolor[2].$stringcolor[3];
10500  $hexb = $stringcolor[4].$stringcolor[5];
10501  $r = hexdec($hexr);
10502  $g = hexdec($hexg);
10503  $b = hexdec($hexb);
10504  }
10505  $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
10506  if ($bright > 0.6) {
10507  $res = 1;
10508  }
10509  }
10510  return $res;
10511 }
10512 
10521 function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
10522 {
10523  global $conf;
10524 
10525  //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
10526  //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
10527  if (empty($menuentry['enabled'])) {
10528  return 0; // Entry disabled by condition
10529  }
10530  if ($type_user && $menuentry['module']) {
10531  $tmploops = explode('|', $menuentry['module']);
10532  $found = 0;
10533  foreach ($tmploops as $tmploop) {
10534  if (in_array($tmploop, $listofmodulesforexternal)) {
10535  $found++;
10536  break;
10537  }
10538  }
10539  if (!$found) {
10540  return 0; // Entry is for menus all excluded to external users
10541  }
10542  }
10543  if (!$menuentry['perms'] && $type_user) {
10544  return 0; // No permissions and user is external
10545  }
10546  if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) {
10547  return 0; // No permissions and option to hide when not allowed, even for internal user, is on
10548  }
10549  if (!$menuentry['perms']) {
10550  return 2; // No permissions and user is external
10551  }
10552  return 1;
10553 }
10554 
10562 function roundUpToNextMultiple($n, $x = 5)
10563 {
10564  return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
10565 }
10566 
10578 function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
10579 {
10580  $attr = array(
10581  'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css'])
10582  );
10583 
10584  if (empty($html)) {
10585  $html = $label;
10586  }
10587 
10588  if (!empty($url)) {
10589  $attr['href'] = $url;
10590  }
10591 
10592  if ($mode === 'dot') {
10593  $attr['class'] .= ' classfortooltip';
10594  $attr['title'] = $html;
10595  $attr['aria-label'] = $label;
10596  $html = '';
10597  }
10598 
10599  // Override attr
10600  if (!empty($params['attr']) && is_array($params['attr'])) {
10601  foreach ($params['attr'] as $key => $value) {
10602  if ($key == 'class') {
10603  $attr['class'] .= ' '.$value;
10604  } elseif ($key == 'classOverride') {
10605  $attr['class'] = $value;
10606  } else {
10607  $attr[$key] = $value;
10608  }
10609  }
10610  }
10611 
10612  // TODO: add hook
10613 
10614  // escape all attribute
10615  $attr = array_map('dol_escape_htmltag', $attr);
10616 
10617  $TCompiledAttr = array();
10618  foreach ($attr as $key => $value) {
10619  $TCompiledAttr[] = $key.'="'.$value.'"';
10620  }
10621 
10622  $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
10623 
10624  $tag = !empty($url) ? 'a' : 'span';
10625 
10626  return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
10627 }
10628 
10629 
10642 function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
10643 {
10644  global $conf;
10645 
10646  $return = '';
10647  $dolGetBadgeParams = array();
10648 
10649  if (!empty($params['badgeParams'])) {
10650  $dolGetBadgeParams = $params['badgeParams'];
10651  }
10652 
10653  // TODO : add a hook
10654  if ($displayMode == 0) {
10655  $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
10656  } elseif ($displayMode == 1) {
10657  $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10658  } elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) {
10659  // Use status with images (for backward compatibility)
10660  $return = '';
10661  $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
10662  $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>' : '');
10663 
10664  // For small screen, we always use the short label instead of long label.
10665  if (!empty($conf->dol_optimize_smallscreen)) {
10666  if ($displayMode == 0) {
10667  $displayMode = 1;
10668  } elseif ($displayMode == 4) {
10669  $displayMode = 2;
10670  } elseif ($displayMode == 6) {
10671  $displayMode = 5;
10672  }
10673  }
10674 
10675  // For backward compatibility. Image's filename are still in French, so we use this array to convert
10676  $statusImg = array(
10677  'status0' => 'statut0',
10678  'status1' => 'statut1',
10679  'status2' => 'statut2',
10680  'status3' => 'statut3',
10681  'status4' => 'statut4',
10682  'status5' => 'statut5',
10683  'status6' => 'statut6',
10684  'status7' => 'statut7',
10685  'status8' => 'statut8',
10686  'status9' => 'statut9'
10687  );
10688 
10689  if (!empty($statusImg[$statusType])) {
10690  $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
10691  } else {
10692  $htmlImg = img_picto($statusLabel, $statusType);
10693  }
10694 
10695  if ($displayMode === 2) {
10696  $return = $htmlImg.' '.$htmlLabelShort;
10697  } elseif ($displayMode === 3) {
10698  $return = $htmlImg;
10699  } elseif ($displayMode === 4) {
10700  $return = $htmlImg.' '.$htmlLabel;
10701  } elseif ($displayMode === 5) {
10702  $return = $htmlLabelShort.' '.$htmlImg;
10703  } else { // $displayMode >= 6
10704  $return = $htmlLabel.' '.$htmlImg;
10705  }
10706  } elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) {
10707  // Use new badge
10708  $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10709 
10710  $dolGetBadgeParams['attr']['class'] = 'badge-status';
10711  $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
10712 
10713  if ($displayMode == 3) {
10714  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
10715  } elseif ($displayMode === 5) {
10716  $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
10717  } else {
10718  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
10719  }
10720  }
10721 
10722  return $return;
10723 }
10724 
10725 
10755 function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
10756 {
10757  global $hookmanager, $action, $object, $langs;
10758 
10759  //var_dump($params);
10760  if (!empty($params['isDropdown']))
10761  $class = "dropdown-item";
10762  else {
10763  $class = 'butAction';
10764  if ($actionType == 'danger' || $actionType == 'delete') {
10765  $class = 'butActionDelete';
10766  if (!empty($url) && strpos($url, 'token=') === false) $url .= '&token='.newToken();
10767  }
10768  }
10769  $attr = array(
10770  'class' => $class,
10771  'href' => empty($url) ? '' : $url,
10772  'title' => $label
10773  );
10774 
10775  if (empty($text)) {
10776  $text = $label;
10777  $attr['title'] = ''; // if html not set, leave label on title is redundant
10778  } else {
10779  $attr['title'] = $label;
10780  $attr['aria-label'] = $label;
10781  }
10782 
10783  if (empty($userRight)) {
10784  $attr['class'] = 'butActionRefused';
10785  $attr['href'] = '';
10786  $attr['title'] = (($label && $text && $label != $text) ? $label : $langs->trans('NotEnoughPermissions'));
10787  }
10788 
10789  if (!empty($id)) {
10790  $attr['id'] = $id;
10791  }
10792 
10793  // Override attr
10794  if (!empty($params['attr']) && is_array($params['attr'])) {
10795  foreach ($params['attr'] as $key => $value) {
10796  if ($key == 'class') {
10797  $attr['class'] .= ' '.$value;
10798  } elseif ($key == 'classOverride') {
10799  $attr['class'] = $value;
10800  } else {
10801  $attr[$key] = $value;
10802  }
10803  }
10804  }
10805 
10806  // automatic add tooltip when title is detected
10807  if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
10808  $attr['class'].= ' classfortooltip';
10809  }
10810 
10811  // Js Confirm button
10812  if ($userRight && !empty($params['confirm'])) {
10813  if (!is_array($params['confirm'])) {
10814  $params['confirm'] = array();
10815  }
10816 
10817  if (empty($params['confirm']['url'])) {
10818  $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
10819  }
10820 
10821  // for js desabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
10822  $attr['data-confirm-url'] = $params['confirm']['url'];
10823  $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
10824  $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
10825  $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
10826  $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
10827  $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
10828  $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
10829 
10830  $attr['class'].= ' butActionConfirm';
10831  }
10832 
10833  if (isset($attr['href']) && empty($attr['href'])) {
10834  unset($attr['href']);
10835  }
10836 
10837  // escape all attribute
10838  $attr = array_map('dol_escape_htmltag', $attr);
10839 
10840  $TCompiledAttr = array();
10841  foreach ($attr as $key => $value) {
10842  $TCompiledAttr[] = $key.'= "'.$value.'"';
10843  }
10844 
10845  $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
10846 
10847  $tag = !empty($attr['href']) ? 'a' : 'span';
10848 
10849 
10850  $parameters = array(
10851  'TCompiledAttr' => $TCompiledAttr, // array
10852  'compiledAttributes' => $compiledAttributes, // string
10853  'attr' => $attr,
10854  'tag' => $tag,
10855  'label' => $label,
10856  'html' => $text,
10857  'actionType' => $actionType,
10858  'url' => $url,
10859  'id' => $id,
10860  'userRight' => $userRight,
10861  'params' => $params
10862  );
10863 
10864  $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
10865  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
10866 
10867  if (empty($reshook)) {
10868  if (dol_textishtml($text)) { // If content already HTML encoded
10869  return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
10870  } else {
10871  return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
10872  }
10873  } else {
10874  return $hookmanager->resPrint;
10875  }
10876 }
10877 
10884 function dolGetButtonTitleSeparator($moreClass = "")
10885 {
10886  return '<span class="button-title-separator '.$moreClass.'" ></span>';
10887 }
10888 
10895 function getFieldErrorIcon($fieldValidationErrorMsg)
10896 {
10897  $out = '';
10898  if (!empty($fieldValidationErrorMsg)) {
10899  $out.= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
10900  $out.= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
10901  $out.= '</span>';
10902  }
10903 
10904  return $out;
10905 }
10906 
10919 function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
10920 {
10921  global $langs, $conf, $user;
10922 
10923  // Actually this conf is used in css too for external module compatibility and smooth transition to this function
10924  if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) {
10925  return '';
10926  }
10927 
10928  $class = 'btnTitle';
10929  if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
10930  $class .= ' btnTitlePlus';
10931  }
10932  $useclassfortooltip = 1;
10933 
10934  if (!empty($params['morecss'])) {
10935  $class .= ' '.$params['morecss'];
10936  }
10937 
10938  $attr = array(
10939  'class' => $class,
10940  'href' => empty($url) ? '' : $url
10941  );
10942 
10943  if (!empty($helpText)) {
10944  $attr['title'] = dol_escape_htmltag($helpText);
10945  } elseif (empty($attr['title']) && $label) {
10946  $attr['title'] = $label;
10947  $useclassfortooltip = 0;
10948  }
10949 
10950  if ($status == 2) {
10951  $attr['class'] .= ' btnTitleSelected';
10952  } elseif ($status <= 0) {
10953  $attr['class'] .= ' refused';
10954 
10955  $attr['href'] = '';
10956 
10957  if ($status == -1) { // disable
10958  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
10959  } elseif ($status == 0) { // Not enough permissions
10960  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
10961  }
10962  }
10963 
10964  if (!empty($attr['title']) && $useclassfortooltip) {
10965  $attr['class'] .= ' classfortooltip';
10966  }
10967 
10968  if (!empty($id)) {
10969  $attr['id'] = $id;
10970  }
10971 
10972  // Override attr
10973  if (!empty($params['attr']) && is_array($params['attr'])) {
10974  foreach ($params['attr'] as $key => $value) {
10975  if ($key == 'class') {
10976  $attr['class'] .= ' '.$value;
10977  } elseif ($key == 'classOverride') {
10978  $attr['class'] = $value;
10979  } else {
10980  $attr[$key] = $value;
10981  }
10982  }
10983  }
10984 
10985  if (isset($attr['href']) && empty($attr['href'])) {
10986  unset($attr['href']);
10987  }
10988 
10989  // TODO : add a hook
10990 
10991  // escape all attribute
10992  $attr = array_map('dol_escape_htmltag', $attr);
10993 
10994  $TCompiledAttr = array();
10995  foreach ($attr as $key => $value) {
10996  $TCompiledAttr[] = $key.'="'.$value.'"';
10997  }
10998 
10999  $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
11000 
11001  $tag = (empty($attr['href']) ? 'span' : 'a');
11002 
11003  $button = '<'.$tag.' '.$compiledAttributes.'>';
11004  $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
11005  if (!empty($params['forcenohideoftext'])) {
11006  $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
11007  }
11008  $button .= '</'.$tag.'>';
11009 
11010  return $button;
11011 }
11012 
11020 function getElementProperties($element_type)
11021 {
11022  $regs = array();
11023 
11024  $classfile = $classname = $classpath = '';
11025 
11026  // Parse element/subelement (ex: project_task)
11027  $module = $element_type;
11028  $element = $element_type;
11029  $subelement = $element_type;
11030 
11031  // If we ask an resource form external module (instead of default path)
11032  if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) {
11033  $element = $subelement = $regs[1];
11034  $module = $regs[2];
11035  }
11036 
11037  //print '<br>1. element : '.$element.' - module : '.$module .'<br>';
11038  if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) {
11039  $module = $element = $regs[1];
11040  $subelement = $regs[2];
11041  }
11042 
11043  // For compat
11044  if ($element_type == "action") {
11045  $classpath = 'comm/action/class';
11046  $subelement = 'Actioncomm';
11047  $module = 'agenda';
11048  }
11049 
11050  // To work with non standard path
11051  if ($element_type == 'facture' || $element_type == 'invoice') {
11052  $classpath = 'compta/facture/class';
11053  $module = 'facture';
11054  $subelement = 'facture';
11055  }
11056  if ($element_type == 'commande' || $element_type == 'order') {
11057  $classpath = 'commande/class';
11058  $module = 'commande';
11059  $subelement = 'commande';
11060  }
11061  if ($element_type == 'propal') {
11062  $classpath = 'comm/propal/class';
11063  }
11064  if ($element_type == 'supplier_proposal') {
11065  $classpath = 'supplier_proposal/class';
11066  }
11067  if ($element_type == 'shipping') {
11068  $classpath = 'expedition/class';
11069  $subelement = 'expedition';
11070  $module = 'expedition_bon';
11071  }
11072  if ($element_type == 'delivery') {
11073  $classpath = 'delivery/class';
11074  $subelement = 'delivery';
11075  $module = 'delivery_note';
11076  }
11077  if ($element_type == 'contract') {
11078  $classpath = 'contrat/class';
11079  $module = 'contrat';
11080  $subelement = 'contrat';
11081  }
11082  if ($element_type == 'member') {
11083  $classpath = 'adherents/class';
11084  $module = 'adherent';
11085  $subelement = 'adherent';
11086  }
11087  if ($element_type == 'cabinetmed_cons') {
11088  $classpath = 'cabinetmed/class';
11089  $module = 'cabinetmed';
11090  $subelement = 'cabinetmedcons';
11091  }
11092  if ($element_type == 'fichinter') {
11093  $classpath = 'fichinter/class';
11094  $module = 'ficheinter';
11095  $subelement = 'fichinter';
11096  }
11097  if ($element_type == 'dolresource' || $element_type == 'resource') {
11098  $classpath = 'resource/class';
11099  $module = 'resource';
11100  $subelement = 'dolresource';
11101  }
11102  if ($element_type == 'propaldet') {
11103  $classpath = 'comm/propal/class';
11104  $module = 'propal';
11105  $subelement = 'propaleligne';
11106  }
11107  if ($element_type == 'order_supplier') {
11108  $classpath = 'fourn/class';
11109  $module = 'fournisseur';
11110  $subelement = 'commandefournisseur';
11111  $classfile = 'fournisseur.commande';
11112  }
11113  if ($element_type == 'invoice_supplier') {
11114  $classpath = 'fourn/class';
11115  $module = 'fournisseur';
11116  $subelement = 'facturefournisseur';
11117  $classfile = 'fournisseur.facture';
11118  }
11119  if ($element_type == "service") {
11120  $classpath = 'product/class';
11121  $subelement = 'product';
11122  }
11123 
11124  if (empty($classfile)) {
11125  $classfile = strtolower($subelement);
11126  }
11127  if (empty($classname)) {
11128  $classname = ucfirst($subelement);
11129  }
11130  if (empty($classpath)) {
11131  $classpath = $module.'/class';
11132  }
11133 
11134  $element_properties = array(
11135  'module' => $module,
11136  'classpath' => $classpath,
11137  'element' => $element,
11138  'subelement' => $subelement,
11139  'classfile' => $classfile,
11140  'classname' => $classname
11141  );
11142  return $element_properties;
11143 }
11144 
11154 function fetchObjectByElement($element_id, $element_type, $element_ref = '')
11155 {
11156  global $conf, $db;
11157 
11158  $element_prop = getElementProperties($element_type);
11159  if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled) {
11160  dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
11161 
11162  $objecttmp = new $element_prop['classname']($db);
11163  $ret = $objecttmp->fetch($element_id, $element_ref);
11164  if ($ret >= 0) {
11165  return $objecttmp;
11166  }
11167  }
11168  return 0;
11169 }
11170 
11177 function isAFileWithExecutableContent($filename)
11178 {
11179  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)) {
11180  return true;
11181  }
11182 
11183  return false;
11184 }
11185 
11192 function newToken()
11193 {
11194  return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
11195 }
11196 
11203 function currentToken()
11204 {
11205  return isset($_SESSION['token']) ? $_SESSION['token'] : '';
11206 }
11207 
11220 function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1)
11221 {
11222  global $langs;
11223 
11224  print '<div class="div-table-responsive-no-min">';
11225  print '<table class="noborder centpercent">';
11226  print '<tr class="liste_titre">';
11227 
11228  print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">';
11229 
11230  print $langs->trans($header);
11231 
11232  // extra space between the first header and the number
11233  if ($number > -1) {
11234  print ' ';
11235  }
11236 
11237  if (!empty($link)) {
11238  if (!empty($arguments)) {
11239  print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11240  } else {
11241  print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
11242  }
11243  }
11244 
11245  if ($number > -1) {
11246  print '<span class="badge">'.$number.'</span>';
11247  }
11248 
11249  if (!empty($link)) {
11250  print '</a>';
11251  }
11252 
11253  print '</th>';
11254 
11255  if ($number < 0 && !empty($link)) {
11256  print '<th class="right">';
11257 
11258  if (!empty($arguments)) {
11259  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11260  } else {
11261  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">';
11262  }
11263 
11264  print $langs->trans("FullList");
11265  print '</a>';
11266  print '</th>';
11267  }
11268 
11269  print '</tr>';
11270 }
11271 
11280 function finishSimpleTable($addLineBreak = false)
11281 {
11282  print '</table>';
11283  print '</div>';
11284 
11285  if ($addLineBreak) {
11286  print '<br>';
11287  }
11288 }
11289 
11301 function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
11302 {
11303  global $langs;
11304 
11305  if ($num === 0) {
11306  print '<tr class="oddeven">';
11307  print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>';
11308  print '</tr>';
11309  return;
11310  }
11311 
11312  if ($nbofloop === 0) {
11313  // don't show a summary line
11314  return;
11315  }
11316 
11317  if ($num === 0) {
11318  $colspan = $tableColumnCount;
11319  } elseif ($num > $nbofloop) {
11320  $colspan = $tableColumnCount;
11321  } else {
11322  $colspan = $tableColumnCount - 1;
11323  }
11324 
11325  if ($extraRightColumn) {
11326  $colspan--;
11327  }
11328 
11329  print '<tr class="liste_total">';
11330 
11331  if ($nbofloop > 0 && $num > $nbofloop) {
11332  print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
11333  } else {
11334  print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
11335  print '<td class="right" width="100">'.price($total).'</td>';
11336  }
11337 
11338  if ($extraRightColumn) {
11339  print '<td></td>';
11340  }
11341 
11342  print '</tr>';
11343 }
11344 
11353 function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
11354 {
11355  global $conf;
11356 
11357  if ($method == -1) {
11358  $method = 0;
11359  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) {
11360  $method = 1;
11361  }
11362  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) {
11363  $method = 2;
11364  }
11365  }
11366 
11367  // Be sure we don't have output buffering enabled to have readfile working correctly
11368  while (ob_get_level()) {
11369  ob_end_flush();
11370  }
11371 
11372  // Solution 0
11373  if ($method == 0) {
11374  readfile($fullpath_original_file_osencoded);
11375  } elseif ($method == 1) {
11376  // Solution 1
11377  $handle = fopen($fullpath_original_file_osencoded, "rb");
11378  while (!feof($handle)) {
11379  print fread($handle, 8192);
11380  }
11381  fclose($handle);
11382  } elseif ($method == 2) {
11383  // Solution 2
11384  $handle1 = fopen($fullpath_original_file_osencoded, "rb");
11385  $handle2 = fopen("php://output", "wb");
11386  stream_copy_to_stream($handle1, $handle2);
11387  fclose($handle1);
11388  fclose($handle2);
11389  }
11390 }
11391 
11401 function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
11402 {
11403  /*
11404  global $conf;
11405 
11406  if (!empty($conf->dol_no_mouse_hover)) {
11407  $showonlyonhover = 0;
11408  }*/
11409 
11410  $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
11411  if ($texttoshow === 'none') {
11412  $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>';
11413  } elseif ($texttoshow) {
11414  $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>';
11415  } else {
11416  $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>';
11417  }
11418 
11419  return $result;
11420 }
11421 
11422 
11429 function jsonOrUnserialize($stringtodecode)
11430 {
11431  $result = json_decode($stringtodecode);
11432  if ($result === null) {
11433  $result = unserialize($stringtodecode);
11434  }
11435 
11436  return $result;
11437 }
11438 
11439 
11455 function dolCheckFilters($sqlfilters, &$error = '')
11456 {
11457  //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
11458  //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
11459  $tmp = $sqlfilters;
11460  $i = 0; $nb = strlen($tmp);
11461  $counter = 0;
11462  while ($i < $nb) {
11463  if ($tmp[$i] == '(') {
11464  $counter++;
11465  }
11466  if ($tmp[$i] == ')') {
11467  $counter--;
11468  }
11469  if ($counter < 0) {
11470  $error = "Bad sqlfilters=".$sqlfilters;
11471  dol_syslog($error, LOG_WARNING);
11472  return false;
11473  }
11474  $i++;
11475  }
11476  return true;
11477 }
11478 
11494 function dolForgeCriteriaCallback($matches)
11495 {
11496  global $db;
11497 
11498  dol_syslog("Convert matches ".$matches[1]);
11499  if (empty($matches[1])) {
11500  return '';
11501  }
11502  $tmp = explode(':', $matches[1], 3);
11503 
11504  if (count($tmp) < 3) {
11505  return '';
11506  }
11507 
11508  $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
11509 
11510  $operator = strtoupper(preg_replace('/[^a-z<>=]/i', '', trim($tmp[1])));
11511  if ($operator == 'NOTLIKE') {
11512  $operator = 'NOT LIKE';
11513  }
11514 
11515  $tmpescaped = trim($tmp[2]);
11516  $regbis = array();
11517  if ($operator == 'IN') {
11518  $tmpescaped = "(".$db->sanitize($tmpescaped, 1).")";
11519  } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
11520  $tmpescaped = "'".$db->escape($regbis[1])."'";
11521  } else {
11522  $tmpescaped = $db->sanitize($db->escape($tmpescaped));
11523  }
11524 
11525  return $db->escape($operand).' '.$db->escape($operator)." ".$tmpescaped;
11526 }
11527 
11528 
11529 
11537 function getTimelineIcon($actionstatic, &$histo, $key)
11538 {
11539  global $conf, $langs;
11540  $out = '<!-- timeline icon -->'."\n";
11541  $iconClass = 'fa fa-comments';
11542  $img_picto = '';
11543  $colorClass = '';
11544  $pictoTitle = '';
11545 
11546  if ($histo[$key]['percent'] == -1) {
11547  $colorClass = 'timeline-icon-not-applicble';
11548  $pictoTitle = $langs->trans('StatusNotApplicable');
11549  } elseif ($histo[$key]['percent'] == 0) {
11550  $colorClass = 'timeline-icon-todo';
11551  $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
11552  } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
11553  $colorClass = 'timeline-icon-in-progress';
11554  $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
11555  } elseif ($histo[$key]['percent'] >= 100) {
11556  $colorClass = 'timeline-icon-done';
11557  $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
11558  }
11559 
11560  if ($actionstatic->code == 'AC_TICKET_CREATE') {
11561  $iconClass = 'fa fa-ticket';
11562  } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
11563  $iconClass = 'fa fa-pencilxxx';
11564  } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
11565  $iconClass = 'fa fa-comments';
11566  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
11567  $iconClass = 'fa fa-mask';
11568  } elseif (!empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
11569  if ($actionstatic->type_picto) {
11570  $img_picto = img_picto('', $actionstatic->type_picto);
11571  } else {
11572  if ($actionstatic->type_code == 'AC_RDV') {
11573  $iconClass = 'fa fa-handshake';
11574  } elseif ($actionstatic->type_code == 'AC_TEL') {
11575  $iconClass = 'fa fa-phone';
11576  } elseif ($actionstatic->type_code == 'AC_FAX') {
11577  $iconClass = 'fa fa-fax';
11578  } elseif ($actionstatic->type_code == 'AC_EMAIL') {
11579  $iconClass = 'fa fa-envelope';
11580  } elseif ($actionstatic->type_code == 'AC_INT') {
11581  $iconClass = 'fa fa-shipping-fast';
11582  } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
11583  $iconClass = 'fa fa-robot';
11584  } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
11585  $iconClass = 'fa fa-robot';
11586  }
11587  }
11588  }
11589 
11590  $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
11591  return $out;
11592 }
11593 
11600 function getActionCommEcmList($object)
11601 {
11602  global $conf, $db;
11603 
11604  $documents = array();
11605 
11606  $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
11607  $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
11608  $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
11609  //$sql.= " ecm.src_object_type = '".$db->escape($object->element)."' AND ecm.src_object_id = ".((int) $object->id); // Old version didn't add object_type during upload
11610  $sql .= ' ORDER BY ecm.position ASC';
11611 
11612  $resql = $db->query($sql);
11613  if ($resql) {
11614  if ($db->num_rows($resql)) {
11615  while ($obj = $db->fetch_object($resql)) {
11616  $documents[$obj->id] = $obj;
11617  }
11618  }
11619  }
11620 
11621  return $documents;
11622 }
11623 
11624 
11625 
11643 function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = '', $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
11644 {
11645  global $user, $conf;
11646  global $form;
11647 
11648  global $param, $massactionbutton;
11649 
11650  dol_include_once('/comm/action/class/actioncomm.class.php');
11651 
11652  // Check parameters
11653  if (!is_object($filterobj) && !is_object($objcon)) {
11654  dol_print_error('', 'BadParameter');
11655  }
11656 
11657  $histo = array();
11658  $numaction = 0;
11659  $now = dol_now();
11660 
11661  $sortfield_list = explode(',', $sortfield);
11662  $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
11663  $sortfield_new_list = array();
11664  foreach ($sortfield_list as $sortfield_value) {
11665  $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
11666  }
11667  $sortfield_new = implode(',', $sortfield_new_list);
11668 
11669  if (isModEnabled('agenda')) {
11670  // Search histo on actioncomm
11671  if (is_object($objcon) && $objcon->id > 0) {
11672  $sql = "SELECT DISTINCT a.id, a.label as label,";
11673  } else {
11674  $sql = "SELECT a.id, a.label as label,";
11675  }
11676  $sql .= " a.datep as dp,";
11677  $sql .= " a.note as message,";
11678  $sql .= " a.datep2 as dp2,";
11679  $sql .= " a.percent as percent, 'action' as type,";
11680  $sql .= " a.fk_element, a.elementtype,";
11681  $sql .= " a.fk_contact,";
11682  $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
11683  $sql .= " u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname";
11684  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
11685  $sql .= ", sp.lastname, sp.firstname";
11686  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11687  $sql .= ", m.lastname, m.firstname";
11688  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11689  $sql .= ", o.ref";
11690  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11691  $sql .= ", o.ref";
11692  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11693  $sql .= ", o.ref";
11694  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
11695  $sql .= ", o.ref";
11696  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
11697  $sql .= ", o.ref";
11698  }
11699  $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
11700  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
11701  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
11702 
11703  $force_filter_contact = false;
11704  if (is_object($objcon) && $objcon->id > 0) {
11705  $force_filter_contact = true;
11706  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
11707  $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
11708  }
11709 
11710  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
11711  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
11712  } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
11713  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
11714  $sql .= " ON er.resource_type = 'dolresource'";
11715  $sql .= " AND er.element_id = a.id";
11716  $sql .= " AND er.resource_id = ".((int) $filterobj->id);
11717  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11718  $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
11719  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11720  $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
11721  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11722  $sql .= ", ".MAIN_DB_PREFIX."product as o";
11723  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11724  $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
11725  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
11726  $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
11727  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
11728  $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
11729  }
11730 
11731  $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
11732  if ($force_filter_contact === false) {
11733  if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
11734  $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
11735  } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
11736  $sql .= " AND a.fk_project = ".((int) $filterobj->id);
11737  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11738  $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
11739  if ($filterobj->id) {
11740  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11741  }
11742  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11743  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
11744  if ($filterobj->id) {
11745  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11746  }
11747  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11748  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
11749  if ($filterobj->id) {
11750  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11751  }
11752  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11753  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
11754  if ($filterobj->id) {
11755  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11756  }
11757  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
11758  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
11759  if ($filterobj->id) {
11760  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11761  }
11762  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
11763  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
11764  if ($filterobj->id) {
11765  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11766  }
11767  }
11768  }
11769 
11770  // Condition on actioncode
11771  if (!empty($actioncode)) {
11772  if (empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
11773  if ($actioncode == 'AC_NON_AUTO') {
11774  $sql .= " AND c.type != 'systemauto'";
11775  } elseif ($actioncode == 'AC_ALL_AUTO') {
11776  $sql .= " AND c.type = 'systemauto'";
11777  } else {
11778  if ($actioncode == 'AC_OTH') {
11779  $sql .= " AND c.type != 'systemauto'";
11780  } elseif ($actioncode == 'AC_OTH_AUTO') {
11781  $sql .= " AND c.type = 'systemauto'";
11782  }
11783  }
11784  } else {
11785  if ($actioncode == 'AC_NON_AUTO') {
11786  $sql .= " AND c.type != 'systemauto'";
11787  } elseif ($actioncode == 'AC_ALL_AUTO') {
11788  $sql .= " AND c.type = 'systemauto'";
11789  } else {
11790  $sql .= " AND c.code = '".$db->escape($actioncode)."'";
11791  }
11792  }
11793  }
11794  if ($donetodo == 'todo') {
11795  $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
11796  } elseif ($donetodo == 'done') {
11797  $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
11798  }
11799  if (is_array($filters) && $filters['search_agenda_label']) {
11800  $sql .= natural_search('a.label', $filters['search_agenda_label']);
11801  }
11802  }
11803 
11804  // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
11805  if (isModEnabled('mailing') && !empty($objcon->email)
11806  && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
11807  $langs->load("mails");
11808 
11809  $sql2 = "SELECT m.rowid as id, m.titre as label, mc.date_envoi as dp, mc.date_envoi as dp2, '100' as percent, 'mailing' as type";
11810  $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
11811  $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
11812  $sql2 .= ", u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname"; // User that valid action
11813  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
11814  $sql2 .= ", '' as lastname, '' as firstname";
11815  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11816  $sql2 .= ", '' as lastname, '' as firstname";
11817  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11818  $sql2 .= ", '' as ref";
11819  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11820  $sql2 .= ", '' as ref";
11821  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11822  $sql2 .= ", '' as ref";
11823  }
11824  $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
11825  $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
11826  $sql2 .= " AND mc.statut = 1";
11827  $sql2 .= " AND u.rowid = m.fk_user_valid";
11828  $sql2 .= " AND mc.fk_mailing=m.rowid";
11829  }
11830 
11831  if (!empty($sql) && !empty($sql2)) {
11832  $sql = $sql." UNION ".$sql2;
11833  } elseif (empty($sql) && !empty($sql2)) {
11834  $sql = $sql2;
11835  }
11836 
11837  // TODO Add limit in nb of results
11838  if ($sql) { // May not be defined if module Agenda is not enabled and mailing module disabled too
11839  $sql .= $db->order($sortfield_new, $sortorder);
11840 
11841  dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
11842  $resql = $db->query($sql);
11843  if ($resql) {
11844  $i = 0;
11845  $num = $db->num_rows($resql);
11846 
11847  while ($i < $num) {
11848  $obj = $db->fetch_object($resql);
11849 
11850  if ($obj->type == 'action') {
11851  $contactaction = new ActionComm($db);
11852  $contactaction->id = $obj->id;
11853  $result = $contactaction->fetchResources();
11854  if ($result < 0) {
11855  dol_print_error($db);
11856  setEventMessage("actions.lib::show_actions_messaging Error fetch ressource", 'errors');
11857  }
11858 
11859  //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
11860  //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
11861  $tododone = '';
11862  if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
11863  $tododone = 'todo';
11864  }
11865 
11866  $histo[$numaction] = array(
11867  'type'=>$obj->type,
11868  'tododone'=>$tododone,
11869  'id'=>$obj->id,
11870  'datestart'=>$db->jdate($obj->dp),
11871  'dateend'=>$db->jdate($obj->dp2),
11872  'note'=>$obj->label,
11873  'message'=>$obj->message,
11874  'percent'=>$obj->percent,
11875 
11876  'userid'=>$obj->user_id,
11877  'login'=>$obj->user_login,
11878  'userfirstname'=>$obj->user_firstname,
11879  'userlastname'=>$obj->user_lastname,
11880  'userphoto'=>$obj->user_photo,
11881 
11882  'contact_id'=>$obj->fk_contact,
11883  'socpeopleassigned' => $contactaction->socpeopleassigned,
11884  'lastname'=>$obj->lastname,
11885  'firstname'=>$obj->firstname,
11886  'fk_element'=>$obj->fk_element,
11887  'elementtype'=>$obj->elementtype,
11888  // Type of event
11889  'acode'=>$obj->acode,
11890  'alabel'=>$obj->alabel,
11891  'libelle'=>$obj->alabel, // deprecated
11892  'apicto'=>$obj->apicto
11893  );
11894  } else {
11895  $histo[$numaction] = array(
11896  'type'=>$obj->type,
11897  'tododone'=>'done',
11898  'id'=>$obj->id,
11899  'datestart'=>$db->jdate($obj->dp),
11900  'dateend'=>$db->jdate($obj->dp2),
11901  'note'=>$obj->label,
11902  'message'=>$obj->message,
11903  'percent'=>$obj->percent,
11904  'acode'=>$obj->acode,
11905 
11906  'userid'=>$obj->user_id,
11907  'login'=>$obj->user_login,
11908  'userfirstname'=>$obj->user_firstname,
11909  'userlastname'=>$obj->user_lastname,
11910  'userphoto'=>$obj->user_photo
11911  );
11912  }
11913 
11914  $numaction++;
11915  $i++;
11916  }
11917  } else {
11918  dol_print_error($db);
11919  }
11920  }
11921 
11922  // Set $out to show events
11923  $out = '';
11924 
11925  if (!isModEnabled('agenda')) {
11926  $langs->loadLangs(array("admin", "errors"));
11927  $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
11928  }
11929 
11930  if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
11931  $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
11932 
11933  require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
11934  include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
11935  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
11936  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
11937 
11938  $formactions = new FormActions($db);
11939 
11940  $actionstatic = new ActionComm($db);
11941  $userstatic = new User($db);
11942  $contactstatic = new Contact($db);
11943  $userGetNomUrlCache = array();
11944 
11945  $out .= '<div class="filters-container" >';
11946  $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
11947  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
11948 
11949  if ($objcon && get_class($objcon) == 'Contact' &&
11950  (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
11951  $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
11952  } else {
11953  $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
11954  }
11955  if ($filterobj && get_class($filterobj) == 'Societe') {
11956  $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
11957  }
11958 
11959  $out .= "\n";
11960 
11961  $out .= '<div class="div-table-responsive-no-min">';
11962  $out .= '<table class="noborder borderbottom centpercent">';
11963 
11964  $out .= '<tr class="liste_titre">';
11965 
11966  $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
11967 
11968  $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
11969  if ($donetodo) {
11970  $out .= '<th class="liste_titre"></th>';
11971  }
11972  $out .= '<th class="liste_titre">';
11973  $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
11974  //$out .= img_picto($langs->trans("Type"), 'type');
11975  $out .= $formactions->select_type_actions($actioncode, "actioncode", '', empty($conf->global->AGENDA_USE_EVENT_TYPE) ? 1 : -1, 0, 0, 1, 'minwidth200imp');
11976  $out .= '</th>';
11977  $out .= '<th class="liste_titre maxwidth100onsmartphone">';
11978  $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
11979  $out .= '</th>';
11980 
11981  $out .= '<th class="liste_titre width50 middle">';
11982  $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
11983  $out .= $searchpicto;
11984  $out .= '</th>';
11985  $out .= '</tr>';
11986 
11987 
11988  $out .= '</table>';
11989 
11990  $out .= '</form>';
11991  $out .= '</div>';
11992 
11993  $out .= "\n";
11994 
11995  $out .= '<ul class="timeline">';
11996 
11997  if ($donetodo) {
11998  $tmp = '';
11999  if (get_class($filterobj) == 'Societe') {
12000  $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
12001  }
12002  $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
12003  $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
12004  $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
12005  //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
12006  if (get_class($filterobj) == 'Societe') {
12007  $tmp .= '</a>';
12008  }
12009  $out .= getTitleFieldOfList($tmp);
12010  }
12011 
12012 
12013  //require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
12014  //$caction=new CActionComm($db);
12015  //$arraylist=$caction->liste_array(1, 'code', '', (empty($conf->global->AGENDA_USE_EVENT_TYPE)?1:0), '', 1);
12016 
12017  $actualCycleDate = false;
12018 
12019  // Loop on each event to show it
12020  foreach ($histo as $key => $value) {
12021  $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
12022 
12023  $actionstatic->type_picto = $histo[$key]['apicto'];
12024  $actionstatic->type_code = $histo[$key]['acode'];
12025 
12026  $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
12027 
12028  $tmpa = dol_getdate($histo[$key]['datestart'], false);
12029  if ($actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
12030  $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
12031  $out .= '<!-- timeline time label -->';
12032  $out .= '<li class="time-label">';
12033  $out .= '<span class="timeline-badge-date">';
12034  $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
12035  $out .= '</span>';
12036  $out .= '</li>';
12037  $out .= '<!-- /.timeline-label -->';
12038  }
12039 
12040 
12041  $out .= '<!-- timeline item -->'."\n";
12042  $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
12043 
12044  $out .= getTimelineIcon($actionstatic, $histo, $key);
12045 
12046  $out .= '<div class="timeline-item">'."\n";
12047 
12048  $out .= '<span class="timeline-header-action">';
12049 
12050  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
12051  $out .= '<a class="timeline-btn" href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12052  $out .= $histo[$key]['id'];
12053  $out .= '</a> ';
12054  } else {
12055  $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
12056  }
12057 
12058  if ($user->hasRight('agenda', 'allactions', 'create') ||
12059  (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && !empty($user->rights->agenda->myactions->create))) {
12060  $out .= '<a class="timeline-btn" href="'.DOL_MAIN_URL_ROOT.'/comm/action/card.php?action=edit&token='.newToken().'&id='.$actionstatic->id.'&backtopage='.urlencode($_SERVER["PHP_SELF"].'?'.$param).'"><i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i></a>';
12061  }
12062 
12063  $out .= '</span>';
12064  // Date
12065  $out .= '<span class="time"><i class="fa fa-clock-o"></i> ';
12066  $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
12067  if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
12068  $tmpa = dol_getdate($histo[$key]['datestart'], true);
12069  $tmpb = dol_getdate($histo[$key]['dateend'], true);
12070  if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
12071  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
12072  } else {
12073  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
12074  }
12075  }
12076  $late = 0;
12077  if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12078  $late = 1;
12079  }
12080  if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12081  $late = 1;
12082  }
12083  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
12084  $late = 1;
12085  }
12086  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12087  $late = 1;
12088  }
12089  if ($late) {
12090  $out .= img_warning($langs->trans("Late")).' ';
12091  }
12092  $out .= "</span>\n";
12093 
12094  // Ref
12095  $out .= '<h3 class="timeline-header">';
12096 
12097  // Author of event
12098  $out .= '<span class="messaging-author">';
12099  if ($histo[$key]['userid'] > 0) {
12100  if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
12101  $userstatic->fetch($histo[$key]['userid']);
12102  $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
12103  }
12104  $out .= $userGetNomUrlCache[$histo[$key]['userid']];
12105  }
12106  $out .= '</span>';
12107 
12108  // Title
12109  $out .= ' <span class="messaging-title">';
12110 
12111  if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
12112  $out .= $langs->trans('TicketNewMessage');
12113  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
12114  $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
12115  } else {
12116  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'action') {
12117  $transcode = $langs->trans("Action".$histo[$key]['acode']);
12118  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
12119  $libelle = $histo[$key]['note'];
12120  $actionstatic->id = $histo[$key]['id'];
12121  $out .= dol_trunc($libelle, 120);
12122  }
12123  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
12124  $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12125  $transcode = $langs->trans("Action".$histo[$key]['acode']);
12126  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
12127  $out .= dol_trunc($libelle, 120);
12128  }
12129  }
12130 
12131  $out .= '</span>';
12132 
12133  $out .= '</h3>';
12134 
12135  if (!empty($histo[$key]['message'])
12136  && $actionstatic->code != 'AC_TICKET_CREATE'
12137  && $actionstatic->code != 'AC_TICKET_MODIFY'
12138  ) {
12139  $out .= '<div class="timeline-body">';
12140  $out .= $histo[$key]['message'];
12141  $out .= '</div>';
12142  }
12143 
12144  // Timeline footer
12145  $footer = '';
12146 
12147  // Contact for this action
12148  if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
12149  $contactList = '';
12150  foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
12151  $contact = new Contact($db);
12152  $result = $contact->fetch($cid);
12153 
12154  if ($result < 0) {
12155  dol_print_error($db, $contact->error);
12156  }
12157 
12158  if ($result > 0) {
12159  $contactList .= !empty($contactList) ? ', ' : '';
12160  $contactList .= $contact->getNomUrl(1);
12161  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12162  if (!empty($contact->phone_pro)) {
12163  $contactList .= '('.dol_print_phone($contact->phone_pro).')';
12164  }
12165  }
12166  }
12167  }
12168 
12169  $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
12170  } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
12171  $contact = new Contact($db);
12172  $result = $contact->fetch($histo[$key]['contact_id']);
12173 
12174  if ($result < 0) {
12175  dol_print_error($db, $contact->error);
12176  }
12177 
12178  if ($result > 0) {
12179  $footer .= $contact->getNomUrl(1);
12180  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12181  if (!empty($contact->phone_pro)) {
12182  $footer .= '('.dol_print_phone($contact->phone_pro).')';
12183  }
12184  }
12185  }
12186  }
12187 
12188  $documents = getActionCommEcmList($actionstatic);
12189  if (!empty($documents)) {
12190  $footer .= '<div class="timeline-documents-container">';
12191  foreach ($documents as $doc) {
12192  $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
12193  $footer .= ' data-id="'.$doc->id.'" ';
12194  $footer .= ' data-path="'.$doc->filepath.'"';
12195  $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
12196  $footer .= '>';
12197 
12198  $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
12199  $mime = dol_mimetype($filePath);
12200  $file = $actionstatic->id.'/'.$doc->filename;
12201  $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
12202  $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
12203  $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
12204 
12205  $mimeAttr = ' mime="'.$mime.'" ';
12206  $class = '';
12207  if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
12208  $class .= ' documentpreview';
12209  }
12210 
12211  $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
12212  $footer .= img_mime($filePath).' '.$doc->filename;
12213  $footer .= '</a>';
12214 
12215  $footer .= '</span>';
12216  }
12217  $footer .= '</div>';
12218  }
12219 
12220  if (!empty($footer)) {
12221  $out .= '<div class="timeline-footer">'.$footer.'</div>';
12222  }
12223 
12224  $out .= '</div>'."\n"; // end timeline-item
12225 
12226  $out .= '</li>';
12227  $out .= '<!-- END timeline item -->';
12228 
12229  $i++;
12230  }
12231 
12232  $out .= "</ul>\n";
12233 
12234  if (empty($histo)) {
12235  $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
12236  }
12237  }
12238 
12239  if ($noprint) {
12240  return $out;
12241  } else {
12242  print $out;
12243  }
12244 }
if(preg_match('/set_([a-z0-9_\-]+)/i', $action, $reg)) if(preg_match('/del_([a-z0-9_\-]+)/i', $action, $reg)) if($action=='set') elseif($action=='specimen') elseif($action=='setmodel') elseif($action=='del') elseif($action=='setdoc') $formactions
View.
ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array(), $morecss='', $htmlname='')
On/off button to change status of an object This is called when MAIN_DIRECT_STATUS_UPDATE is set and ...
Definition: ajax.lib.php:681
Class to manage agenda events (actions)
Class to manage contact/addresses.
Classe to manage GeoIP Usage: $geoip=new GeoIP('country',$datfile); $geoip->getCountryCodeFromIP($ip)...
Class to manage standard extra fields.
Class to manage invoices.
Class to manage building of HTML components.
Class to manage hooks.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
isACompany()
Return if third party is a company (Business) or an end user (Consumer)
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:45
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("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->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
dol_get_prev_month($month, $year)
Return previous month.
Definition: date.lib.php:494
dol_get_next_day($day, $month, $year)
Return next day.
Definition: date.lib.php:479
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition: date.lib.php:83
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition: date.lib.php:463
dol_get_next_month($month, $year)
Return next month.
Definition: date.lib.php:513
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert an image file or a PDF into another image format.
Definition: files.lib.php:1992
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:61
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:450
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access)
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_fiche_end($notab=0)
Show tab footer of a card.
dolCheckFilters($sqlfilters, &$error='')
Return if a $sqlfilters parameter is valid and will pass the preg_replace_callback() to replace Gener...
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formated size.
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.
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
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...
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 ...
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
startSimpleTable($header, $link="", $arguments="", $emptyRows=0, $number=-1)
Start a table with headers and a optinal clickable number (don't forget to use "finishSimpleTable()" ...
dol_getmypid()
Return getmypid() or random PID when function is disabled Some web hosts disable this php function fo...
getLanguageCodeFromCountryCode($countrycode)
Return default language from country code.
setEntity($currentobject)
Set entity id to use when to create an object.
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.
dolExplodeIntoArray($string, $delimiter=';', $kv='=')
Split a string with 2 keys into key array.
dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
img_right($titlealt='default', $selected=0, $moreatt='')
Show right arrow logo.
load_fiche_titre($titre, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tabs of a record.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto='UTF-8')
This function is called to decode a HTML string (it decodes entities and br tags)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
dol_print_ip($ip, $mode=0)
Return an IP formated to be shown on screen.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
GETPOSTINT($paramname, $method=0)
Return value of a param into GET or POST supervariable.
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.
isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
Function to test if an entry is enabled or not.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
get_date_range($date_start, $date_end, $format='', $outputlangs='', $withparenthesis=1)
Format output for start and end date.
get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Get formated error messages to output (Used to show messages on html output).
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.
roundUpToNextMultiple($n, $x=5)
Round to next multiple.
dol_user_country()
Return country code for current user.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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.
isHTTPS()
Return if we are using a HTTPS connexion Check HTTPS (no way to be modified by user but may be empty ...
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
picto_required()
Return picto saying a field is required.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
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...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='button bordertransp', $backtopagejsfields='')
Return HTML code to output a button to open a dialog popup box.
checkVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dolGetBadge($label, $html='', $type='primary', $mode='', $url='', $params=array())
Function dolGetBadge.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
get_htmloutput_mesg($mesgstring='', $mesgarray='', $style='ok', $keepembedded=0)
Get formated messages to output (Used to show messages on html output).
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dol_now($mode='auto')
Return date for now.
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
get_localtax_by_third($local)
Get values of localtaxes (1 or 2) for company country for the common vat with the highest value.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
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_set_focus($selector)
Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
dol_print_url($url, $target='_blank', $max=32, $withpicto=0)
Show Url link.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_eval($s, $returnvalue=0, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array())
Clean a string to keep only desirable HTML tags.
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows='')
Function to show navigation arrows into lists.
img_search($titlealt='default', $other='')
Show search logo.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
isValidPhone($phone)
Return true if phone number syntax is ok TODO Decide what to do with this.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
img_previous($titlealt='default', $moreatt='')
Show previous logo.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
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_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1, $separ='&nbsp;')
Format profIDs according to country.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='')
Clean a string from all punctuation characters to use it as a ref or login.
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
getBrowserInfo($user_agent)
Return information about user browser.
dolGetFirstLetters($s, $nbofchar=1)
Return first letters of a strings.
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
getDolUserInt($key, $default=0)
Return dolibarr user constant int value.
img_action($titlealt, $numaction, $picto='')
Show logo action.
dol_sanitizeUrl($stringtoclean, $type=1)
Clean a string to use it as an URL (into a href or src attribute)
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
img_printer($titlealt="default", $other='')
Show printer logo.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formated messages to output (Used to show messages on html output).
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
ascii_check($str)
Check if a string is in ASCII.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
getArrayOfSocialNetworks()
Get array of social network dictionary.
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
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.
num2Alpha($n)
Return a numeric value into an Excel like column number.
dol_size($size, $type='')
Optimize a size for some browsers (phone, smarphone, ...)
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
img_pdf($titlealt='default', $size=3)
Show pdf logo.
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
colorIsLight($stringcolor)
Return true if the color is light.
dol_substr($string, $start, $length, $stringencoding='', $trunconbytes=0)
Make a substring.
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
dolIsAllowedForPreview($file)
Return if a file is qualified for preview.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='')
Show information for admin users or standard users.
dol_shutdown()
Function called at end of web php process.
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl='')
Format address string.
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
setEventMessage($mesgs, $style='mesgs')
Set event message in dol_events session object.
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.
dol_bc($var, $moreclass='')
Return string to add class property on html element with pair/impair.
print_titre($title)
Show a title.
getElementProperties($element_type)
Get an array with properties of an element.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
showDirectDownloadLink($object)
Return string with full Url.
if(!function_exists('utf8_encode')) if(!function_exists('utf8_decode')) getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
img_error($titlealt='default')
Show error logo.
getTimelineIcon($actionstatic, &$histo, $key)
Get timeline icon.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formated messages to output (Used to show messages on html output).
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
getDolUserString($key, $default='')
Return dolibarr user constant string value.
img_next($titlealt='default', $moreatt='')
Show next logo.
print_date_range($date_start, $date_end, $format='', $outputlangs='')
Format output for start and end date.
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
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...
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode='add', $filterorigmodule='')
Complete or removed entries into a head array (used to build tabs).
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...
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_nboflines($s, $maxchar=0)
Return nb of lines of a clear text.
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js and dangerous content.
jsonOrUnserialize($stringtodecode)
Decode an encode string.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
getActionCommEcmList($object)
getActionCommEcmList
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
img_edit_add($titlealt='default', $other='')
Show logo +.
verifCond($strToEvaluate)
Verify if condition in string is ok or not.
print_fiche_titre($title, $mesg='', $picto='generic', $pictoisfullpath=0, $id='')
Show a title with picto.
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.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
dolForgeCriteriaCallback($matches)
Function to forge a SQL criteria from a Generic filter string.
getUserRemoteIP()
Return the IP of remote user.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_validElement($element)
Return if var element is ok.
isModEnabled($module)
Is Dolibarr module enabled.
img_credit_card($brand, $morecss=null)
Return image of a credit card according to its brand name.
img_searchclear($titlealt='default', $other='')
Show search logo.
fetchObjectByElement($element_id, $element_type, $element_ref='')
Fetch an object from its id and element_type Inclusion of classes is automatic.
utf8_check($str)
Check if a string is in UTF8.
img_edit($titlealt='default', $float=0, $other='')
Show logo editer/modifier fiche.
show_actions_messaging($conf, $langs, $db, $filterobj, $objcon='', $noprint=0, $actioncode='', $donetodo='done', $filters=array(), $sortfield='a.datep, a.id', $sortorder='DESC')
Show html area with actions in messaging format.
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...
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Print formated error messages to output (Used to show messages on html output).
getFieldErrorIcon($fieldValidationErrorMsg)
get field error icon
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
img_edit_remove($titlealt='default', $other='')
Show logo -.
img_info($titlealt='default')
Show info logo.
getDoliDBInstance($type, $host, $user, $pass, $name, $port)
Return a DoliDB instance (database handler).
dol_sanitizeEmail($stringtoclean)
Clean a string to use it as an Email.
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...
dol_convertToWord($num, $langs, $currency='', $centimes=false)
Function to return a number into a text.
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition: inc.php:292
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
if(!defined('NOREQUIREMENU')) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
Definition: main.inc.php:1436
if(!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition: main.inc.php:61
div float
Buy price without taxes.
Definition: style.css.php:913
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
dol_getcache($memoryid)
Read a memory area shared by all users, all sessions on server.
Definition: memory.lib.php:135
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:122
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge,...