dolibarr  17.0.4
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) (isset($conf->global->$key) ? $conf->global->$key : $default);
87 }
88 
96 function getDolGlobalInt($key, $default = 0)
97 {
98  global $conf;
99  // return $conf->global->$key ?? $default;
100  return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
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 
421 function GETPOSTISSET($paramname)
422 {
423  $isset = false;
424 
425  $relativepathstring = $_SERVER["PHP_SELF"];
426  // Clean $relativepathstring
427  if (constant('DOL_URL_ROOT')) {
428  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
429  }
430  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
431  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
432  //var_dump($relativepathstring);
433  //var_dump($user->default_values);
434 
435  // Code for search criteria persistence.
436  // Retrieve values if restore_lastsearch_values
437  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
438  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
439  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
440  if (is_array($tmp)) {
441  foreach ($tmp as $key => $val) {
442  if ($key == $paramname) { // We are on the requested parameter
443  $isset = true;
444  break;
445  }
446  }
447  }
448  }
449  // If there is saved contextpage, limit, page or mode
450  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
451  $isset = true;
452  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
453  $isset = true;
454  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
455  $isset = true;
456  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
457  $isset = true;
458  }
459  } else {
460  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
461  }
462 
463  return $isset;
464 }
465 
474 function GETPOSTISARRAY($paramname, $method = 0)
475 {
476  // for $method test need return the same $val as GETPOST
477  if (empty($method)) {
478  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
479  } elseif ($method == 1) {
480  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
481  } elseif ($method == 2) {
482  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
483  } elseif ($method == 3) {
484  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
485  } else {
486  $val = 'BadFirstParameterForGETPOST';
487  }
488 
489  return is_array($val);
490 }
491 
520 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
521 {
522  global $mysoc, $user, $conf;
523 
524  if (empty($paramname)) {
525  return 'BadFirstParameterForGETPOST';
526  }
527  if (empty($check)) {
528  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
529  // Enable this line to know who call the GETPOST with '' $check parameter.
530  //var_dump(debug_backtrace()[0]);
531  }
532 
533  if (empty($method)) {
534  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
535  } elseif ($method == 1) {
536  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
537  } elseif ($method == 2) {
538  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
539  } elseif ($method == 3) {
540  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
541  } else {
542  return 'BadThirdParameterForGETPOST';
543  }
544 
545  if (empty($method) || $method == 3 || $method == 4) {
546  $relativepathstring = $_SERVER["PHP_SELF"];
547  // Clean $relativepathstring
548  if (constant('DOL_URL_ROOT')) {
549  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
550  }
551  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
552  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
553  //var_dump($relativepathstring);
554  //var_dump($user->default_values);
555 
556  // Code for search criteria persistence.
557  // Retrieve values if restore_lastsearch_values
558  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
559  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
560  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
561  if (is_array($tmp)) {
562  foreach ($tmp as $key => $val) {
563  if ($key == $paramname) { // We are on the requested parameter
564  $out = $val;
565  break;
566  }
567  }
568  }
569  }
570  // If there is saved contextpage, page or limit
571  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
572  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
573  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
574  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
575  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
576  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
577  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
578  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
579  }
580  } elseif (!isset($_GET['sortfield'])) {
581  // Else, retrieve default values if we are not doing a sort
582  // 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
583  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
584  // Search default value from $object->field
585  global $object;
586  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
587  $out = $object->fields[$paramname]['default'];
588  }
589  }
590  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
591  if (!empty($_GET['action']) && (preg_match('/^create|^add_price|^make/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
592  // Now search in setup to overwrite default values
593  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
594  if (isset($user->default_values[$relativepathstring]['createform'])) {
595  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
596  $qualified = 0;
597  if ($defkey != '_noquery_') {
598  $tmpqueryarraytohave = explode('&', $defkey);
599  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
600  $foundintru = 0;
601  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
602  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
603  $foundintru = 1;
604  }
605  }
606  if (!$foundintru) {
607  $qualified = 1;
608  }
609  //var_dump($defkey.'-'.$qualified);
610  } else {
611  $qualified = 1;
612  }
613 
614  if ($qualified) {
615  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
616  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
617  break;
618  }
619  }
620  }
621  }
622  }
623  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
624  // Management of default search_filters and sort order
625  if (!empty($user->default_values)) {
626  // $user->default_values defined from menu 'Setup - Default values'
627  //var_dump($user->default_values[$relativepathstring]);
628  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
629  // Sorted on which fields ? ASC or DESC ?
630  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
631  // Even if paramname is sortfield, data are stored into ['sortorder...']
632  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
633  $qualified = 0;
634  if ($defkey != '_noquery_') {
635  $tmpqueryarraytohave = explode('&', $defkey);
636  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
637  $foundintru = 0;
638  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
639  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
640  $foundintru = 1;
641  }
642  }
643  if (!$foundintru) {
644  $qualified = 1;
645  }
646  //var_dump($defkey.'-'.$qualified);
647  } else {
648  $qualified = 1;
649  }
650 
651  if ($qualified) {
652  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
653  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
654  if ($out) {
655  $out .= ', ';
656  }
657  if ($paramname == 'sortfield') {
658  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
659  }
660  if ($paramname == 'sortorder') {
661  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
662  }
663  }
664  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
665  }
666  }
667  }
668  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
669  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
670  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
671  continue;
672  }
673  $qualified = 0;
674  if ($defkey != '_noquery_') {
675  $tmpqueryarraytohave = explode('&', $defkey);
676  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
677  $foundintru = 0;
678  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
679  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
680  $foundintru = 1;
681  }
682  }
683  if (!$foundintru) {
684  $qualified = 1;
685  }
686  //var_dump($defkey.'-'.$qualified);
687  } else {
688  $qualified = 1;
689  }
690 
691  if ($qualified) {
692  // We must keep $_POST and $_GET here
693  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
694  // We made a search from quick search menu, do we still use default filter ?
695  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
696  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
697  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
698  }
699  } else {
700  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
701  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
702  }
703  break;
704  }
705  }
706  }
707  }
708  }
709  }
710  }
711  }
712 
713  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
714  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
715  // 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.
716  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
717  $reg = array();
718  $maxloop = 20;
719  $loopnb = 0; // Protection against infinite loop
720  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.
721  $loopnb++;
722  $newout = '';
723 
724  if ($reg[1] == 'DAY') {
725  $tmp = dol_getdate(dol_now(), true);
726  $newout = $tmp['mday'];
727  } elseif ($reg[1] == 'MONTH') {
728  $tmp = dol_getdate(dol_now(), true);
729  $newout = $tmp['mon'];
730  } elseif ($reg[1] == 'YEAR') {
731  $tmp = dol_getdate(dol_now(), true);
732  $newout = $tmp['year'];
733  } elseif ($reg[1] == 'PREVIOUS_DAY') {
734  $tmp = dol_getdate(dol_now(), true);
735  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
736  $newout = $tmp2['day'];
737  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
738  $tmp = dol_getdate(dol_now(), true);
739  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
740  $newout = $tmp2['month'];
741  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
742  $tmp = dol_getdate(dol_now(), true);
743  $newout = ($tmp['year'] - 1);
744  } elseif ($reg[1] == 'NEXT_DAY') {
745  $tmp = dol_getdate(dol_now(), true);
746  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
747  $newout = $tmp2['day'];
748  } elseif ($reg[1] == 'NEXT_MONTH') {
749  $tmp = dol_getdate(dol_now(), true);
750  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
751  $newout = $tmp2['month'];
752  } elseif ($reg[1] == 'NEXT_YEAR') {
753  $tmp = dol_getdate(dol_now(), true);
754  $newout = ($tmp['year'] + 1);
755  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
756  $newout = $mysoc->country_id;
757  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
758  $newout = $user->id;
759  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
760  $newout = $user->fk_user;
761  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
762  $newout = $conf->entity;
763  } else {
764  $newout = ''; // Key not found, we replace with empty string
765  }
766  //var_dump('__'.$reg[1].'__ -> '.$newout);
767  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
768  }
769  }
770 
771  // Check rule
772  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
773  if (!is_array($out) || empty($out)) {
774  $out = array();
775  } else {
776  $tmparray = explode(':', $check);
777  if (!empty($tmparray[1])) {
778  $tmpcheck = $tmparray[1];
779  } else {
780  $tmpcheck = 'alphanohtml';
781  }
782  foreach ($out as $outkey => $outval) {
783  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
784  }
785  }
786  } else {
787  // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
788  // we use the < or > to make a search on a numeric value to do higher or lower so we can add a space to break html tags
789  if (strpos($paramname, 'search_') === 0) {
790  $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
791  }
792 
793  $out = sanitizeVal($out, $check, $filter, $options);
794  }
795 
796  // Sanitizing for special parameters.
797  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
798  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
799  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
800  $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.
801  do {
802  $oldstringtoclean = $out;
803  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
804  $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'
805  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
806  } while ($oldstringtoclean != $out);
807  }
808 
809  // Code for search criteria persistence.
810  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
811  if (empty($method) || $method == 3 || $method == 4) {
812  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
813  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
814 
815  // We save search key only if $out not empty that means:
816  // - posted value not empty, or
817  // - 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).
818 
819  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
820  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
821  }
822  }
823  }
824 
825  return $out;
826 }
827 
837 function GETPOSTINT($paramname, $method = 0)
838 {
839  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
840 }
841 
842 
853 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
854 {
855  return sanitizeVal($out, $check, $filter, $options);
856 }
857 
867 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
868 {
869  global $conf;
870 
871  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
872  // Check is done after replacement
873  switch ($check) {
874  case 'none':
875  break;
876  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
877  if (!is_numeric($out)) {
878  $out = '';
879  }
880  break;
881  case 'intcomma':
882  if (preg_match('/[^0-9,-]+/i', $out)) {
883  $out = '';
884  }
885  break;
886  case 'san_alpha':
887  $out = filter_var($out, FILTER_SANITIZE_STRING);
888  break;
889  case 'email':
890  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
891  break;
892  case 'aZ':
893  if (!is_array($out)) {
894  $out = trim($out);
895  if (preg_match('/[^a-z]+/i', $out)) {
896  $out = '';
897  }
898  }
899  break;
900  case 'aZ09':
901  if (!is_array($out)) {
902  $out = trim($out);
903  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
904  $out = '';
905  }
906  }
907  break;
908  case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
909  if (!is_array($out)) {
910  $out = trim($out);
911  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
912  $out = '';
913  }
914  }
915  break;
916  case 'nohtml': // No html
917  $out = dol_string_nohtmltag($out, 0);
918  break;
919  case 'alpha': // No html and no ../ and "
920  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
921  if (!is_array($out)) {
922  $out = trim($out);
923  do {
924  $oldstringtoclean = $out;
925  // Remove html tags
926  $out = dol_string_nohtmltag($out, 0);
927  // Remove also other dangerous string sequences
928  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
929  // '../' or '..\' is dangerous because it allows dir transversals
930  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
931  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
932  } while ($oldstringtoclean != $out);
933  // keep lines feed
934  }
935  break;
936  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'
937  if (!is_array($out)) {
938  $out = trim($out);
939  do {
940  $oldstringtoclean = $out;
941  // Remove html tags
942  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
943  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
944  // '../' or '..\' is dangerous because it allows dir transversals
945  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
946  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
947  } while ($oldstringtoclean != $out);
948  }
949  break;
950  case 'restricthtml': // Recommended for most html textarea
951  case 'restricthtmlallowunvalid':
952  $out = dol_htmlwithnojs($out, 1, $check);
953  break;
954  case 'custom':
955  if (!empty($out)) {
956  if (empty($filter)) {
957  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
958  }
959  /*if (empty($options)) {
960  return 'BadParameterForGETPOST - Param 4 of sanitizeVal()';
961  }*/
962  $out = filter_var($out, $filter, $options);
963  }
964  break;
965  }
966 
967  return $out;
968 }
969 
970 
971 if (!function_exists('dol_getprefix')) {
981  function dol_getprefix($mode = '')
982  {
983  // If prefix is for email (we need to have $conf already loaded for this case)
984  if ($mode == 'email') {
985  global $conf;
986 
987  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
988  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
989  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
990  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
991  return $_SERVER["SERVER_NAME"];
992  }
993  }
994 
995  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
996  if (!empty($conf->file->instance_unique_id)) {
997  return sha1('dolibarr'.$conf->file->instance_unique_id);
998  }
999 
1000  // For backward compatibility when instance_unique_id is not set
1001  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1002  }
1003 
1004  // If prefix is for session (no need to have $conf loaded)
1005  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1006  $tmp_instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance
1007 
1008  // The recommended value (may be not defined for old versions)
1009  if (!empty($tmp_instance_unique_id)) {
1010  return sha1('dolibarr'.$tmp_instance_unique_id);
1011  }
1012 
1013  // For backward compatibility when instance_unique_id is not set
1014  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1015  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1016  } else {
1017  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1018  }
1019  }
1020 }
1021 
1032 function dol_include_once($relpath, $classname = '')
1033 {
1034  global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retrieved with $GLOBALS['var']
1035 
1036  $fullpath = dol_buildpath($relpath);
1037 
1038  if (!file_exists($fullpath)) {
1039  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1040  return false;
1041  }
1042 
1043  if (!empty($classname) && !class_exists($classname)) {
1044  return include $fullpath;
1045  } else {
1046  return include_once $fullpath;
1047  }
1048 }
1049 
1050 
1061 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1062 {
1063  global $conf;
1064 
1065  $path = preg_replace('/^\//', '', $path);
1066 
1067  if (empty($type)) { // For a filesystem path
1068  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1069  if (is_array($conf->file->dol_document_root)) {
1070  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1071  if ($key == 'main') {
1072  continue;
1073  }
1074  if (file_exists($dirroot.'/'.$path)) {
1075  $res = $dirroot.'/'.$path;
1076  return $res;
1077  }
1078  }
1079  }
1080  if ($returnemptyifnotfound) {
1081  // Not found into alternate dir
1082  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1083  return '';
1084  }
1085  }
1086  } else {
1087  // For an url path
1088  // We try to get local path of file on filesystem from url
1089  // Note that trying to know if a file on disk exist by forging path on disk from url
1090  // works only for some web server and some setup. This is bugged when
1091  // using proxy, rewriting, virtual path, etc...
1092  $res = '';
1093  if ($type == 1) {
1094  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1095  }
1096  if ($type == 2) {
1097  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1098  }
1099  if ($type == 3) {
1100  $res = DOL_URL_ROOT.'/'.$path;
1101  }
1102 
1103  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1104  if ($key == 'main') {
1105  if ($type == 3) {
1106  global $dolibarr_main_url_root;
1107 
1108  // Define $urlwithroot
1109  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1110  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1111  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1112 
1113  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1114  }
1115  continue;
1116  }
1117  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1118  if (!empty($regs[1])) {
1119  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1120  if (file_exists($dirroot.'/'.$regs[1])) {
1121  if ($type == 1) {
1122  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1123  }
1124  if ($type == 2) {
1125  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1126  }
1127  if ($type == 3) {
1128  global $dolibarr_main_url_root;
1129 
1130  // Define $urlwithroot
1131  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1132  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1133  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1134 
1135  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).$conf->file->dol_url_root[$key].'/'.$path; // Test on start with http is for old conf syntax
1136  }
1137  break;
1138  }
1139  }
1140  }
1141  }
1142 
1143  return $res;
1144 }
1145 
1157 function dol_clone($object, $native = 0)
1158 {
1159  if ($native == 0) {
1160  // deprecated method, use the method with native = 2 instead
1161  $tmpsavdb = null;
1162  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1163  $tmpsavdb = $object->db;
1164  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1165  }
1166 
1167  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1168 
1169  if (!empty($tmpsavdb)) {
1170  $object->db = $tmpsavdb;
1171  }
1172  } elseif ($native == 2) {
1173  // recommended method to have a full isolated cloned object
1174  $myclone = new stdClass();
1175  $tmparray = get_object_vars($object); // return only public properties
1176 
1177  if (is_array($tmparray)) {
1178  foreach ($tmparray as $propertykey => $propertyval) {
1179  if (is_scalar($propertyval) || is_array($propertyval)) {
1180  $myclone->$propertykey = $propertyval;
1181  }
1182  }
1183  }
1184  } else {
1185  $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)
1186  }
1187 
1188  return $myclone;
1189 }
1190 
1200 function dol_size($size, $type = '')
1201 {
1202  global $conf;
1203  if (empty($conf->dol_optimize_smallscreen)) {
1204  return $size;
1205  }
1206  if ($type == 'width' && $size > 250) {
1207  return 250;
1208  } else {
1209  return 10;
1210  }
1211 }
1212 
1213 
1225 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1226 {
1227  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1228  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1229  // Char '/' and '\' are file delimiters.
1230  // 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
1231  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1232  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1233  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1234  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1235  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1236  $tmp = str_replace('..', '', $tmp);
1237  return $tmp;
1238 }
1239 
1251 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1252 {
1253  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1254  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1255  // Chars '--' can be used into filename to inject special paramaters like --use-compress-program to make command with file as parameter making remote execution of command
1256  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1257  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1258  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1259  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1260  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1261  $tmp = str_replace('..', '', $tmp);
1262  return $tmp;
1263 }
1264 
1272 function dol_sanitizeUrl($stringtoclean, $type = 1)
1273 {
1274  // 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)
1275  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1276  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1277  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1278  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1279 
1280  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1281  if ($type == 1) {
1282  // removing : should disable links to external url like http:aaa)
1283  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1284  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1285  }
1286 
1287  do {
1288  $oldstringtoclean = $stringtoclean;
1289  // removing '&colon' should disable links to external url like http:aaa)
1290  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1291  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1292  } while ($oldstringtoclean != $stringtoclean);
1293 
1294  if ($type == 1) {
1295  // removing '//' should disable links to external url like //aaa or http//)
1296  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1297  }
1298 
1299  return $stringtoclean;
1300 }
1301 
1308 function dol_sanitizeEmail($stringtoclean)
1309 {
1310  do {
1311  $oldstringtoclean = $stringtoclean;
1312  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1313  } while ($oldstringtoclean != $stringtoclean);
1314 
1315  return $stringtoclean;
1316 }
1317 
1326 function dol_string_unaccent($str)
1327 {
1328  global $conf;
1329 
1330  if (is_null($str)) {
1331  return '';
1332  }
1333 
1334  if (utf8_check($str)) {
1335  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1336  $transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
1337  return $transliterator->transliterate($str);
1338  }
1339  // See http://www.utf8-chartable.de/
1340  $string = rawurlencode($str);
1341  $replacements = array(
1342  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1343  '%C3%87' => 'C',
1344  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1345  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1346  '%C3%91' => 'N',
1347  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1348  '%C5%A0' => 'S',
1349  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1350  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1351  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1352  '%C3%A7' => 'c',
1353  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1354  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1355  '%C3%B1' => 'n',
1356  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1357  '%C5%A1' => 's',
1358  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1359  '%C3%BD' => 'y', '%C3%BF' => 'y'
1360  );
1361  $string = strtr($string, $replacements);
1362  return rawurldecode($string);
1363  } else {
1364  // See http://www.ascii-code.com/
1365  $string = strtr(
1366  $str,
1367  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1368  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1369  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1370  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1371  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1372  \xF9\xFA\xFB\xFC\xFD\xFF",
1373  "AAAAAAC
1374  EEEEIIIIDN
1375  OOOOOUUUY
1376  aaaaaaceeee
1377  iiiidnooooo
1378  uuuuyy"
1379  );
1380  $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"));
1381  return $string;
1382  }
1383 }
1384 
1397 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '')
1398 {
1399  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName
1400  $forbidden_chars_to_remove = array();
1401  //$forbidden_chars_to_remove=array("(",")");
1402 
1403  if (is_array($badcharstoreplace)) {
1404  $forbidden_chars_to_replace = $badcharstoreplace;
1405  }
1406  if (is_array($badcharstoremove)) {
1407  $forbidden_chars_to_remove = $badcharstoremove;
1408  }
1409 
1410  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1411 }
1412 
1413 
1427 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1428 {
1429  if ($removetabcrlf) {
1430  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1431  } else {
1432  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
1433  }
1434 }
1435 
1444 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1445 {
1446  if (is_null($stringtoescape)) {
1447  return '';
1448  }
1449 
1450  // escape quotes and backslashes, newlines, etc.
1451  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1452  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1453  if (empty($noescapebackslashn)) {
1454  $substitjs["\n"] = '\\n';
1455  $substitjs['\\'] = '\\\\';
1456  }
1457  if (empty($mode)) {
1458  $substitjs["'"] = "\\'";
1459  $substitjs['"'] = "\\'";
1460  } elseif ($mode == 1) {
1461  $substitjs["'"] = "\\'";
1462  } elseif ($mode == 2) {
1463  $substitjs['"'] = '\\"';
1464  } elseif ($mode == 3) {
1465  $substitjs["'"] = "\\'";
1466  $substitjs['"'] = "\\\"";
1467  }
1468  return strtr($stringtoescape, $substitjs);
1469 }
1470 
1477 function dol_escape_json($stringtoescape)
1478 {
1479  return str_replace('"', '\"', $stringtoescape);
1480 }
1481 
1493 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0)
1494 {
1495  if ($noescapetags == 'common') {
1496  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1497  }
1498 
1499  // escape quotes and backslashes, newlines, etc.
1500  if ($escapeonlyhtmltags) {
1501  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1502  } else {
1503  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1504  }
1505  if (!$keepb) {
1506  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1507  }
1508  if (!$keepn) {
1509  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1510  }
1511 
1512  if ($escapeonlyhtmltags) {
1513  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1514  } else {
1515  // Escape tags to keep
1516  // TODO Does not works yet when there is attributes into tag
1517  $tmparrayoftags = array();
1518  if ($noescapetags) {
1519  $tmparrayoftags = explode(',', $noescapetags);
1520  }
1521  if (count($tmparrayoftags)) {
1522  foreach ($tmparrayoftags as $tagtoreplace) {
1523  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1524  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1525  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1526  }
1527  }
1528 
1529  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1530 
1531  if (count($tmparrayoftags)) {
1532  foreach ($tmparrayoftags as $tagtoreplace) {
1533  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1534  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1535  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1536  }
1537  }
1538 
1539  return $result;
1540  }
1541 }
1542 
1550 function dol_strtolower($string, $encoding = "UTF-8")
1551 {
1552  if (function_exists('mb_strtolower')) {
1553  return mb_strtolower($string, $encoding);
1554  } else {
1555  return strtolower($string);
1556  }
1557 }
1558 
1566 function dol_strtoupper($string, $encoding = "UTF-8")
1567 {
1568  if (function_exists('mb_strtoupper')) {
1569  return mb_strtoupper($string, $encoding);
1570  } else {
1571  return strtoupper($string);
1572  }
1573 }
1574 
1582 function dol_ucfirst($string, $encoding = "UTF-8")
1583 {
1584  if (function_exists('mb_substr')) {
1585  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1586  } else {
1587  return ucfirst($string);
1588  }
1589 }
1590 
1598 function dol_ucwords($string, $encoding = "UTF-8")
1599 {
1600  if (function_exists('mb_convert_case')) {
1601  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1602  } else {
1603  return ucwords($string);
1604  }
1605 }
1606 
1628 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1629 {
1630  global $conf, $user, $debugbar;
1631 
1632  // If syslog module enabled
1633  if (empty($conf->syslog->enabled)) {
1634  return;
1635  }
1636 
1637  // Check if we are into execution of code of a website
1638  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1639  global $website, $websitekey;
1640  if (is_object($website) && !empty($website->ref)) {
1641  $suffixinfilename .= '_website_'.$website->ref;
1642  } elseif (!empty($websitekey)) {
1643  $suffixinfilename .= '_website_'.$websitekey;
1644  }
1645  }
1646 
1647  // Check if we have a forced suffix
1648  if (defined('USESUFFIXINLOG')) {
1649  $suffixinfilename .= constant('USESUFFIXINLOG');
1650  }
1651 
1652  if ($ident < 0) {
1653  foreach ($conf->loghandlers as $loghandlerinstance) {
1654  $loghandlerinstance->setIdent($ident);
1655  }
1656  }
1657 
1658  if (!empty($message)) {
1659  // Test log level
1660  $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');
1661  if (!array_key_exists($level, $logLevels)) {
1662  throw new Exception('Incorrect log level');
1663  }
1664  if ($level > $conf->global->SYSLOG_LEVEL) {
1665  return;
1666  }
1667 
1668  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1669  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1670  }
1671 
1672  // If adding log inside HTML page is required
1673  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1674  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1675  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1676  }
1677 
1678  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1679  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1680  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1681  print "\n\n<!-- Log start\n";
1682  print dol_escape_htmltag($message)."\n";
1683  print "Log end -->\n";
1684  }
1685 
1686  $data = array(
1687  'message' => $message,
1688  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1689  'level' => $level,
1690  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1691  'ip' => false
1692  );
1693 
1694  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1695  if (!empty($remoteip)) {
1696  $data['ip'] = $remoteip;
1697  // This is when server run behind a reverse proxy
1698  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1699  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1700  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1701  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1702  }
1703  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1704  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1705  $data['ip'] = $_SERVER['SERVER_ADDR'];
1706  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1707  // 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).
1708  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1709  } elseif (!empty($_SERVER['LOGNAME'])) {
1710  // 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).
1711  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1712  }
1713 
1714  // Loop on each log handler and send output
1715  foreach ($conf->loghandlers as $loghandlerinstance) {
1716  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1717  continue;
1718  }
1719  $loghandlerinstance->export($data, $suffixinfilename);
1720  }
1721  unset($data);
1722  }
1723 
1724  if ($ident > 0) {
1725  foreach ($conf->loghandlers as $loghandlerinstance) {
1726  $loghandlerinstance->setIdent($ident);
1727  }
1728  }
1729 }
1730 
1745 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'button bordertransp', $backtopagejsfields = '')
1746 {
1747  global $conf;
1748 
1749  if (strpos($url, '?') > 0) {
1750  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1751  } else {
1752  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1753  }
1754 
1755  $out = '';
1756 
1757  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1758  if ($backtopagejsfields) {
1759  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1760  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1761  $backtopagejsfields = $name.":".$backtopagejsfields;
1762  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1763  } else {
1764  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1765  }
1766  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1767  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1768  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1769  }
1770 
1771  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1772  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
1773  $out .= '<a class="cursorpointer classlink button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
1774  if (empty($conf->use_javascript_ajax)) {
1775  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
1776  }
1777  $out .= '>'.$buttonstring.'</a>';
1778 
1779  if (!empty($conf->use_javascript_ajax)) {
1780  // Add code to open url using the popup. Add also hidden field to retreive the returned variables
1781  $out .= '<!-- code to open popup and variables to retreive returned variables -->';
1782  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1783  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1784  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1785  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1786  $out .= '<script type="text/javascript">
1787  jQuery(document).ready(function () {
1788  jQuery(".button_'.$name.'").click(function () {
1789  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1790  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1791  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1792  $tmpdialog.dialog({
1793  autoOpen: false,
1794  modal: true,
1795  height: (window.innerHeight - 150),
1796  width: \'80%\',
1797  title: \''.dol_escape_js($label).'\',
1798  open: function (event, ui) {
1799  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1800  },
1801  close: function (event, ui) {
1802  returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1803  returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1804  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1805  if (returnedid != "" && returnedid != "div for returned id") {
1806  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1807  }
1808  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1809  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1810  }
1811  }
1812  });
1813 
1814  $tmpdialog.dialog(\'open\');
1815  });
1816  });
1817  </script>';
1818  }
1819  return $out;
1820 }
1821 
1838 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1839 {
1840  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1841 }
1842 
1858 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1859 {
1860  global $conf, $langs, $hookmanager;
1861 
1862  // Show title
1863  $showtitle = 1;
1864  if (!empty($conf->dol_optimize_smallscreen)) {
1865  $showtitle = 0;
1866  }
1867 
1868  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1869 
1870  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1871  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1872  }
1873 
1874  // Show right part
1875  if ($morehtmlright) {
1876  $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.
1877  }
1878 
1879  // Show title
1880  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1881  $limittitle = 30;
1882  $out .= '<a class="tabTitle">';
1883  if ($picto) {
1884  $noprefix = $pictoisfullpath;
1885  if (strpos($picto, 'fontawesome_') !== false) {
1886  $noprefix = 1;
1887  }
1888  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
1889  }
1890  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
1891  $out .= '</a>';
1892  }
1893 
1894  // Show tabs
1895 
1896  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1897  $maxkey = -1;
1898  if (is_array($links) && !empty($links)) {
1899  $keys = array_keys($links);
1900  if (count($keys)) {
1901  $maxkey = max($keys);
1902  }
1903  }
1904 
1905  // Show tabs
1906  // if =0 we don't use the feature
1907  if (empty($limittoshow)) {
1908  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
1909  }
1910  if (!empty($conf->dol_optimize_smallscreen)) {
1911  $limittoshow = 2;
1912  }
1913 
1914  $displaytab = 0;
1915  $nbintab = 0;
1916  $popuptab = 0;
1917  $outmore = '';
1918  for ($i = 0; $i <= $maxkey; $i++) {
1919  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1920  // If active tab is already present
1921  if ($i >= $limittoshow) {
1922  $limittoshow--;
1923  }
1924  }
1925  }
1926 
1927  for ($i = 0; $i <= $maxkey; $i++) {
1928  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1929  $isactive = true;
1930  } else {
1931  $isactive = false;
1932  }
1933 
1934  if ($i < $limittoshow || $isactive) {
1935  // Output entry with a visible tab
1936  $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])).' -->';
1937 
1938  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1939  if (!empty($links[$i][0])) {
1940  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1941  } else {
1942  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1943  }
1944  } elseif (!empty($links[$i][1])) {
1945  //print "x $i $active ".$links[$i][2]." z";
1946  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
1947  if (!empty($links[$i][0])) {
1948  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
1949  $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).'">';
1950  }
1951  $out .= $links[$i][1];
1952  if (!empty($links[$i][0])) {
1953  $out .= '</a>'."\n";
1954  }
1955  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
1956  $out .= '</div>';
1957  }
1958 
1959  $out .= '</div>';
1960  } else {
1961  // Add entry into the combo popup with the other tabs
1962  if (!$popuptab) {
1963  $popuptab = 1;
1964  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1965  }
1966  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
1967  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1968  if (!empty($links[$i][0])) {
1969  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1970  } else {
1971  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1972  }
1973  } elseif (!empty($links[$i][1])) {
1974  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1975  $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.
1976  $outmore .= '</a>'."\n";
1977  }
1978  $outmore .= '</div>';
1979 
1980  $nbintab++;
1981  }
1982  $displaytab = $i;
1983  }
1984  if ($popuptab) {
1985  $outmore .= '</div>';
1986  }
1987 
1988  if ($popuptab) { // If there is some tabs not shown
1989  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
1990  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
1991  $widthofpopup = 200;
1992 
1993  $tabsname = $moretabssuffix;
1994  if (empty($tabsname)) {
1995  $tabsname = str_replace("@", "", $picto);
1996  }
1997  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
1998  $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".
1999  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2000  $out .= $outmore;
2001  $out .= '</div>';
2002  $out .= '<div></div>';
2003  $out .= "</div>\n";
2004 
2005  $out .= "<script>";
2006  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2007  var x = this.offsetLeft, y = this.offsetTop;
2008  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2009  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2010  $('#moretabsList".$tabsname."').css('".$right."','8px');
2011  }
2012  $('#moretabsList".$tabsname."').css('".$left."','auto');
2013  });
2014  ";
2015  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2016  $out .= "</script>";
2017  }
2018 
2019  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2020  $out .= "</div>\n";
2021  }
2022 
2023  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2024  $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom'))).'">'."\n";
2025  }
2026 
2027  $parameters = array('tabname' => $active, 'out' => $out);
2028  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2029  if ($reshook > 0) {
2030  $out = $hookmanager->resPrint;
2031  }
2032 
2033  return $out;
2034 }
2035 
2043 function dol_fiche_end($notab = 0)
2044 {
2045  print dol_get_fiche_end($notab);
2046 }
2047 
2054 function dol_get_fiche_end($notab = 0)
2055 {
2056  if (!$notab || $notab == -1) {
2057  return "\n</div>\n";
2058  } else {
2059  return '';
2060  }
2061 }
2062 
2082 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2083 {
2084  global $conf, $form, $user, $langs, $hookmanager, $action;
2085 
2086  $error = 0;
2087 
2088  $maxvisiblephotos = 1;
2089  $showimage = 1;
2090  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2091  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2092  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2093  $showbarcode = 0;
2094  }
2095  $modulepart = 'unknown';
2096 
2097  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2098  $modulepart = $object->element;
2099  } elseif ($object->element == 'member') {
2100  $modulepart = 'memberphoto';
2101  } elseif ($object->element == 'user') {
2102  $modulepart = 'userphoto';
2103  }
2104 
2105  if (class_exists("Imagick")) {
2106  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2107  $modulepart = $object->element;
2108  } elseif ($object->element == 'fichinter') {
2109  $modulepart = 'ficheinter';
2110  } elseif ($object->element == 'contrat') {
2111  $modulepart = 'contract';
2112  } elseif ($object->element == 'order_supplier') {
2113  $modulepart = 'supplier_order';
2114  } elseif ($object->element == 'invoice_supplier') {
2115  $modulepart = 'supplier_invoice';
2116  }
2117  }
2118 
2119  if ($object->element == 'product') {
2120  $width = 80;
2121  $cssclass = 'photowithmargin photoref';
2122  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2123  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2124  if ($conf->browser->layout == 'phone') {
2125  $maxvisiblephotos = 1;
2126  }
2127  if ($showimage) {
2128  $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>';
2129  } else {
2130  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2131  $nophoto = '';
2132  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2133  } else { // Show no photo link
2134  $nophoto = '/public/theme/common/nophoto.png';
2135  $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>';
2136  }
2137  }
2138  } elseif ($object->element == 'ticket') {
2139  $width = 80;
2140  $cssclass = 'photoref';
2141  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2142  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2143  if ($conf->browser->layout == 'phone') {
2144  $maxvisiblephotos = 1;
2145  }
2146 
2147  if ($showimage) {
2148  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2149  if ($object->nbphoto > 0) {
2150  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2151  } else {
2152  $showimage = 0;
2153  }
2154  }
2155  if (!$showimage) {
2156  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2157  $nophoto = '';
2158  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2159  } else { // Show no photo link
2160  $nophoto = img_picto('No photo', 'object_ticket');
2161  $morehtmlleft .= '<!-- No photo to show -->';
2162  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2163  $morehtmlleft .= $nophoto;
2164  $morehtmlleft .= '</div></div>';
2165  }
2166  }
2167  } else {
2168  if ($showimage) {
2169  if ($modulepart != 'unknown') {
2170  $phototoshow = '';
2171  // Check if a preview file is available
2172  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2173  $objectref = dol_sanitizeFileName($object->ref);
2174  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2175  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2176  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2177  $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
2178  } else {
2179  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2180  }
2181  if (empty($subdir)) {
2182  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2183  }
2184 
2185  $filepath = $dir_output.$subdir."/";
2186 
2187  $filepdf = $filepath.$objectref.".pdf";
2188  $relativepath = $subdir.'/'.$objectref.'.pdf';
2189 
2190  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2191  $fileimage = $filepdf.'_preview.png';
2192  $relativepathimage = $relativepath.'_preview.png';
2193 
2194  $pdfexists = file_exists($filepdf);
2195 
2196  // If PDF file exists
2197  if ($pdfexists) {
2198  // Conversion du PDF en image png si fichier png non existant
2199  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2200  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2201  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2202  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2203  if ($ret < 0) {
2204  $error++;
2205  }
2206  }
2207  }
2208  }
2209 
2210  if ($pdfexists && !$error) {
2211  $heightforphotref = 80;
2212  if (!empty($conf->dol_optimize_smallscreen)) {
2213  $heightforphotref = 60;
2214  }
2215  // If the preview file is found
2216  if (file_exists($fileimage)) {
2217  $phototoshow = '<div class="photoref">';
2218  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2219  $phototoshow .= '</div>';
2220  }
2221  }
2222  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2223  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2224  }
2225 
2226  if ($phototoshow) {
2227  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2228  $morehtmlleft .= $phototoshow;
2229  $morehtmlleft .= '</div>';
2230  }
2231  }
2232 
2233  if (empty($phototoshow)) { // Show No photo link (picto of object)
2234  if ($object->element == 'action') {
2235  $width = 80;
2236  $cssclass = 'photorefcenter';
2237  $nophoto = img_picto('No photo', 'title_agenda');
2238  } else {
2239  $width = 14;
2240  $cssclass = 'photorefcenter';
2241  $picto = $object->picto;
2242  $prefix = 'object_';
2243  if ($object->element == 'project' && !$object->public) {
2244  $picto = 'project'; // instead of projectpub
2245  }
2246  if (strpos($picto, 'fontawesome_') !== false) {
2247  $prefix = '';
2248  }
2249  $nophoto = img_picto('No photo', $prefix.$picto);
2250  }
2251  $morehtmlleft .= '<!-- No photo to show -->';
2252  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2253  $morehtmlleft .= $nophoto;
2254  $morehtmlleft .= '</div></div>';
2255  }
2256  }
2257  }
2258 
2259  if ($showbarcode) {
2260  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2261  }
2262 
2263  if ($object->element == 'societe') {
2264  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2265  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2266  } else {
2267  $morehtmlstatus .= $object->getLibStatut(6);
2268  }
2269  } elseif ($object->element == 'product') {
2270  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2271  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2272  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2273  } else {
2274  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2275  }
2276  $morehtmlstatus .= ' &nbsp; ';
2277  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2278  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2279  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2280  } else {
2281  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2282  }
2283  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2284  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2285  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2286  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2287  }
2288  $morehtmlstatus .= $tmptxt;
2289  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2290  if ($object->statut == 0) {
2291  $morehtmlstatus .= $object->getLibStatut(5);
2292  } else {
2293  $morehtmlstatus .= $object->getLibStatut(4);
2294  }
2295  } elseif ($object->element == 'facturerec') {
2296  if ($object->frequency == 0) {
2297  $morehtmlstatus .= $object->getLibStatut(2);
2298  } else {
2299  $morehtmlstatus .= $object->getLibStatut(5);
2300  }
2301  } elseif ($object->element == 'project_task') {
2302  $object->fk_statut = 1;
2303  if ($object->progress > 0) {
2304  $object->fk_statut = 2;
2305  }
2306  if ($object->progress >= 100) {
2307  $object->fk_statut = 3;
2308  }
2309  $tmptxt = $object->getLibStatut(5);
2310  $morehtmlstatus .= $tmptxt; // No status on task
2311  } elseif (method_exists($object, 'getLibStatut')) { // Generic case
2312  $tmptxt = $object->getLibStatut(6);
2313  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2314  $tmptxt = $object->getLibStatut(5);
2315  }
2316  $morehtmlstatus .= $tmptxt;
2317  }
2318 
2319  // Add if object was dispatched "into accountancy"
2320  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2321  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2322  if (method_exists($object, 'getVentilExportCompta')) {
2323  $accounted = $object->getVentilExportCompta();
2324  $langs->load("accountancy");
2325  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2326  }
2327  }
2328 
2329  // Add alias for thirdparty
2330  if (!empty($object->name_alias)) {
2331  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->name_alias.'</div>';
2332  }
2333 
2334  // Add label
2335  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2336  if (!empty($object->label)) {
2337  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2338  }
2339  }
2340 
2341  // Show address and email
2342  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2343  $moreaddress = $object->getBannerAddress('refaddress', $object);
2344  if ($moreaddress) {
2345  $morehtmlref .= '<div class="refidno refaddress">';
2346  $morehtmlref .= $moreaddress;
2347  $morehtmlref .= '</div>';
2348  }
2349  }
2350  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)) {
2351  $morehtmlref .= '<div style="clear: both;"></div>';
2352  $morehtmlref .= '<div class="refidno opacitymedium">';
2353  $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id;
2354  $morehtmlref .= '</div>';
2355  }
2356 
2357  $parameters=array('morehtmlref'=>$morehtmlref);
2358  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2359  if ($reshook < 0) {
2360  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2361  } elseif (empty($reshook)) {
2362  $morehtmlref .= $hookmanager->resPrint;
2363  } elseif ($reshook > 0) {
2364  $morehtmlref = $hookmanager->resPrint;
2365  }
2366 
2367 
2368  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2369  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2370  print '</div>';
2371  print '<div class="underrefbanner clearboth"></div>';
2372 }
2373 
2383 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2384 {
2385  global $langs;
2386  $ret = '';
2387  if ($fieldrequired) {
2388  $ret .= '<span class="fieldrequired">';
2389  }
2390  $ret .= '<label for="'.$fieldkey.'">';
2391  $ret .= $langs->trans($langkey);
2392  $ret .= '</label>';
2393  if ($fieldrequired) {
2394  $ret .= '</span>';
2395  }
2396  return $ret;
2397 }
2398 
2406 function dol_bc($var, $moreclass = '')
2407 {
2408  global $bc;
2409  $ret = ' '.$bc[$var];
2410  if ($moreclass) {
2411  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2412  }
2413  return $ret;
2414 }
2415 
2429 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2430 {
2431  global $conf, $langs, $hookmanager;
2432 
2433  $ret = '';
2434  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2435 
2436  // See format of addresses on https://en.wikipedia.org/wiki/Address
2437  // Address
2438  if (empty($mode)) {
2439  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address));
2440  }
2441  // Zip/Town/State
2442  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2443  // US: title firstname name \n address lines \n town, state, zip \n country
2444  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2445  $ret .= (($ret && $town) ? $sep : '').$town;
2446 
2447  if (!empty($object->state)) {
2448  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2449  }
2450  if (!empty($object->zip)) {
2451  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2452  }
2453  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2454  // UK: title firstname name \n address lines \n town state \n zip \n country
2455  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2456  $ret .= ($ret ? $sep : '').$town;
2457  if (!empty($object->state)) {
2458  $ret .= ($ret ? ", " : '').$object->state;
2459  }
2460  if (!empty($object->zip)) {
2461  $ret .= ($ret ? $sep : '').$object->zip;
2462  }
2463  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2464  // ES: title firstname name \n address lines \n zip town \n state \n country
2465  $ret .= ($ret ? $sep : '').$object->zip;
2466  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2467  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2468  if (!empty($object->state)) {
2469  $ret .= "\n".$object->state;
2470  }
2471  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2472  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2473  // See https://www.sljfaq.org/afaq/addresses.html
2474  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2475  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2476  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2477  // IT: title firstname name\n address lines \n zip town state_code \n country
2478  $ret .= ($ret ? $sep : '').$object->zip;
2479  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2480  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2481  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2482  } else {
2483  // Other: title firstname name \n address lines \n zip town[, state] \n country
2484  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2485  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2486  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2487  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2488  $ret .= ($ret ? ", " : '').$object->state;
2489  }
2490  }
2491  if (!is_object($outputlangs)) {
2492  $outputlangs = $langs;
2493  }
2494  if ($withcountry) {
2495  $langs->load("dict");
2496  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2497  }
2498  if ($hookmanager) {
2499  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2500  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2501  if ($reshook > 0) {
2502  $ret = '';
2503  }
2504  $ret .= $hookmanager->resPrint;
2505  }
2506 
2507  return $ret;
2508 }
2509 
2510 
2511 
2520 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2521 {
2522  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2523  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2524  } else {
2525  return 'Error date into a not supported range';
2526  }
2527 }
2528 
2550 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2551 {
2552  global $conf, $langs;
2553 
2554  // If date undefined or "", we return ""
2555  if (dol_strlen($time) == 0) {
2556  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2557  }
2558 
2559  if ($tzoutput === 'auto') {
2560  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2561  }
2562 
2563  // Clean parameters
2564  $to_gmt = false;
2565  $offsettz = $offsetdst = 0;
2566  if ($tzoutput) {
2567  $to_gmt = true; // For backward compatibility
2568  if (is_string($tzoutput)) {
2569  if ($tzoutput == 'tzserver') {
2570  $to_gmt = false;
2571  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2572  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
2573  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
2574  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2575  $to_gmt = true;
2576  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2577 
2578  if (class_exists('DateTimeZone')) {
2579  $user_date_tz = new DateTimeZone($offsettzstring);
2580  $user_dt = new DateTime();
2581  $user_dt->setTimezone($user_date_tz);
2582  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2583  $offsettz = $user_dt->getOffset(); // should include dst ?
2584  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
2585  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2586  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2587  }
2588  }
2589  }
2590  }
2591  if (!is_object($outputlangs)) {
2592  $outputlangs = $langs;
2593  }
2594  if (!$format) {
2595  $format = 'daytextshort';
2596  }
2597 
2598  // Do we have to reduce the length of date (year on 2 chars) to save space.
2599  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2600  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2601  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2602  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2603  if ($formatwithoutreduce != $format) {
2604  $format = $formatwithoutreduce;
2605  $reduceformat = 1;
2606  } // so format 'dayreduceformat' is processed like day
2607 
2608  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2609  // TODO Add format daysmallyear and dayhoursmallyear
2610  if ($format == 'day') {
2611  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2612  } elseif ($format == 'hour') {
2613  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2614  } elseif ($format == 'hourduration') {
2615  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2616  } elseif ($format == 'daytext') {
2617  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2618  } elseif ($format == 'daytextshort') {
2619  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2620  } elseif ($format == 'dayhour') {
2621  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2622  } elseif ($format == 'dayhoursec') {
2623  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2624  } elseif ($format == 'dayhourtext') {
2625  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2626  } elseif ($format == 'dayhourtextshort') {
2627  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2628  } elseif ($format == 'dayhourlog') {
2629  // Format not sensitive to language
2630  $format = '%Y%m%d%H%M%S';
2631  } elseif ($format == 'dayhourlogsmall') {
2632  // Format not sensitive to language
2633  $format = '%y%m%d%H%M';
2634  } elseif ($format == 'dayhourldap') {
2635  $format = '%Y%m%d%H%M%SZ';
2636  } elseif ($format == 'dayhourxcard') {
2637  $format = '%Y%m%dT%H%M%SZ';
2638  } elseif ($format == 'dayxcard') {
2639  $format = '%Y%m%d';
2640  } elseif ($format == 'dayrfc') {
2641  $format = '%Y-%m-%d'; // DATE_RFC3339
2642  } elseif ($format == 'dayhourrfc') {
2643  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2644  } elseif ($format == 'standard') {
2645  $format = '%Y-%m-%d %H:%M:%S';
2646  }
2647 
2648  if ($reduceformat) {
2649  $format = str_replace('%Y', '%y', $format);
2650  $format = str_replace('yyyy', 'yy', $format);
2651  }
2652 
2653  // Clean format
2654  if (preg_match('/%b/i', $format)) { // There is some text to translate
2655  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2656  $format = str_replace('%b', '__b__', $format);
2657  $format = str_replace('%B', '__B__', $format);
2658  }
2659  if (preg_match('/%a/i', $format)) { // There is some text to translate
2660  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2661  $format = str_replace('%a', '__a__', $format);
2662  $format = str_replace('%A', '__A__', $format);
2663  }
2664 
2665  $useadodb = getDolGlobalInt('MAIN_USE_LEGACY_ADODB_FOR_DATE', 0);
2666  //$useadodb = 1; // To switch to adodb
2667  if (!empty($useadodb)) {
2668  include_once DOL_DOCUMENT_ROOT.'/includes/adodbtime/adodb-time.inc.php';
2669  }
2670 
2671  // Analyze date
2672  $reg = array();
2673  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
2674  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2675  return '';
2676  } 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
2677  // This part of code should not be used anymore.
2678  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2679  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2680  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2681  $syear = (!empty($reg[1]) ? $reg[1] : '');
2682  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2683  $sday = (!empty($reg[3]) ? $reg[3] : '');
2684  $shour = (!empty($reg[4]) ? $reg[4] : '');
2685  $smin = (!empty($reg[5]) ? $reg[5] : '');
2686  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2687 
2688  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2689  if (empty($useadodb)) {
2690  if ($to_gmt) {
2691  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2692  } else {
2693  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2694  }
2695  $dtts = new DateTime();
2696  $dtts->setTimestamp($time);
2697  $dtts->setTimezone($tzo);
2698  $newformat = str_replace(
2699  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2700  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2701  $format);
2702  $ret = $dtts->format($newformat);
2703  $ret = str_replace(
2704  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2705  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2706  $ret);
2707  } else {
2708  $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt);
2709  }
2710  } else {
2711  // Date is a timestamps
2712  if ($time < 100000000000) { // Protection against bad date values
2713  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2714 
2715  if (empty($useadodb)) {
2716  if ($to_gmt) {
2717  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2718  } else {
2719  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2720  }
2721  $dtts = new DateTime();
2722  $dtts->setTimestamp($timetouse);
2723  $dtts->setTimezone($tzo);
2724  $newformat = str_replace(
2725  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2726  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2727  $format);
2728  $ret = $dtts->format($newformat);
2729  $ret = str_replace(
2730  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2731  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2732  $ret);
2733  } else {
2734  $ret = adodb_strftime($format, $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2735  }
2736  //var_dump($ret);exit;
2737  } else {
2738  $ret = 'Bad value '.$time.' for date';
2739  }
2740  }
2741 
2742  if (preg_match('/__b__/i', $format)) {
2743  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2744 
2745  if (empty($useadodb)) {
2746  if ($to_gmt) {
2747  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2748  } else {
2749  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2750  }
2751  $dtts = new DateTime();
2752  $dtts->setTimestamp($timetouse);
2753  $dtts->setTimezone($tzo);
2754  $month = $dtts->format("m");
2755  } else {
2756  // After this ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
2757  $month = adodb_strftime('%m', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2758  }
2759  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2760  if ($encodetooutput) {
2761  $monthtext = $outputlangs->transnoentities('Month'.$month);
2762  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2763  } else {
2764  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2765  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2766  }
2767  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2768  $ret = str_replace('__b__', $monthtextshort, $ret);
2769  $ret = str_replace('__B__', $monthtext, $ret);
2770  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2771  //return $ret;
2772  }
2773  if (preg_match('/__a__/i', $format)) {
2774  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2775  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2776 
2777  if (empty($useadodb)) {
2778  if ($to_gmt) {
2779  $tzo = new DateTimeZone('UTC');
2780  } else {
2781  $tzo = new DateTimeZone(date_default_timezone_get());
2782  }
2783  $dtts = new DateTime();
2784  $dtts->setTimestamp($timetouse);
2785  $dtts->setTimezone($tzo);
2786  $w = $dtts->format("w");
2787  } else {
2788  $w = adodb_strftime('%w', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2789  }
2790  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2791 
2792  $ret = str_replace('__A__', $dayweek, $ret);
2793  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2794  }
2795 
2796  return $ret;
2797 }
2798 
2799 
2820 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2821 {
2822  $datetimeobj = new DateTime();
2823  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2824  if ($forcetimezone) {
2825  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2826  }
2827  $arrayinfo = array(
2828  'year'=>((int) date_format($datetimeobj, 'Y')),
2829  'mon'=>((int) date_format($datetimeobj, 'm')),
2830  'mday'=>((int) date_format($datetimeobj, 'd')),
2831  'wday'=>((int) date_format($datetimeobj, 'w')),
2832  'yday'=>((int) date_format($datetimeobj, 'z')),
2833  'hours'=>((int) date_format($datetimeobj, 'H')),
2834  'minutes'=>((int) date_format($datetimeobj, 'i')),
2835  'seconds'=>((int) date_format($datetimeobj, 's')),
2836  '0'=>$timestamp
2837  );
2838 
2839  return $arrayinfo;
2840 }
2841 
2863 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2864 {
2865  global $conf;
2866  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2867 
2868  if ($gm === 'auto') {
2869  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2870  }
2871  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2872 
2873  // Clean parameters
2874  if ($hour == -1 || empty($hour)) {
2875  $hour = 0;
2876  }
2877  if ($minute == -1 || empty($minute)) {
2878  $minute = 0;
2879  }
2880  if ($second == -1 || empty($second)) {
2881  $second = 0;
2882  }
2883 
2884  // Check parameters
2885  if ($check) {
2886  if (!$month || !$day) {
2887  return '';
2888  }
2889  if ($day > 31) {
2890  return '';
2891  }
2892  if ($month > 12) {
2893  return '';
2894  }
2895  if ($hour < 0 || $hour > 24) {
2896  return '';
2897  }
2898  if ($minute < 0 || $minute > 60) {
2899  return '';
2900  }
2901  if ($second < 0 || $second > 60) {
2902  return '';
2903  }
2904  }
2905 
2906  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
2907  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
2908  $localtz = new DateTimeZone($default_timezone);
2909  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
2910  // We use dol_tz_string first because it is more reliable.
2911  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2912  try {
2913  $localtz = new DateTimeZone($default_timezone);
2914  } catch (Exception $e) {
2915  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2916  $default_timezone = @date_default_timezone_get();
2917  }
2918  } elseif (strrpos($gm, "tz,") !== false) {
2919  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2920  try {
2921  $localtz = new DateTimeZone($timezone);
2922  } catch (Exception $e) {
2923  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2924  }
2925  }
2926 
2927  if (empty($localtz)) {
2928  $localtz = new DateTimeZone('UTC');
2929  }
2930  //var_dump($localtz);
2931  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2932  $dt = new DateTime('now', $localtz);
2933  $dt->setDate((int) $year, (int) $month, (int) $day);
2934  $dt->setTime((int) $hour, (int) $minute, (int) $second);
2935  $date = $dt->getTimestamp(); // should include daylight saving time
2936  //var_dump($date);
2937  return $date;
2938 }
2939 
2940 
2951 function dol_now($mode = 'auto')
2952 {
2953  $ret = 0;
2954 
2955  if ($mode === 'auto') {
2956  $mode = 'gmt';
2957  }
2958 
2959  if ($mode == 'gmt') {
2960  $ret = time(); // Time for now at greenwich.
2961  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
2962  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2963  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2964  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2965  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
2966  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2967  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
2968  // $ret=dol_now('gmt')+($tzsecond*3600);
2969  //}
2970  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
2971  // Time for now with user timezone added
2972  //print 'time: '.time();
2973  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
2974  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
2975  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
2976  }
2977 
2978  return $ret;
2979 }
2980 
2981 
2990 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2991 {
2992  global $conf, $langs;
2993  $level = 1024;
2994 
2995  if (!empty($conf->dol_optimize_smallscreen)) {
2996  $shortunit = 1;
2997  }
2998 
2999  // Set value text
3000  if (empty($shortvalue) || $size < ($level * 10)) {
3001  $ret = $size;
3002  $textunitshort = $langs->trans("b");
3003  $textunitlong = $langs->trans("Bytes");
3004  } else {
3005  $ret = round($size / $level, 0);
3006  $textunitshort = $langs->trans("Kb");
3007  $textunitlong = $langs->trans("KiloBytes");
3008  }
3009  // Use long or short text unit
3010  if (empty($shortunit)) {
3011  $ret .= ' '.$textunitlong;
3012  } else {
3013  $ret .= ' '.$textunitshort;
3014  }
3015 
3016  return $ret;
3017 }
3018 
3028 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
3029 {
3030  global $langs;
3031 
3032  if (empty($url)) {
3033  return '';
3034  }
3035 
3036  $link = '<a href="';
3037  if (!preg_match('/^http/i', $url)) {
3038  $link .= 'http://';
3039  }
3040  $link .= $url;
3041  $link .= '"';
3042  if ($target) {
3043  $link .= ' target="'.$target.'"';
3044  }
3045  $link .= '>';
3046  if (!preg_match('/^http/i', $url)) {
3047  $link .= 'http://';
3048  }
3049  $link .= dol_trunc($url, $max);
3050  $link .= '</a>';
3051  return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
3052 }
3053 
3066 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3067 {
3068  global $conf, $user, $langs, $hookmanager;
3069 
3070  $newemail = dol_escape_htmltag($email);
3071 
3072  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
3073  $withpicto = 0;
3074  }
3075 
3076  if (empty($email)) {
3077  return '&nbsp;';
3078  }
3079 
3080  if (!empty($addlink)) {
3081  $newemail = '<a style="text-overflow: ellipsis;" href="';
3082  if (!preg_match('/^mailto:/i', $email)) {
3083  $newemail .= 'mailto:';
3084  }
3085  $newemail .= $email;
3086  $newemail .= '">';
3087  $newemail .= dol_trunc($email, $max);
3088  $newemail .= '</a>';
3089  if ($showinvalid && !isValidEmail($email)) {
3090  $langs->load("errors");
3091  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3092  }
3093 
3094  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3095  $type = 'AC_EMAIL';
3096  $link = '';
3097  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
3098  $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>';
3099  }
3100  if ($link) {
3101  $newemail = '<div>'.$newemail.' '.$link.'</div>';
3102  }
3103  }
3104  } else {
3105  if ($showinvalid && !isValidEmail($email)) {
3106  $langs->load("errors");
3107  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3108  }
3109  }
3110 
3111  //$rep = '<div class="nospan" style="margin-right: 10px">';
3112  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3113  //$rep .= '</div>';
3114  if ($hookmanager) {
3115  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3116 
3117  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3118  if ($reshook > 0) {
3119  $rep = '';
3120  }
3121  $rep .= $hookmanager->resPrint;
3122  }
3123 
3124  return $rep;
3125 }
3126 
3133 {
3134  global $conf, $db;
3135 
3136  $socialnetworks = array();
3137  // Enable caching of array
3138  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3139  $cachekey = 'socialnetworks_' . $conf->entity;
3140  $dataretrieved = dol_getcache($cachekey);
3141  if (!is_null($dataretrieved)) {
3142  $socialnetworks = $dataretrieved;
3143  } else {
3144  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3145  $sql .= " WHERE entity=".$conf->entity;
3146  $resql = $db->query($sql);
3147  if ($resql) {
3148  while ($obj = $db->fetch_object($resql)) {
3149  $socialnetworks[$obj->code] = array(
3150  'rowid' => $obj->rowid,
3151  'label' => $obj->label,
3152  'url' => $obj->url,
3153  'icon' => $obj->icon,
3154  'active' => $obj->active,
3155  );
3156  }
3157  }
3158  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3159  }
3160 
3161  return $socialnetworks;
3162 }
3163 
3174 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3175 {
3176  global $conf, $user, $langs;
3177 
3178  $htmllink = $value;
3179 
3180  if (empty($value)) {
3181  return '&nbsp;';
3182  }
3183 
3184  if (!empty($type)) {
3185  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3186  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3187  $htmllink .= '<span class="fa paddingright '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3188  if ($type == 'skype') {
3189  $htmllink .= dol_escape_htmltag($value);
3190  $htmllink .= '&nbsp;';
3191  $htmllink .= '<a href="skype:';
3192  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3193  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3194  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3195  $htmllink .= '</a><a href="skype:';
3196  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3197  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3198  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3199  $htmllink .= '</a>';
3200  if (($cid || $socid) && isModEnabled('agenda') && $user->rights->agenda->myactions->create) {
3201  $addlink = 'AC_SKYPE';
3202  $link = '';
3203  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3204  $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>';
3205  }
3206  $htmllink .= ($link ? ' '.$link : '');
3207  }
3208  } else {
3209  if (!empty($dictsocialnetworks[$type]['url'])) {
3210  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3211  if ($tmpvirginurl) {
3212  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3213  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3214 
3215  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3216  if ($tmpvirginurl3) {
3217  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3218  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3219  }
3220 
3221  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3222  if ($tmpvirginurl2) {
3223  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3224  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3225  }
3226  }
3227  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3228  if (preg_match('/^https?:\/\//i', $link)) {
3229  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3230  } else {
3231  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3232  }
3233  } else {
3234  $htmllink .= dol_escape_htmltag($value);
3235  }
3236  }
3237  $htmllink .= '</div>';
3238  } else {
3239  $langs->load("errors");
3240  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3241  }
3242  return $htmllink;
3243 }
3244 
3255 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3256 {
3257  global $mysoc;
3258 
3259  if (empty($profID) || empty($profIDtype)) {
3260  return '';
3261  }
3262  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3263  $newProfID = $profID;
3264  $id = substr($profIDtype, -1);
3265  $ret = '';
3266  if (strtoupper($countrycode) == 'FR') {
3267  // France
3268  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3269  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);
3270  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);
3271  }
3272  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3273  else $ret = $newProfID;
3274  return $ret;
3275 }
3276 
3291 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3292 {
3293  global $conf, $user, $langs, $mysoc, $hookmanager;
3294 
3295  // Clean phone parameter
3296  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3297  if (empty($phone)) {
3298  return '';
3299  }
3300  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3301  $separ = $conf->global->MAIN_PHONE_SEPAR;
3302  }
3303  if (empty($countrycode) && is_object($mysoc)) {
3304  $countrycode = $mysoc->country_code;
3305  }
3306 
3307  // Short format for small screens
3308  if ($conf->dol_optimize_smallscreen) {
3309  $separ = '';
3310  }
3311 
3312  $newphone = $phone;
3313  if (strtoupper($countrycode) == "FR") {
3314  // France
3315  if (dol_strlen($phone) == 10) {
3316  $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);
3317  } elseif (dol_strlen($phone) == 7) {
3318  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3319  } elseif (dol_strlen($phone) == 9) {
3320  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3321  } elseif (dol_strlen($phone) == 11) {
3322  $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);
3323  } elseif (dol_strlen($phone) == 12) {
3324  $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);
3325  } elseif (dol_strlen($phone) == 13) {
3326  $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);
3327  }
3328  } elseif (strtoupper($countrycode) == "CA") {
3329  if (dol_strlen($phone) == 10) {
3330  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3331  }
3332  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3333  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3334  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3335  }
3336  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3337  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3338  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3339  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3340  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3341  }
3342  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3343  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3344  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3345  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3346  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3347  }
3348  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3349  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3350  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3351  }
3352  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3353  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3354  $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);
3355  }
3356  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3357  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3358  $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);
3359  }
3360  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3361  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3362  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3363  }
3364  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3365  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3366  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3367  }
3368  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3369  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3370  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3371  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3372  $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);
3373  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3374  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3375  }
3376  } elseif (strtoupper($countrycode) == "ML") {//Mali
3377  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3378  $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);
3379  }
3380  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3381  if (dol_strlen($phone) == 11) {//ex: +66_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: +66_A_BCD_EF_GHI
3384  $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);
3385  }
3386  } elseif (strtoupper($countrycode) == "MU") {
3387  //Maurice
3388  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3389  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3390  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3391  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3392  }
3393  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3394  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3395  $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);
3396  }
3397  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3398  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3399  $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);
3400  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3401  $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);
3402  }
3403  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3404  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3405  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3406  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3407  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3408  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3409  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3410  }
3411  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3412  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3413  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3414  }
3415  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3416  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3417  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3418  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3419  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3420  }
3421  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3422  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3423  $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);
3424  }
3425  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3426  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3427  $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);
3428  }
3429  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3430  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3431  $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);
3432  }
3433  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3434  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3435  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3436  }
3437  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3438  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3439  $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);
3440  }
3441  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3442  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3443  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3444  }
3445  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3446  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3447  $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);
3448  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3449  $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);
3450  }
3451  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3452  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3453  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3454  }
3455  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3456  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3457  $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);
3458  }
3459  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3460  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3461  $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);
3462  }
3463  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3464  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3465  $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);
3466  }
3467  } elseif (strtoupper($countrycode) == "IT") {//Italie
3468  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3469  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3470  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3471  $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);
3472  }
3473  } elseif (strtoupper($countrycode) == "AU") {
3474  //Australie
3475  if (dol_strlen($phone) == 12) {
3476  //ex: +61_A_BCDE_FGHI
3477  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3478  }
3479  } elseif (strtoupper($countrycode) == "LU") {
3480  // Luxembourg
3481  if (dol_strlen($phone) == 10) {// fixe 6 chiffres +352_AA_BB_CC
3482  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
3483  } elseif (dol_strlen($phone) == 11) {// fixe 7 chiffres +352_AA_BB_CC_D
3484  $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);
3485  } elseif (dol_strlen($phone) == 12) {// fixe 8 chiffres +352_AA_BB_CC_DD
3486  $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);
3487  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
3488  $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);
3489  }
3490  }
3491  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3492  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
3493  $newphoneform = $newphone;
3494  $newphone = '<a href="tel:'.$phone.'"';
3495  $newphone .= '>'.$newphoneform.'</a>';
3496  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3497  if (empty($user->clicktodial_loaded)) {
3498  $user->fetch_clicktodial();
3499  }
3500 
3501  // Define urlmask
3502  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3503  if (!empty($conf->global->CLICKTODIAL_URL)) {
3504  $urlmask = $conf->global->CLICKTODIAL_URL;
3505  }
3506  if (!empty($user->clicktodial_url)) {
3507  $urlmask = $user->clicktodial_url;
3508  }
3509 
3510  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3511  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3512  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3513  // This line is for backward compatibility
3514  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3515  // Thoose lines are for substitution
3516  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3517  '__PHONETO__'=>urlencode($phone),
3518  '__LOGIN__'=>$clicktodial_login,
3519  '__PASS__'=>$clicktodial_password);
3520  $url = make_substitutions($url, $substitarray);
3521  $newphonesav = $newphone;
3522  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3523  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3524  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3525  $newphone .= '>'.$newphonesav.'</a>';
3526  } else {
3527  // Old method
3528  $newphone = '<a href="'.$url.'"';
3529  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3530  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3531  }
3532  $newphone .= '>'.$newphonesav.'</a>';
3533  }
3534  }
3535 
3536  //if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
3537  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3538  $type = 'AC_TEL';
3539  $link = '';
3540  if ($addlink == 'AC_FAX') {
3541  $type = 'AC_FAX';
3542  }
3543  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3544  $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage='. urlencode($_SERVER['REQUEST_URI']) .'&amp;actioncode='.$type.($cid ? '&amp;contactid='.$cid : '').($socid ? '&amp;socid='.$socid : '').'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3545  }
3546  if ($link) {
3547  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3548  }
3549  }
3550  }
3551 
3552  if (empty($titlealt)) {
3553  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3554  }
3555  $rep = '';
3556 
3557  if ($hookmanager) {
3558  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3559  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3560  $rep .= $hookmanager->resPrint;
3561  }
3562  if (empty($reshook)) {
3563  $picto = '';
3564  if ($withpicto) {
3565  if ($withpicto == 'fax') {
3566  $picto = 'phoning_fax';
3567  } elseif ($withpicto == 'phone') {
3568  $picto = 'phoning';
3569  } elseif ($withpicto == 'mobile') {
3570  $picto = 'phoning_mobile';
3571  } else {
3572  $picto = '';
3573  }
3574  }
3575  if ($adddivfloat == 1) {
3576  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3577  } elseif (empty($adddivfloat)) {
3578  $rep .= '<span style="margin-right: 10px;">';
3579  }
3580  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3581  if ($adddivfloat == 1) {
3582  $rep .= '</div>';
3583  } elseif (empty($adddivfloat)) {
3584  $rep .= '</span>';
3585  }
3586  }
3587 
3588  return $rep;
3589 }
3590 
3598 function dol_print_ip($ip, $mode = 0)
3599 {
3600  global $conf, $langs;
3601 
3602  $ret = '';
3603 
3604  if (empty($mode)) {
3605  $ret .= $ip;
3606  }
3607 
3608  if ($mode != 2) {
3609  $countrycode = dolGetCountryCodeFromIp($ip);
3610  if ($countrycode) { // If success, countrycode is us, fr, ...
3611  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3612  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3613  } else {
3614  $ret .= ' ('.$countrycode.')';
3615  }
3616  } else {
3617  // Nothing
3618  }
3619  }
3620 
3621  return $ret;
3622 }
3623 
3633 {
3634  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3635  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3636  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3637  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3638  } else {
3639  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3640  }
3641  } else {
3642  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3643  }
3644  } else {
3645  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3646  }
3647  return $ip;
3648 }
3649 
3658 function isHTTPS()
3659 {
3660  $isSecure = false;
3661  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3662  $isSecure = true;
3663  } 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') {
3664  $isSecure = true;
3665  }
3666  return $isSecure;
3667 }
3668 
3676 {
3677  global $conf;
3678 
3679  $countrycode = '';
3680 
3681  if (!empty($conf->geoipmaxmind->enabled)) {
3682  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3683  //$ip='24.24.24.24';
3684  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3685  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3686  $geoip = new DolGeoIP('country', $datafile);
3687  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3688  $countrycode = $geoip->getCountryCodeFromIP($ip);
3689  }
3690 
3691  return $countrycode;
3692 }
3693 
3694 
3702 {
3703  global $conf, $langs, $user;
3704 
3705  //$ret=$user->xxx;
3706  $ret = '';
3707  if (!empty($conf->geoipmaxmind->enabled)) {
3708  $ip = getUserRemoteIP();
3709  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3710  //$ip='24.24.24.24';
3711  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3712  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3713  $geoip = new DolGeoIP('country', $datafile);
3714  $countrycode = $geoip->getCountryCodeFromIP($ip);
3715  $ret = $countrycode;
3716  }
3717  return $ret;
3718 }
3719 
3732 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3733 {
3734  global $conf, $user, $langs, $hookmanager;
3735 
3736  $out = '';
3737 
3738  if ($address) {
3739  if ($hookmanager) {
3740  $parameters = array('element' => $element, 'id' => $id);
3741  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3742  $out .= $hookmanager->resPrint;
3743  }
3744  if (empty($reshook)) {
3745  if (empty($charfornl)) {
3746  $out .= nl2br($address);
3747  } else {
3748  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3749  }
3750 
3751  // TODO Remove this block, we can add this using the hook now
3752  $showgmap = $showomap = 0;
3753  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3754  $showgmap = 1;
3755  }
3756  if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3757  $showgmap = 1;
3758  }
3759  if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3760  $showgmap = 1;
3761  }
3762  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3763  $showomap = 1;
3764  }
3765  if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3766  $showomap = 1;
3767  }
3768  if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3769  $showomap = 1;
3770  }
3771  if ($showgmap) {
3772  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3773  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3774  }
3775  if ($showomap) {
3776  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3777  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3778  }
3779  }
3780  }
3781  if ($noprint) {
3782  return $out;
3783  } else {
3784  print $out;
3785  }
3786 }
3787 
3788 
3798 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3799 {
3800  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3801  return true;
3802  }
3803  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3804  return true;
3805  }
3806  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3807  return true;
3808  }
3809 
3810  return false;
3811 }
3812 
3821 function isValidMXRecord($domain)
3822 {
3823  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3824  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3825  return 0;
3826  }
3827  if (function_exists('getmxrr')) {
3828  $mxhosts = array();
3829  $weight = array();
3830  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3831  if (count($mxhosts) > 1) {
3832  return 1;
3833  }
3834  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3835  return 1;
3836  }
3837 
3838  return 0;
3839  }
3840  }
3841 
3842  // function idn_to_ascii or checkdnsrr does not exists
3843  return -1;
3844 }
3845 
3853 function isValidPhone($phone)
3854 {
3855  return true;
3856 }
3857 
3858 
3868 function dolGetFirstLetters($s, $nbofchar = 1)
3869 {
3870  $ret = '';
3871  $tmparray = explode(' ', $s);
3872  foreach ($tmparray as $tmps) {
3873  $ret .= dol_substr($tmps, 0, $nbofchar);
3874  }
3875 
3876  return $ret;
3877 }
3878 
3879 
3887 function dol_strlen($string, $stringencoding = 'UTF-8')
3888 {
3889  if (is_null($string)) {
3890  return 0;
3891  }
3892 
3893  if (function_exists('mb_strlen')) {
3894  return mb_strlen($string, $stringencoding);
3895  } else {
3896  return strlen($string);
3897  }
3898 }
3899 
3910 function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
3911 {
3912  global $langs;
3913 
3914  if (empty($stringencoding)) {
3915  $stringencoding = $langs->charset_output;
3916  }
3917 
3918  $ret = '';
3919  if (empty($trunconbytes)) {
3920  if (function_exists('mb_substr')) {
3921  $ret = mb_substr($string, $start, $length, $stringencoding);
3922  } else {
3923  $ret = substr($string, $start, $length);
3924  }
3925  } else {
3926  if (function_exists('mb_strcut')) {
3927  $ret = mb_strcut($string, $start, $length, $stringencoding);
3928  } else {
3929  $ret = substr($string, $start, $length);
3930  }
3931  }
3932  return $ret;
3933 }
3934 
3935 
3949 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
3950 {
3951  global $conf;
3952 
3953  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
3954  return $string;
3955  }
3956 
3957  if (empty($stringencoding)) {
3958  $stringencoding = 'UTF-8';
3959  }
3960  // reduce for small screen
3961  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
3962  $size = round($size / 3);
3963  }
3964 
3965  // We go always here
3966  if ($trunc == 'right') {
3967  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3968  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3969  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3970  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
3971  } else {
3972  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
3973  return $string;
3974  }
3975  } elseif ($trunc == 'middle') {
3976  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3977  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3978  $size1 = round($size / 2);
3979  $size2 = round($size / 2);
3980  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
3981  } else {
3982  return $string;
3983  }
3984  } elseif ($trunc == 'left') {
3985  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3986  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3987  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3988  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
3989  } else {
3990  return $string;
3991  }
3992  } elseif ($trunc == 'wrap') {
3993  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3994  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3995  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
3996  } else {
3997  return $string;
3998  }
3999  } else {
4000  return 'BadParam3CallingDolTrunc';
4001  }
4002 }
4003 
4024 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4025 {
4026  global $conf, $langs;
4027 
4028  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4029  $url = DOL_URL_ROOT;
4030  $theme = isset($conf->theme) ? $conf->theme : null;
4031  $path = 'theme/'.$theme;
4032  // Define fullpathpicto to use into src
4033  if ($pictoisfullpath) {
4034  // Clean parameters
4035  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4036  $picto .= '.png';
4037  }
4038  $fullpathpicto = $picto;
4039  $reg = array();
4040  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4041  $morecss .= ($morecss ? ' ' : '').$reg[1];
4042  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4043  }
4044  } else {
4045  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
4046  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4047 
4048  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
4049  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
4050  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4051  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4052 
4053  $pictowithouttextarray = explode('_', $pictowithouttext);
4054  $marginleftonlyshort = 0;
4055 
4056  if (!empty($pictowithouttextarray[1])) {
4057  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4058  $fakey = 'fa-'.$pictowithouttextarray[0];
4059  $fa = empty($pictowithouttextarray[1]) ? 'fa' : $pictowithouttextarray[1];
4060  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4061  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4062  } else {
4063  $fakey = 'fa-'.$pictowithouttext;
4064  $fa = 'fa';
4065  $facolor = '';
4066  $fasize = '';
4067  }
4068 
4069  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4070  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4071  $morestyle = '';
4072  $reg = array();
4073  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4074  $morecss .= ($morecss ? ' ' : '').$reg[1];
4075  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4076  }
4077  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4078  $morestyle = $reg[1];
4079  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4080  }
4081  $moreatt = trim($moreatt);
4082 
4083  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4084  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4085  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4086  $enabledisablehtml .= $titlealt;
4087  }*/
4088  $enabledisablehtml .= '</span>';
4089 
4090  return $enabledisablehtml;
4091  }
4092 
4093  if (empty($srconly) && in_array($pictowithouttext, array(
4094  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4095  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
4096  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
4097  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4098  'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cubes',
4099  'currency', 'multicurrency',
4100  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4101  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4102  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
4103  'gears', 'generate', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4104  'help', 'holiday',
4105  'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4106  'knowledgemanagement',
4107  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4108  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4109  'off', 'on', 'order',
4110  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4111  'stock', 'resize', 'service', 'stats', 'trip',
4112  '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',
4113  'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4114  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4115  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4116  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4117  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4118  'technic', 'ticket',
4119  'error', 'warning',
4120  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4121  'shapes', 'skill', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4122  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
4123  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4124  'conferenceorbooth', 'eventorganization',
4125  'stamp', 'signature'
4126  ))) {
4127  $fakey = $pictowithouttext;
4128  $facolor = '';
4129  $fasize = '';
4130  $fa = 'fas';
4131  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'))) {
4132  $fa = 'far';
4133  }
4134  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4135  $fa = 'fab';
4136  }
4137 
4138  $arrayconvpictotofa = array(
4139  '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',
4140  'bank_account'=>'university',
4141  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
4142  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
4143  'bom'=>'shapes',
4144  'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time',
4145  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
4146  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
4147  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4148  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4149  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4150  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4151  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4152  'generic'=>'file', 'holiday'=>'umbrella-beach',
4153  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'jobprofile'=>'cogs',
4154  'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4155  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4156  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4157  'sign-out'=>'sign-out-alt',
4158  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4159  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4160  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4161  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4162  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4163  'other'=>'square',
4164  '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',
4165  'partnership'=>'handshake', 'payment'=>'money-check-alt', 'payment_vat'=>'money-check-alt', 'pictoconfirm'=>'check-square', 'phoning'=>'phone', 'phoning_mobile'=>'mobile-alt', 'phoning_fax'=>'fax', 'previous'=>'arrow-alt-circle-left', 'printer'=>'print', 'product'=>'cube', 'puce'=>'angle-right',
4166  'recent' => 'check-square', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4167  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4168  'refresh'=>'redo', 'region'=>'map-marked', 'replacement'=>'exchange-alt', 'resource'=>'laptop-house', 'recurring'=>'history',
4169  'service'=>'concierge-bell',
4170  'skill'=>'shapes', 'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4171  'supplier'=>'building', 'technic'=>'cogs',
4172  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4173  'title_agenda'=>'calendar-alt',
4174  'uncheck'=>'times', 'uparrow'=>'share', 'url'=>'external-link-alt', 'vat'=>'money-check-alt', 'vcard'=>'address-card',
4175  'jabber'=>'comment-o',
4176  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4177  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4178  );
4179  if ($pictowithouttext == 'off') {
4180  $fakey = 'fa-square';
4181  $fasize = '1.3em';
4182  } elseif ($pictowithouttext == 'on') {
4183  $fakey = 'fa-check-square';
4184  $fasize = '1.3em';
4185  } elseif ($pictowithouttext == 'listlight') {
4186  $fakey = 'fa-download';
4187  $marginleftonlyshort = 1;
4188  } elseif ($pictowithouttext == 'printer') {
4189  $fakey = 'fa-print';
4190  $fasize = '1.2em';
4191  } elseif ($pictowithouttext == 'note') {
4192  $fakey = 'fa-sticky-note';
4193  $marginleftonlyshort = 1;
4194  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4195  $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');
4196  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4197  if (preg_match('/selected/', $pictowithouttext)) {
4198  $facolor = '#888';
4199  }
4200  $marginleftonlyshort = 1;
4201  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4202  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4203  } else {
4204  $fakey = 'fa-'.$pictowithouttext;
4205  }
4206 
4207  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4208  $morecss .= ' em092';
4209  }
4210  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4211  $morecss .= ' em088';
4212  }
4213  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4214  $morecss .= ' em080';
4215  }
4216 
4217  // Define $marginleftonlyshort
4218  $arrayconvpictotomarginleftonly = array(
4219  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4220  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4221  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4222  );
4223  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4224  $marginleftonlyshort = 0;
4225  }
4226 
4227  // Add CSS
4228  $arrayconvpictotomorcess = array(
4229  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4230  'bank_account'=>'infobox-bank_account',
4231  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4232  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4233  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4234  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4235  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4236  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4237  'incoterm'=>'infobox-supplier_proposal',
4238  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4239  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4240  'order'=>'infobox-commande',
4241  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4242  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_red'=>'font-status8',
4243  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4244  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4245  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4246  'propal'=>'infobox-propal', 'proposal'=>'infobox-propal','private'=>'infobox-project',
4247  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4248  'resource'=>'infobox-action',
4249  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4250  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4251  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4252  'vat'=>'infobox-bank_account',
4253  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4254  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4255  );
4256  if (!empty($arrayconvpictotomorcess[$pictowithouttext])) {
4257  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4258  }
4259 
4260  // Define $color
4261  $arrayconvpictotocolor = array(
4262  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4263  'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4264  'dynamicprice'=>'#a69944',
4265  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4266  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4267  'lock'=>'#ddd', 'lot'=>'#a69944',
4268  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4269  'other'=>'#ddd', 'world'=>'#986c6a',
4270  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4271  //'shipment'=>'#a69944',
4272  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4273  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4274  'website'=>'#304', 'workstation'=>'#a69944'
4275  );
4276  if (isset($arrayconvpictotocolor[$pictowithouttext])) {
4277  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4278  }
4279 
4280  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4281  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4282  $morestyle = '';
4283  $reg = array();
4284  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4285  $morecss .= ($morecss ? ' ' : '').$reg[1];
4286  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4287  }
4288  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4289  $morestyle = $reg[1];
4290  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4291  }
4292  $moreatt = trim($moreatt);
4293 
4294  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4295  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4296  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4297  $enabledisablehtml .= $titlealt;
4298  }*/
4299  $enabledisablehtml .= '</span>';
4300 
4301  return $enabledisablehtml;
4302  }
4303 
4304  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4305  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4306  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4307  $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
4308  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4309  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4310  }
4311 
4312  // If we ask an image into $url/$mymodule/img (instead of default path)
4313  $regs = array();
4314  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4315  $picto = $regs[1];
4316  $path = $regs[2]; // $path is $mymodule
4317  }
4318 
4319  // Clean parameters
4320  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4321  $picto .= '.png';
4322  }
4323  // If alt path are defined, define url where img file is, according to physical path
4324  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4325  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4326  if ($type == 'main') {
4327  continue;
4328  }
4329  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4330  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4331  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4332  break;
4333  }
4334  }
4335 
4336  // $url is '' or '/custom', $path is current theme or
4337  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4338  }
4339 
4340  if ($srconly) {
4341  return $fullpathpicto;
4342  }
4343  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4344  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
4345 }
4346 
4360 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4361 {
4362  if (strpos($picto, '^') === 0) {
4363  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4364  } else {
4365  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4366  }
4367 }
4368 
4380 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4381 {
4382  global $conf;
4383 
4384  if (is_numeric($picto)) {
4385  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4386  //$picto = $leveltopicto[$picto];
4387  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4388  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4389  $picto .= '.png';
4390  }
4391 
4392  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4393 
4394  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4395 }
4396 
4408 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4409 {
4410  global $conf;
4411 
4412  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4413  $picto .= '.png';
4414  }
4415 
4416  if ($pictoisfullpath) {
4417  $path = $picto;
4418  } else {
4419  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4420 
4421  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4422  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4423 
4424  if (file_exists($themepath)) {
4425  $path = $themepath;
4426  }
4427  }
4428  }
4429 
4430  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4431 }
4432 
4445 function img_action($titlealt, $numaction, $picto = '')
4446 {
4447  global $langs;
4448 
4449  if (empty($titlealt) || $titlealt == 'default') {
4450  if ($numaction == '-1' || $numaction == 'ST_NO') {
4451  $numaction = -1;
4452  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4453  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4454  $numaction = 0;
4455  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4456  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4457  $numaction = 1;
4458  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4459  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4460  $numaction = 2;
4461  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4462  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4463  $numaction = 3;
4464  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4465  } else {
4466  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4467  $numaction = 0;
4468  }
4469  }
4470  if (!is_numeric($numaction)) {
4471  $numaction = 0;
4472  }
4473 
4474  return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png');
4475 }
4476 
4484 function img_pdf($titlealt = 'default', $size = 3)
4485 {
4486  global $langs;
4487 
4488  if ($titlealt == 'default') {
4489  $titlealt = $langs->trans('Show');
4490  }
4491 
4492  return img_picto($titlealt, 'pdf'.$size.'.png');
4493 }
4494 
4502 function img_edit_add($titlealt = 'default', $other = '')
4503 {
4504  global $langs;
4505 
4506  if ($titlealt == 'default') {
4507  $titlealt = $langs->trans('Add');
4508  }
4509 
4510  return img_picto($titlealt, 'edit_add.png', $other);
4511 }
4519 function img_edit_remove($titlealt = 'default', $other = '')
4520 {
4521  global $langs;
4522 
4523  if ($titlealt == 'default') {
4524  $titlealt = $langs->trans('Remove');
4525  }
4526 
4527  return img_picto($titlealt, 'edit_remove.png', $other);
4528 }
4529 
4538 function img_edit($titlealt = 'default', $float = 0, $other = '')
4539 {
4540  global $langs;
4541 
4542  if ($titlealt == 'default') {
4543  $titlealt = $langs->trans('Modify');
4544  }
4545 
4546  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4547 }
4548 
4557 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4558 {
4559  global $langs;
4560 
4561  if ($titlealt == 'default') {
4562  $titlealt = $langs->trans('View');
4563  }
4564 
4565  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4566 
4567  return img_picto($titlealt, 'eye', $moreatt);
4568 }
4569 
4578 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4579 {
4580  global $langs;
4581 
4582  if ($titlealt == 'default') {
4583  $titlealt = $langs->trans('Delete');
4584  }
4585 
4586  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4587 }
4588 
4596 function img_printer($titlealt = "default", $other = '')
4597 {
4598  global $langs;
4599  if ($titlealt == "default") {
4600  $titlealt = $langs->trans("Print");
4601  }
4602  return img_picto($titlealt, 'printer.png', $other);
4603 }
4604 
4612 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4613 {
4614  global $langs;
4615 
4616  if ($titlealt == 'default') {
4617  $titlealt = $langs->trans('Split');
4618  }
4619 
4620  return img_picto($titlealt, 'split.png', $other);
4621 }
4622 
4630 function img_help($usehelpcursor = 1, $usealttitle = 1)
4631 {
4632  global $langs;
4633 
4634  if ($usealttitle) {
4635  if (is_string($usealttitle)) {
4636  $usealttitle = dol_escape_htmltag($usealttitle);
4637  } else {
4638  $usealttitle = $langs->trans('Info');
4639  }
4640  }
4641 
4642  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4643 }
4644 
4651 function img_info($titlealt = 'default')
4652 {
4653  global $langs;
4654 
4655  if ($titlealt == 'default') {
4656  $titlealt = $langs->trans('Informations');
4657  }
4658 
4659  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4660 }
4661 
4670 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4671 {
4672  global $langs;
4673 
4674  if ($titlealt == 'default') {
4675  $titlealt = $langs->trans('Warning');
4676  }
4677 
4678  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4679  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4680 }
4681 
4688 function img_error($titlealt = 'default')
4689 {
4690  global $langs;
4691 
4692  if ($titlealt == 'default') {
4693  $titlealt = $langs->trans('Error');
4694  }
4695 
4696  return img_picto($titlealt, 'error.png');
4697 }
4698 
4706 function img_next($titlealt = 'default', $moreatt = '')
4707 {
4708  global $langs;
4709 
4710  if ($titlealt == 'default') {
4711  $titlealt = $langs->trans('Next');
4712  }
4713 
4714  //return img_picto($titlealt, 'next.png', $moreatt);
4715  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4716 }
4717 
4725 function img_previous($titlealt = 'default', $moreatt = '')
4726 {
4727  global $langs;
4728 
4729  if ($titlealt == 'default') {
4730  $titlealt = $langs->trans('Previous');
4731  }
4732 
4733  //return img_picto($titlealt, 'previous.png', $moreatt);
4734  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4735 }
4736 
4745 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4746 {
4747  global $langs;
4748 
4749  if ($titlealt == 'default') {
4750  $titlealt = $langs->trans('Down');
4751  }
4752 
4753  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4754 }
4755 
4764 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4765 {
4766  global $langs;
4767 
4768  if ($titlealt == 'default') {
4769  $titlealt = $langs->trans('Up');
4770  }
4771 
4772  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4773 }
4774 
4783 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4784 {
4785  global $langs;
4786 
4787  if ($titlealt == 'default') {
4788  $titlealt = $langs->trans('Left');
4789  }
4790 
4791  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4792 }
4793 
4802 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4803 {
4804  global $langs;
4805 
4806  if ($titlealt == 'default') {
4807  $titlealt = $langs->trans('Right');
4808  }
4809 
4810  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4811 }
4812 
4820 function img_allow($allow, $titlealt = 'default')
4821 {
4822  global $langs;
4823 
4824  if ($titlealt == 'default') {
4825  $titlealt = $langs->trans('Active');
4826  }
4827 
4828  if ($allow == 1) {
4829  return img_picto($titlealt, 'tick.png');
4830  }
4831 
4832  return '-';
4833 }
4834 
4842 function img_credit_card($brand, $morecss = null)
4843 {
4844  if (is_null($morecss)) {
4845  $morecss = 'fa-2x';
4846  }
4847 
4848  if ($brand == 'visa' || $brand == 'Visa') {
4849  $brand = 'cc-visa';
4850  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4851  $brand = 'cc-mastercard';
4852  } elseif ($brand == 'amex' || $brand == 'American Express') {
4853  $brand = 'cc-amex';
4854  } elseif ($brand == 'discover' || $brand == 'Discover') {
4855  $brand = 'cc-discover';
4856  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4857  $brand = 'cc-jcb';
4858  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4859  $brand = 'cc-diners-club';
4860  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4861  $brand = 'credit-card';
4862  }
4863 
4864  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4865 }
4866 
4875 function img_mime($file, $titlealt = '', $morecss = '')
4876 {
4877  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4878 
4879  $mimetype = dol_mimetype($file, '', 1);
4880  $mimeimg = dol_mimetype($file, '', 2);
4881  $mimefa = dol_mimetype($file, '', 4);
4882 
4883  if (empty($titlealt)) {
4884  $titlealt = 'Mime type: '.$mimetype;
4885  }
4886 
4887  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
4888  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
4889 }
4890 
4891 
4899 function img_search($titlealt = 'default', $other = '')
4900 {
4901  global $conf, $langs;
4902 
4903  if ($titlealt == 'default') {
4904  $titlealt = $langs->trans('Search');
4905  }
4906 
4907  $img = img_picto($titlealt, 'search.png', $other, false, 1);
4908 
4909  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
4910  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4911 
4912  return $input;
4913 }
4914 
4922 function img_searchclear($titlealt = 'default', $other = '')
4923 {
4924  global $conf, $langs;
4925 
4926  if ($titlealt == 'default') {
4927  $titlealt = $langs->trans('Search');
4928  }
4929 
4930  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
4931 
4932  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
4933  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4934 
4935  return $input;
4936 }
4937 
4949 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
4950 {
4951  global $conf, $langs;
4952 
4953  if ($infoonimgalt) {
4954  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
4955  } else {
4956  if (empty($conf->use_javascript_ajax)) {
4957  $textfordropdown = '';
4958  }
4959 
4960  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
4961  $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>');
4962 
4963  if ($textfordropdown) {
4964  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
4965  $tmpresult .= '<script type="text/javascript">
4966  jQuery(document).ready(function() {
4967  jQuery(".'.$class.'text").click(function() {
4968  console.log("toggle text");
4969  jQuery(".'.$class.'").toggle();
4970  });
4971  });
4972  </script>';
4973 
4974  $result = $tmpresult.$result;
4975  }
4976  }
4977 
4978  return $result;
4979 }
4980 
4981 
4993 function dol_print_error($db = '', $error = '', $errors = null)
4994 {
4995  global $conf, $langs, $argv;
4996  global $dolibarr_main_prod;
4997 
4998  $out = '';
4999  $syslog = '';
5000 
5001  // If error occurs before the $lang object was loaded
5002  if (!$langs) {
5003  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5004  $langs = new Translate('', $conf);
5005  $langs->load("main");
5006  }
5007 
5008  // Load translation files required by the error messages
5009  $langs->loadLangs(array('main', 'errors'));
5010 
5011  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5012  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5013  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5014  $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";
5015  }
5016  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5017 
5018  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5019  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5020  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5021  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5022  }
5023  if (function_exists("phpversion")) {
5024  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5025  }
5026  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5027  if (function_exists("php_uname")) {
5028  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5029  }
5030  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5031  $out .= "<br>\n";
5032  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5033  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5034  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5035  $out .= "<br>\n";
5036  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5037  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5038  } else // Mode CLI
5039  {
5040  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5041  $syslog .= "pid=".dol_getmypid();
5042  }
5043 
5044  if (!empty($conf->modules)) {
5045  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
5046  }
5047 
5048  if (is_object($db)) {
5049  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5050  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5051  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ? dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5052  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5053  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5054  $out .= "<br>\n";
5055  } else // Mode CLI
5056  {
5057  // No dol_escape_htmltag for output, we are in CLI mode
5058  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5059  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5060  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5061  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5062  }
5063  $syslog .= ", sql=".$db->lastquery();
5064  $syslog .= ", db_error=".$db->lasterror();
5065  }
5066 
5067  if ($error || $errors) {
5068  $langs->load("errors");
5069 
5070  // Merge all into $errors array
5071  if (is_array($error) && is_array($errors)) {
5072  $errors = array_merge($error, $errors);
5073  } elseif (is_array($error)) {
5074  $errors = $error;
5075  } elseif (is_array($errors)) {
5076  $errors = array_merge(array($error), $errors);
5077  } else {
5078  $errors = array_merge(array($error), array($errors));
5079  }
5080 
5081  foreach ($errors as $msg) {
5082  if (empty($msg)) {
5083  continue;
5084  }
5085  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5086  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5087  } else // Mode CLI
5088  {
5089  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5090  }
5091  $syslog .= ", msg=".$msg;
5092  }
5093  }
5094  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5095  xdebug_print_function_stack();
5096  $out .= '<b>XDebug informations:</b>'."<br>\n";
5097  $out .= 'File: '.xdebug_call_file()."<br>\n";
5098  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5099  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5100  $out .= "<br>\n";
5101  }
5102 
5103  // Return a http header with error code if possible
5104  if (!headers_sent()) {
5105  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5106  top_httphead();
5107  }
5108  http_response_code(500);
5109  }
5110 
5111  if (empty($dolibarr_main_prod)) {
5112  print $out;
5113  } else {
5114  if (empty($langs->defaultlang)) {
5115  $langs->setDefaultLang();
5116  }
5117  $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.
5118  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5119  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";
5120  print $langs->trans("DolibarrHasDetectedError").'. ';
5121  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5122  define("MAIN_CORE_ERROR", 1);
5123  }
5124 
5125  dol_syslog("Error ".$syslog, LOG_ERR);
5126 }
5127 
5138 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5139 {
5140  global $langs, $conf;
5141 
5142  if (empty($email)) {
5143  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
5144  }
5145 
5146  $langs->load("errors");
5147  $now = dol_now();
5148 
5149  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5150  print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S'));
5151  if ($errormessage) {
5152  print '<br><br>'.$errormessage;
5153  }
5154  if (is_array($errormessages) && count($errormessages)) {
5155  foreach ($errormessages as $mesgtoshow) {
5156  print '<br><br>'.$mesgtoshow;
5157  }
5158  }
5159  print '</div></div>';
5160 }
5161 
5178 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5179 {
5180  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5181 }
5182 
5201 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5202 {
5203  global $conf, $langs, $form;
5204  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5205 
5206  if ($moreattrib == 'class="right"') {
5207  $prefix .= 'right '; // For backward compatibility
5208  }
5209 
5210  $sortorder = strtoupper($sortorder);
5211  $out = '';
5212  $sortimg = '';
5213 
5214  $tag = 'th';
5215  if ($thead == 2) {
5216  $tag = 'div';
5217  }
5218 
5219  $tmpsortfield = explode(',', $sortfield);
5220  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5221  $tmpfield = explode(',', $field);
5222  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5223 
5224  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5225  $prefix = 'wrapcolumntitle '.$prefix;
5226  }
5227 
5228  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5229  // If field is used as sort criteria we use a specific css class liste_titre_sel
5230  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5231  $liste_titre = 'liste_titre';
5232  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5233  $liste_titre = 'liste_titre_sel';
5234  }
5235 
5236  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5237  //$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)).'"' : '');
5238  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5239  $tagstart .= '>';
5240 
5241  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5242  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5243  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5244  $options = preg_replace('/&+/i', '&', $options);
5245  if (!preg_match('/^&/', $options)) {
5246  $options = '&'.$options;
5247  }
5248 
5249  $sortordertouseinlink = '';
5250  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5251  if (preg_match('/^DESC/i', $sortorder)) {
5252  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5253  } else { // We reverse the var $sortordertouseinlink
5254  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5255  }
5256  } else { // We are on field that is the first current sorting criteria
5257  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5258  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5259  } else {
5260  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5261  }
5262  }
5263  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5264  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5265  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5266  $out .= '>';
5267  }
5268  if ($tooltip) {
5269  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
5270  if (preg_match('/:\w+$/', $tooltip)) {
5271  $tmptooltip = explode(':', $tooltip);
5272  } else {
5273  $tmptooltip = array($tooltip);
5274  }
5275  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5276  } else {
5277  $out .= $langs->trans($name);
5278  }
5279 
5280  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5281  $out .= '</a>';
5282  }
5283 
5284  if (empty($thead) && $field) { // If this is a sort field
5285  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5286  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5287  $options = preg_replace('/&+/i', '&', $options);
5288  if (!preg_match('/^&/', $options)) {
5289  $options = '&'.$options;
5290  }
5291 
5292  if (!$sortorder || $field1 != $sortfield1) {
5293  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5294  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5295  } else {
5296  if (preg_match('/^DESC/', $sortorder)) {
5297  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5298  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5299  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5300  }
5301  if (preg_match('/^ASC/', $sortorder)) {
5302  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5303  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5304  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5305  }
5306  }
5307  }
5308 
5309  $tagend = '</'.$tag.'>';
5310 
5311  $out = $tagstart.$sortimg.$out.$tagend;
5312 
5313  return $out;
5314 }
5315 
5324 function print_titre($title)
5325 {
5326  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5327 
5328  print '<div class="titre">'.$title.'</div>';
5329 }
5330 
5342 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5343 {
5344  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5345 }
5346 
5360 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5361 {
5362  global $conf;
5363 
5364  $return = '';
5365 
5366  if ($picto == 'setup') {
5367  $picto = 'generic';
5368  }
5369 
5370  $return .= "\n";
5371  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5372  $return .= '<tr class="titre">';
5373  if ($picto) {
5374  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5375  }
5376  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5377  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5378  $return .= '</td>';
5379  if (dol_strlen($morehtmlcenter)) {
5380  $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5381  }
5382  if (dol_strlen($morehtmlright)) {
5383  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
5384  }
5385  $return .= '</tr></table>'."\n";
5386 
5387  return $return;
5388 }
5389 
5413 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 = '')
5414 {
5415  global $conf, $langs;
5416 
5417  $savlimit = $limit;
5418  $savtotalnboflines = $totalnboflines;
5419  $totalnboflines = abs((int) $totalnboflines);
5420 
5421  if ($picto == 'setup') {
5422  $picto = 'title_setup.png';
5423  }
5424  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5425  $picto = 'title.gif';
5426  }
5427  if ($limit < 0) {
5428  $limit = $conf->liste_limit;
5429  }
5430  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5431  $nextpage = 1;
5432  } else {
5433  $nextpage = 0;
5434  }
5435  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
5436 
5437  print "\n";
5438  print "<!-- Begin title -->\n";
5439  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5440 
5441  // Left
5442 
5443  if ($picto && $titre) {
5444  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5445  }
5446  print '<td class="nobordernopadding valignmiddle col-title">';
5447  print '<div class="titre inline-block">'.$titre;
5448  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5449  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5450  }
5451  print '</div></td>';
5452 
5453  // Center
5454  if ($morehtmlcenter) {
5455  print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5456  }
5457 
5458  // Right
5459  print '<td class="nobordernopadding valignmiddle right col-right">';
5460  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5461  if ($sortfield) {
5462  $options .= "&sortfield=".urlencode($sortfield);
5463  }
5464  if ($sortorder) {
5465  $options .= "&sortorder=".urlencode($sortorder);
5466  }
5467  // Show navigation bar
5468  $pagelist = '';
5469  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5470  if ($totalnboflines) { // If we know total nb of lines
5471  // Define nb of extra page links before and after selected page + ... + first or last
5472  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5473 
5474  if ($limit > 0) {
5475  $nbpages = ceil($totalnboflines / $limit);
5476  } else {
5477  $nbpages = 1;
5478  }
5479  $cpt = ($page - $maxnbofpage);
5480  if ($cpt < 0) {
5481  $cpt = 0;
5482  }
5483 
5484  if ($cpt >= 1) {
5485  if (empty($pagenavastextinput)) {
5486  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5487  if ($cpt > 2) {
5488  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5489  } elseif ($cpt == 2) {
5490  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5491  }
5492  }
5493  }
5494 
5495  do {
5496  if ($pagenavastextinput) {
5497  if ($cpt == $page) {
5498  $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5499  $pagelist .= '/';
5500  }
5501  } else {
5502  if ($cpt == $page) {
5503  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5504  } else {
5505  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5506  }
5507  }
5508  $cpt++;
5509  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5510 
5511  if (empty($pagenavastextinput)) {
5512  if ($cpt < $nbpages) {
5513  if ($cpt < $nbpages - 2) {
5514  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5515  } elseif ($cpt == $nbpages - 2) {
5516  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5517  }
5518  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5519  }
5520  } else {
5521  //var_dump($page.' '.$cpt.' '.$nbpages);
5522  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5523  }
5524  } else {
5525  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5526  }
5527  }
5528 
5529  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5530  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
5531  }
5532 
5533  // js to autoselect page field on focus
5534  if ($pagenavastextinput) {
5535  print ajax_autoselect('.pageplusone');
5536  }
5537 
5538  print '</td>';
5539 
5540  print '</tr></table>'."\n";
5541  print "<!-- End title -->\n\n";
5542 }
5543 
5559 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '')
5560 {
5561  global $conf, $langs;
5562 
5563  print '<div class="pagination"><ul>';
5564  if ($beforearrows) {
5565  print '<li class="paginationbeforearrows">';
5566  print $beforearrows;
5567  print '</li>';
5568  }
5569  if ((int) $limit > 0 && empty($hideselectlimit)) {
5570  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5571  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5572  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5573  //$pagesizechoices.=',2:2';
5574  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5575  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5576  }
5577 
5578  print '<li class="pagination">';
5579  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5580  $tmpchoice = explode(',', $pagesizechoices);
5581  $tmpkey = $limit.':'.$limit;
5582  if (!in_array($tmpkey, $tmpchoice)) {
5583  $tmpchoice[] = $tmpkey;
5584  }
5585  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5586  if (!in_array($tmpkey, $tmpchoice)) {
5587  $tmpchoice[] = $tmpkey;
5588  }
5589  asort($tmpchoice, SORT_NUMERIC);
5590  foreach ($tmpchoice as $val) {
5591  $selected = '';
5592  $tmp = explode(':', $val);
5593  $key = $tmp[0];
5594  $val = $tmp[1];
5595  if ($key != '' && $val != '') {
5596  if ((int) $key == (int) $limit) {
5597  $selected = ' selected="selected"';
5598  }
5599  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5600  }
5601  }
5602  print '</select>';
5603  if ($conf->use_javascript_ajax) {
5604  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5605  <script>
5606  jQuery(document).ready(function () {
5607  jQuery(".selectlimit").change(function() {
5608  console.log("Change limit. Send submit");
5609  $(this).parents(\'form:first\').submit();
5610  });
5611  });
5612  </script>
5613  ';
5614  }
5615  print '</li>';
5616  }
5617  if ($page > 0) {
5618  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>';
5619  }
5620  if ($betweenarrows) {
5621  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5622  print $betweenarrows;
5623  print '<!--</div>-->';
5624  }
5625  if ($nextpage > 0) {
5626  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>';
5627  }
5628  if ($afterarrows) {
5629  print '<li class="paginationafterarrows">';
5630  print $afterarrows;
5631  print '</li>';
5632  }
5633  print '</ul></div>'."\n";
5634 }
5635 
5636 
5648 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5649 {
5650  $morelabel = '';
5651 
5652  if (preg_match('/%/', $rate)) {
5653  $rate = str_replace('%', '', $rate);
5654  $addpercent = true;
5655  }
5656  $reg = array();
5657  if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
5658  $morelabel = ' ('.$reg[1].')';
5659  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5660  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5661  }
5662  if (preg_match('/\*/', $rate)) {
5663  $rate = str_replace('*', '', $rate);
5664  $info_bits |= 1;
5665  }
5666 
5667  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5668  if (!preg_match('/\//', $rate)) {
5669  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5670  } else {
5671  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5672  $ret = $rate.($addpercent ? '%' : '');
5673  }
5674  if (($info_bits & 1) && $usestarfornpr >= 0) {
5675  $ret .= ' *';
5676  }
5677  $ret .= $morelabel;
5678  return $ret;
5679 }
5680 
5681 
5697 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5698 {
5699  global $langs, $conf;
5700 
5701  // Clean parameters
5702  if (empty($amount)) {
5703  $amount = 0; // To have a numeric value if amount not defined or = ''
5704  }
5705  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5706  if ($rounding < 0) {
5707  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5708  }
5709  $nbdecimal = $rounding;
5710 
5711  if ($outlangs === 'none') {
5712  // Use international separators
5713  $dec = '.';
5714  $thousand = '';
5715  } else {
5716  // Output separators by default (french)
5717  $dec = ',';
5718  $thousand = ' ';
5719 
5720  // If $outlangs not forced, we use use language
5721  if (!is_object($outlangs)) {
5722  $outlangs = $langs;
5723  }
5724 
5725  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5726  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5727  }
5728  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5729  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5730  }
5731  if ($thousand == 'None') {
5732  $thousand = '';
5733  } elseif ($thousand == 'Space') {
5734  $thousand = ' ';
5735  }
5736  }
5737  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5738 
5739  //print "amount=".$amount."-";
5740  $amount = str_replace(',', '.', $amount); // should be useless
5741  //print $amount."-";
5742  $datas = explode('.', $amount);
5743  $decpart = isset($datas[1]) ? $datas[1] : '';
5744  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5745  //print "decpart=".$decpart."<br>";
5746  $end = '';
5747 
5748  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5749  if (dol_strlen($decpart) > $nbdecimal) {
5750  $nbdecimal = dol_strlen($decpart);
5751  }
5752  // Si on depasse max
5753  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5754  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5755  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5756  // Si un affichage est tronque, on montre des ...
5757  $end = '...';
5758  }
5759  }
5760 
5761  // If force rounding
5762  if ((string) $forcerounding != '-1') {
5763  if ($forcerounding === 'MU') {
5764  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5765  } elseif ($forcerounding === 'MT') {
5766  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_TOT;
5767  } elseif ($forcerounding >= 0) {
5768  $nbdecimal = $forcerounding;
5769  }
5770  }
5771 
5772  // Format number
5773  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5774  if ($form) {
5775  $output = preg_replace('/\s/', '&nbsp;', $output);
5776  $output = preg_replace('/\'/', '&#039;', $output);
5777  }
5778  // Add symbol of currency if requested
5779  $cursymbolbefore = $cursymbolafter = '';
5780  if ($currency_code && is_object($outlangs)) {
5781  if ($currency_code == 'auto') {
5782  $currency_code = $conf->currency;
5783  }
5784 
5785  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC');
5786  $listoflanguagesbefore = array('nl_NL');
5787  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5788  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5789  } else {
5790  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5791  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5792  }
5793  }
5794  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5795 
5796  return $output;
5797 }
5798 
5823 function price2num($amount, $rounding = '', $option = 0)
5824 {
5825  global $langs, $conf;
5826 
5827  // Clean parameters
5828  if (is_null($amount)) {
5829  $amount = '';
5830  }
5831 
5832  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5833  // Numbers must be '1234.56'
5834  // Decimal delimiter for PHP and database SQL requests must be '.'
5835  $dec = ',';
5836  $thousand = ' ';
5837  if (is_null($langs)) { // $langs is not defined, we use english values.
5838  $dec = '.';
5839  $thousand = ',';
5840  } else {
5841  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5842  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5843  }
5844  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5845  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5846  }
5847  }
5848  if ($thousand == 'None') {
5849  $thousand = '';
5850  } elseif ($thousand == 'Space') {
5851  $thousand = ' ';
5852  }
5853  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5854 
5855  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5856  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5857  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5858  if (!is_numeric($amount)) {
5859  $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
5860  }
5861 
5862  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
5863  $amount = str_replace($thousand, '', $amount);
5864  }
5865 
5866  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5867  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
5868  // So if number was already a good number, it is converted into local Dolibarr setup.
5869  if (is_numeric($amount)) {
5870  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5871  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5872  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5873  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5874  $amount = number_format($amount, $nbofdec, $dec, $thousand);
5875  }
5876  //print "QQ".$amount."<br>\n";
5877 
5878  // Now make replace (the main goal of function)
5879  if ($thousand != ',' && $thousand != '.') {
5880  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5881  }
5882 
5883  $amount = str_replace(' ', '', $amount); // To avoid spaces
5884  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5885  $amount = str_replace($dec, '.', $amount);
5886 
5887  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5888  }
5889  //print ' XX'.$amount.' '.$rounding;
5890 
5891  // Now, $amount is a real PHP float number. We make a rounding if required.
5892  if ($rounding) {
5893  $nbofdectoround = '';
5894  if ($rounding == 'MU') {
5895  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5896  } elseif ($rounding == 'MT') {
5897  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
5898  } elseif ($rounding == 'MS') {
5899  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
5900  } elseif ($rounding == 'CU') {
5901  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
5902  } elseif ($rounding == 'CT') {
5903  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
5904  } elseif (is_numeric($rounding)) {
5905  $nbofdectoround = (int) $rounding;
5906  }
5907 
5908  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
5909  if (dol_strlen($nbofdectoround)) {
5910  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
5911  } else {
5912  return 'ErrorBadParameterProvidedToFunction';
5913  }
5914  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
5915 
5916  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5917  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
5918  if (is_numeric($amount)) {
5919  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5920  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5921  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5922  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5923  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
5924  }
5925  //print "TT".$amount.'<br>';
5926 
5927  // Always make replace because each math function (like round) replace
5928  // with local values and we want a number that has a SQL string format x.y
5929  if ($thousand != ',' && $thousand != '.') {
5930  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5931  }
5932 
5933  $amount = str_replace(' ', '', $amount); // To avoid spaces
5934  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5935  $amount = str_replace($dec, '.', $amount);
5936 
5937  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5938  }
5939 
5940  return $amount;
5941 }
5942 
5955 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
5956 {
5957  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
5958 
5959  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
5960  $dimension = $dimension * 1000000;
5961  $unit = $unit - 6;
5962  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
5963  $dimension = $dimension * 1000;
5964  $unit = $unit - 3;
5965  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
5966  $dimension = $dimension / 1000000;
5967  $unit = $unit + 6;
5968  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
5969  $dimension = $dimension / 1000;
5970  $unit = $unit + 3;
5971  }
5972  // Special case when we want output unit into pound or ounce
5973  /* TODO
5974  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
5975  {
5976  $dimension = // convert dimension from standard unit into ounce or pound
5977  $unit = $forceunitoutput;
5978  }
5979  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
5980  {
5981  $dimension = // convert dimension from standard unit into ounce or pound
5982  $unit = $forceunitoutput;
5983  }*/
5984 
5985  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
5986  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
5987 
5988  return $ret;
5989 }
5990 
5991 
6004 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
6005 {
6006  global $db, $conf, $mysoc;
6007 
6008  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6009  $thirdparty_seller = $mysoc;
6010  }
6011 
6012  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);
6013 
6014  $vatratecleaned = $vatrate;
6015  $reg = array();
6016  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6017  $vatratecleaned = trim($reg[1]);
6018  $vatratecode = $reg[2];
6019  }
6020 
6021  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6022  {
6023  return 0;
6024  }*/
6025 
6026  // Some test to guess with no need to make database access
6027  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6028  if ($local == 1) {
6029  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6030  return 0;
6031  }
6032  if ($thirdparty_seller->id == $mysoc->id) {
6033  if (!$thirdparty_buyer->localtax1_assuj) {
6034  return 0;
6035  }
6036  } else {
6037  if (!$thirdparty_seller->localtax1_assuj) {
6038  return 0;
6039  }
6040  }
6041  }
6042 
6043  if ($local == 2) {
6044  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6045  if (!$mysoc->localtax2_assuj) {
6046  return 0; // If main vat is 0, IRPF may be different than 0.
6047  }
6048  if ($thirdparty_seller->id == $mysoc->id) {
6049  if (!$thirdparty_buyer->localtax2_assuj) {
6050  return 0;
6051  }
6052  } else {
6053  if (!$thirdparty_seller->localtax2_assuj) {
6054  return 0;
6055  }
6056  }
6057  }
6058  } else {
6059  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6060  return 0;
6061  }
6062  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6063  return 0;
6064  }
6065  }
6066 
6067  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6068  if (in_array($mysoc->country_code, array('ES'))) {
6069  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6070  }
6071 
6072  // Search local taxes
6073  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
6074  if ($local == 1) {
6075  if ($thirdparty_seller != $mysoc) {
6076  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6077  return $thirdparty_seller->localtax1_value;
6078  }
6079  } else { // i am the seller
6080  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6081  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6082  }
6083  }
6084  }
6085  if ($local == 2) {
6086  if ($thirdparty_seller != $mysoc) {
6087  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6088  // TODO We should also return value defined on thirdparty only if defined
6089  return $thirdparty_seller->localtax2_value;
6090  }
6091  } else { // i am the seller
6092  if (in_array($mysoc->country_code, array('ES'))) {
6093  return $thirdparty_buyer->localtax2_value;
6094  } else {
6095  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6096  }
6097  }
6098  }
6099  }
6100 
6101  // By default, search value of local tax on line of common tax
6102  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6103  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6104  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6105  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6106  if (!empty($vatratecode)) {
6107  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6108  } else {
6109  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
6110  }
6111 
6112  $resql = $db->query($sql);
6113 
6114  if ($resql) {
6115  $obj = $db->fetch_object($resql);
6116  if ($obj) {
6117  if ($local == 1) {
6118  return $obj->localtax1;
6119  } elseif ($local == 2) {
6120  return $obj->localtax2;
6121  }
6122  }
6123  }
6124 
6125  return 0;
6126 }
6127 
6128 
6137 function isOnlyOneLocalTax($local)
6138 {
6139  $tax = get_localtax_by_third($local);
6140 
6141  $valors = explode(":", $tax);
6142 
6143  if (count($valors) > 1) {
6144  return false;
6145  } else {
6146  return true;
6147  }
6148 }
6149 
6156 function get_localtax_by_third($local)
6157 {
6158  global $db, $mysoc;
6159 
6160  $sql = " SELECT t.localtax".$local." as localtax";
6161  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
6162  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = (";
6163  $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";
6164  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)";
6165  $sql .= " AND t.localtax".$local."_type <> '0'";
6166  $sql .= " ORDER BY t.rowid DESC";
6167 
6168  $resql = $db->query($sql);
6169  if ($resql) {
6170  $obj = $db->fetch_object($resql);
6171  return $obj->localtax;
6172  } else {
6173  return 'Error';
6174  }
6175 
6176  return '0';
6177 }
6178 
6179 
6191 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6192 {
6193  global $db, $mysoc;
6194 
6195  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6196 
6197  // Search local taxes
6198  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6199  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6200  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6201  if ($firstparamisid) {
6202  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6203  } else {
6204  $vatratecleaned = $vatrate;
6205  $vatratecode = '';
6206  $reg = array();
6207  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6208  $vatratecleaned = $reg[1];
6209  $vatratecode = $reg[2];
6210  }
6211 
6212  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6213  /*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 ??
6214  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6215  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6216  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6217  if ($vatratecode) {
6218  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6219  }
6220  }
6221 
6222  $resql = $db->query($sql);
6223  if ($resql) {
6224  $obj = $db->fetch_object($resql);
6225  if ($obj) {
6226  return array(
6227  'rowid'=>$obj->rowid,
6228  'code'=>$obj->code,
6229  'rate'=>$obj->rate,
6230  'localtax1'=>$obj->localtax1,
6231  'localtax1_type'=>$obj->localtax1_type,
6232  'localtax2'=>$obj->localtax2,
6233  'localtax2_type'=>$obj->localtax2_type,
6234  'npr'=>$obj->npr,
6235  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6236  'accountancy_code_buy'=>$obj->accountancy_code_buy
6237  );
6238  } else {
6239  return array();
6240  }
6241  } else {
6242  dol_print_error($db);
6243  }
6244 
6245  return array();
6246 }
6247 
6264 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6265 {
6266  global $db, $mysoc;
6267 
6268  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6269 
6270  // Search local taxes
6271  $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";
6272  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6273  if ($firstparamisid) {
6274  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6275  } else {
6276  $vatratecleaned = $vatrate;
6277  $vatratecode = '';
6278  $reg = array();
6279  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6280  $vatratecleaned = $reg[1];
6281  $vatratecode = $reg[2];
6282  }
6283 
6284  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6285  if (!empty($mysoc) && $mysoc->country_code == 'ES') {
6286  $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
6287  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
6288  } else {
6289  $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
6290  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
6291  }
6292  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6293  if ($vatratecode) {
6294  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6295  }
6296  }
6297 
6298  $resql = $db->query($sql);
6299  if ($resql) {
6300  $obj = $db->fetch_object($resql);
6301 
6302  if ($obj) {
6303  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6304 
6305  if ($local == 1) {
6306  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6307  } elseif ($local == 2) {
6308  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6309  } else {
6310  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);
6311  }
6312  }
6313  }
6314 
6315  return array();
6316 }
6317 
6328 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6329 {
6330  global $db, $conf, $mysoc;
6331 
6332  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6333 
6334  $ret = 0;
6335  $found = 0;
6336 
6337  if ($idprod > 0) {
6338  // Load product
6339  $product = new Product($db);
6340  $product->fetch($idprod);
6341 
6342  if ($mysoc->country_code == $thirdpartytouse->country_code) {
6343  // If country to consider is ours
6344  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6345  $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
6346  if ($result > 0) {
6347  $ret = $product->vatrate_supplier;
6348  if ($product->default_vat_code_supplier) {
6349  $ret .= ' ('.$product->default_vat_code_supplier.')';
6350  }
6351  $found = 1;
6352  }
6353  }
6354  if (!$found) {
6355  $ret = $product->tva_tx; // Default sales vat of product
6356  if ($product->default_vat_code) {
6357  $ret .= ' ('.$product->default_vat_code.')';
6358  }
6359  $found = 1;
6360  }
6361  } else {
6362  // TODO Read default product vat according to product and another countrycode.
6363  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6364  }
6365  }
6366 
6367  if (!$found) {
6368  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6369  // 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).
6370  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6371  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6372  $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
6373  $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
6374  $sql .= $db->plimit(1);
6375 
6376  $resql = $db->query($sql);
6377  if ($resql) {
6378  $obj = $db->fetch_object($resql);
6379  if ($obj) {
6380  $ret = $obj->vat_rate;
6381  if ($obj->default_vat_code) {
6382  $ret .= ' ('.$obj->default_vat_code.')';
6383  }
6384  }
6385  $db->free($resql);
6386  } else {
6387  dol_print_error($db);
6388  }
6389  } else {
6390  // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
6391  // '1.23'
6392  // or '1.23 (CODE)'
6393  $defaulttx = '';
6394  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6395  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6396  }
6397  /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6398  $defaultcode = $reg[1];
6399  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6400  }*/
6401 
6402  $ret = $defaulttx;
6403  }
6404  }
6405 
6406  dol_syslog("get_product_vat_for_country: ret=".$ret);
6407  return $ret;
6408 }
6409 
6419 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6420 {
6421  global $db, $mysoc;
6422 
6423  if (!class_exists('Product')) {
6424  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6425  }
6426 
6427  $ret = 0;
6428  $found = 0;
6429 
6430  if ($idprod > 0) {
6431  // Load product
6432  $product = new Product($db);
6433  $result = $product->fetch($idprod);
6434 
6435  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6436  /* Not defined yet, so we don't use this
6437  if ($local==1) $ret=$product->localtax1_tx;
6438  elseif ($local==2) $ret=$product->localtax2_tx;
6439  $found=1;
6440  */
6441  } else {
6442  // TODO Read default product vat according to product and another countrycode.
6443  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6444  }
6445  }
6446 
6447  if (!$found) {
6448  // If vat of product for the country not found or not defined, we return higher vat of country.
6449  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6450  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6451  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6452  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6453  $sql .= $db->plimit(1);
6454 
6455  $resql = $db->query($sql);
6456  if ($resql) {
6457  $obj = $db->fetch_object($resql);
6458  if ($obj) {
6459  if ($local == 1) {
6460  $ret = $obj->localtax1;
6461  } elseif ($local == 2) {
6462  $ret = $obj->localtax2;
6463  }
6464  }
6465  } else {
6466  dol_print_error($db);
6467  }
6468  }
6469 
6470  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6471  return $ret;
6472 }
6473 
6490 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6491 {
6492  global $conf;
6493 
6494  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6495 
6496  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6497  $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;
6498 
6499  $seller_country_code = $thirdparty_seller->country_code;
6500  $seller_in_cee = isInEEC($thirdparty_seller);
6501 
6502  $buyer_country_code = $thirdparty_buyer->country_code;
6503  $buyer_in_cee = isInEEC($thirdparty_buyer);
6504 
6505  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 : ''));
6506 
6507  // 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)
6508  // we use the buyer VAT.
6509  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6510  if ($seller_in_cee && $buyer_in_cee) {
6511  $isacompany = $thirdparty_buyer->isACompany();
6512  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6513  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6514  if (!isValidVATID($thirdparty_buyer)) {
6515  $isacompany = 0;
6516  }
6517  }
6518 
6519  if (!$isacompany) {
6520  //print 'VATRULE 0';
6521  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6522  }
6523  }
6524  }
6525 
6526  // If seller does not use VAT
6527  if (!$seller_use_vat) {
6528  //print 'VATRULE 1';
6529  return 0;
6530  }
6531 
6532  // 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.
6533 
6534  // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
6535  if (($seller_country_code == $buyer_country_code)
6536  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6537  //print 'VATRULE 2';
6538  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6539  }
6540 
6541  // 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.
6542  // 'VATRULE 3' - Not supported
6543 
6544  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
6545  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6546  if (($seller_in_cee && $buyer_in_cee)) {
6547  $isacompany = $thirdparty_buyer->isACompany();
6548  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6549  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6550  if (!isValidVATID($thirdparty_buyer)) {
6551  $isacompany = 0;
6552  }
6553  }
6554 
6555  if (!$isacompany) {
6556  //print 'VATRULE 4';
6557  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6558  } else {
6559  //print 'VATRULE 5';
6560  return 0;
6561  }
6562  }
6563 
6564  // Si (vendeur dans Communaute europeene et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6565  // I don't see any use case that need this rule.
6566  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6567  $isacompany = $thirdparty_buyer->isACompany();
6568  if (!$isacompany) {
6569  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6570  //print 'VATRULE extra';
6571  }
6572  }
6573 
6574  // Sinon la TVA proposee par defaut=0. Fin de regle.
6575  // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
6576  //print 'VATRULE 6';
6577  return 0;
6578 }
6579 
6580 
6591 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6592 {
6593  global $db;
6594 
6595  if ($idprodfournprice > 0) {
6596  if (!class_exists('ProductFournisseur')) {
6597  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6598  }
6599  $prodprice = new ProductFournisseur($db);
6600  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6601  return $prodprice->fourn_tva_npr;
6602  } elseif ($idprod > 0) {
6603  if (!class_exists('Product')) {
6604  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6605  }
6606  $prod = new Product($db);
6607  $prod->fetch($idprod);
6608  return $prod->tva_npr;
6609  }
6610 
6611  return 0;
6612 }
6613 
6627 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6628 {
6629  global $mysoc;
6630 
6631  if (!is_object($thirdparty_seller)) {
6632  return -1;
6633  }
6634  if (!is_object($thirdparty_buyer)) {
6635  return -1;
6636  }
6637 
6638  if ($local == 1) { // Localtax 1
6639  if ($mysoc->country_code == 'ES') {
6640  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6641  return 0;
6642  }
6643  } else {
6644  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6645  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6646  return 0;
6647  }
6648  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6649  return 0;
6650  }
6651  }
6652  } elseif ($local == 2) { //I Localtax 2
6653  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6654  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6655  return 0;
6656  }
6657  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6658  return 0;
6659  }
6660  }
6661 
6662  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6663  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6664  }
6665 
6666  return 0;
6667 }
6668 
6677 function yn($yesno, $case = 1, $color = 0)
6678 {
6679  global $langs;
6680 
6681  $result = 'unknown';
6682  $classname = '';
6683  if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') { // A mettre avant test sur no a cause du == 0
6684  $result = $langs->trans('yes');
6685  if ($case == 1 || $case == 3) {
6686  $result = $langs->trans("Yes");
6687  }
6688  if ($case == 2) {
6689  $result = '<input type="checkbox" value="1" checked disabled>';
6690  }
6691  if ($case == 3) {
6692  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6693  }
6694 
6695  $classname = 'ok';
6696  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6697  $result = $langs->trans("no");
6698  if ($case == 1 || $case == 3) {
6699  $result = $langs->trans("No");
6700  }
6701  if ($case == 2) {
6702  $result = '<input type="checkbox" value="0" disabled>';
6703  }
6704  if ($case == 3) {
6705  $result = '<input type="checkbox" value="0" disabled> '.$result;
6706  }
6707 
6708  if ($color == 2) {
6709  $classname = 'ok';
6710  } else {
6711  $classname = 'error';
6712  }
6713  }
6714  if ($color) {
6715  return '<span class="'.$classname.'">'.$result.'</span>';
6716  }
6717  return $result;
6718 }
6719 
6735 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6736 {
6737  global $conf;
6738 
6739  if (empty($modulepart) && !empty($object->module)) {
6740  $modulepart = $object->module;
6741  }
6742 
6743  $path = '';
6744 
6745  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6746  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
6747  $arrayforoldpath[] = 'product';
6748  }
6749  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6750  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6751  if (empty($alpha)) {
6752  $num = preg_replace('/([^0-9])/i', '', $num);
6753  } else {
6754  $num = preg_replace('/^.*\-/i', '', $num);
6755  }
6756  $num = substr("000".$num, -$level);
6757  if ($level == 1) {
6758  $path = substr($num, 0, 1);
6759  }
6760  if ($level == 2) {
6761  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6762  }
6763  if ($level == 3) {
6764  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6765  }
6766  } else {
6767  // We will enhance here a common way of forging path for document storage.
6768  // In a future, we may distribute directories on several levels depending on setup and object.
6769  // Here, $object->id, $object->ref and $modulepart are required.
6770  //var_dump($modulepart);
6771  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6772  }
6773 
6774  if (empty($withoutslash) && !empty($path)) {
6775  $path .= '/';
6776  }
6777 
6778  return $path;
6779 }
6780 
6789 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6790 {
6791  global $conf;
6792 
6793  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6794 
6795  $dir_osencoded = dol_osencode($dir);
6796  if (@is_dir($dir_osencoded)) {
6797  return 0;
6798  }
6799 
6800  $nberr = 0;
6801  $nbcreated = 0;
6802 
6803  $ccdir = '';
6804  if (!empty($dataroot)) {
6805  // Remove data root from loop
6806  $dir = str_replace($dataroot.'/', '', $dir);
6807  $ccdir = $dataroot.'/';
6808  }
6809 
6810  $cdir = explode("/", $dir);
6811  $num = count($cdir);
6812  for ($i = 0; $i < $num; $i++) {
6813  if ($i > 0) {
6814  $ccdir .= '/'.$cdir[$i];
6815  } else {
6816  $ccdir .= $cdir[$i];
6817  }
6818  if (preg_match("/^.:$/", $ccdir, $regs)) {
6819  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6820  }
6821 
6822  // Attention, le is_dir() peut echouer bien que le rep existe.
6823  // (ex selon config de open_basedir)
6824  if ($ccdir) {
6825  $ccdir_osencoded = dol_osencode($ccdir);
6826  if (!@is_dir($ccdir_osencoded)) {
6827  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6828 
6829  umask(0);
6830  $dirmaskdec = octdec((string) $newmask);
6831  if (empty($newmask)) {
6832  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6833  }
6834  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
6835  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
6836  // Si le is_dir a renvoye une fausse info, alors on passe ici.
6837  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
6838  $nberr++;
6839  } else {
6840  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
6841  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
6842  $nbcreated++;
6843  }
6844  } else {
6845  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
6846  }
6847  }
6848  }
6849  return ($nberr ? -$nberr : $nbcreated);
6850 }
6851 
6852 
6858 function picto_required()
6859 {
6860  return '<span class="fieldrequired">*</span>';
6861 }
6862 
6863 
6880 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
6881 {
6882  if (is_null($stringtoclean)) {
6883  return '';
6884  }
6885 
6886  if ($removelinefeed == 2) {
6887  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
6888  }
6889  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
6890 
6891  // 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)
6892  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
6893 
6894  $temp = str_replace('< ', '__ltspace__', $temp);
6895 
6896  if ($strip_tags) {
6897  $temp = strip_tags($temp);
6898  } else {
6899  // 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).
6900  $pattern = "/<[^<>]+>/";
6901  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
6902  // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
6903  // pass 2 - $temp after pass 2: 0000-021
6904  $tempbis = $temp;
6905  do {
6906  $temp = $tempbis;
6907  $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
6908  $tempbis = preg_replace($pattern, '', $tempbis);
6909  //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
6910  } while ($tempbis != $temp);
6911 
6912  $temp = $tempbis;
6913 
6914  // 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).
6915  $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
6916  }
6917 
6918  $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
6919 
6920  // Remove also carriage returns
6921  if ($removelinefeed == 1) {
6922  $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
6923  }
6924 
6925  // And double quotes
6926  if ($removedoublespaces) {
6927  while (strpos($temp, " ")) {
6928  $temp = str_replace(" ", " ", $temp);
6929  }
6930  }
6931 
6932  $temp = str_replace('__ltspace__', '< ', $temp);
6933 
6934  return trim($temp);
6935 }
6936 
6951 function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array())
6952 {
6953  if (empty($allowed_tags)) {
6954  $allowed_tags = array(
6955  "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link",
6956  "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"
6957  );
6958  }
6959  $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
6960  if ($allowiframe) {
6961  $allowed_tags[] = "iframe";
6962  }
6963 
6964  $allowed_tags_string = join("><", $allowed_tags);
6965  $allowed_tags_string = '<'.$allowed_tags_string.'>';
6966 
6967  $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
6968 
6969  $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
6970 
6971  //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
6972  $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
6973 
6974  $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
6975  $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
6976 
6977  $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
6978 
6979  if ($cleanalsosomestyles) { // Clean for remaining html tags
6980  $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
6981  }
6982  if ($removeclassattribute) { // Clean for remaining html tags
6983  $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
6984  }
6985 
6986  // Remove 'javascript:' that we should not find into a text with
6987  // 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)).
6988  if ($cleanalsojavascript) {
6989  $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);
6990  }
6991 
6992  $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
6993 
6994  $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
6995 
6996 
6997  return $temp;
6998 }
6999 
7000 
7012 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"))
7013 {
7014  if (class_exists('DOMDocument') && !empty($stringtoclean)) {
7015  $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
7016 
7017  $dom = new DOMDocument(null, 'UTF-8');
7018  $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
7019 
7020  if (is_object($dom)) {
7021  for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
7022  for ($attrs = $els->item($i)->attributes, $ii = $attrs->length - 1; $ii >= 0; $ii--) {
7023  //var_dump($attrs->item($ii));
7024  if (!empty($attrs->item($ii)->name)) {
7025  if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
7026  // Delete attribute if not into allowed_attributes
7027  $els->item($i)->removeAttribute($attrs->item($ii)->name);
7028  } elseif (in_array($attrs->item($ii)->name, array('style'))) {
7029  // If attribute is 'style'
7030  $valuetoclean = $attrs->item($ii)->value;
7031 
7032  if (isset($valuetoclean)) {
7033  do {
7034  $oldvaluetoclean = $valuetoclean;
7035  $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
7036  $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
7037  if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
7038  $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
7039  $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
7040  $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
7041  }
7042 
7043  // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
7044  $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
7045  $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
7046  } while ($oldvaluetoclean != $valuetoclean);
7047  }
7048 
7049  $attrs->item($ii)->value = $valuetoclean;
7050  }
7051  }
7052  }
7053  }
7054  }
7055 
7056  $return = $dom->saveHTML();
7057  //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
7058 
7059  $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
7060  $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
7061  $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
7062  return trim($return);
7063  } else {
7064  return $stringtoclean;
7065  }
7066 }
7067 
7079 function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
7080 {
7081  $temp = $stringtoclean;
7082  foreach ($disallowed_tags as $tagtoremove) {
7083  $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
7084  $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
7085  }
7086 
7087  if ($cleanalsosomestyles) {
7088  $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
7089  }
7090 
7091  return $temp;
7092 }
7093 
7094 
7104 function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
7105 {
7106  if ($nboflines == 1) {
7107  if (dol_textishtml($text)) {
7108  $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
7109  $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
7110  } else {
7111  $firstline = preg_replace('/[\n\r].*/', '', $text);
7112  }
7113  return $firstline.((strlen($firstline) != strlen($text)) ? '...' : '');
7114  } else {
7115  $ishtml = 0;
7116  if (dol_textishtml($text)) {
7117  $text = preg_replace('/\n/', '', $text);
7118  $ishtml = 1;
7119  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7120  } else {
7121  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7122  }
7123 
7124  $text = strtr($text, $repTable);
7125  if ($charset == 'UTF-8') {
7126  $pattern = '/(<br[^>]*>)/Uu';
7127  } else {
7128  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7129  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7130  }
7131  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7132 
7133  $firstline = '';
7134  $i = 0;
7135  $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
7136  while (($i < $nba) && ($i < ($nboflines * 2))) {
7137  if ($i % 2 == 0) {
7138  $firstline .= $a[$i];
7139  } elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) {
7140  $firstline .= ($ishtml ? "<br>\n" : "\n");
7141  }
7142  $i++;
7143  }
7144  unset($a);
7145  return $firstline.(($i < $nba) ? '...' : '');
7146  }
7147 }
7148 
7149 
7160 function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
7161 {
7162  if (is_null($stringtoencode)) {
7163  return '';
7164  }
7165 
7166  if (!$nl2brmode) {
7167  return nl2br($stringtoencode, $forxml);
7168  } else {
7169  $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
7170  return $ret;
7171  }
7172 }
7173 
7182 function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
7183 {
7184  global $conf;
7185 
7186  if (empty($nouseofiframesandbox) && !empty($conf->global->MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS)) {
7187  // TODO using sandbox on inline html content is not possible yet with current browsers
7188  //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
7189  //$s .= $stringtoencode;
7190  //$s .= '</body></html></iframe>';
7191  return $stringtoencode;
7192  } else {
7193  $out = $stringtoencode;
7194 
7195  do {
7196  $oldstringtoclean = $out;
7197 
7198  if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') {
7199  try {
7200  $dom = new DOMDocument;
7201  // Add a trick to solve pb with text without parent tag
7202  // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up without the trick into '<h1>Foo<p>bar</p></h1>'
7203  // like 'abc' that wrongly ends up without the tric into with '<p>abc</p>'
7204  $out = '<div class="tricktoremove">'.$out.'</div>';
7205 
7206  $dom->loadHTML($out, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
7207  $out = trim($dom->saveHTML());
7208 
7209  // Remove the trick added to solve pb with text without parent tag
7210  $out = preg_replace('/^<div class="tricktoremove">/', '', $out);
7211  $out = preg_replace('/<\/div>$/', '', $out);
7212  } catch (Exception $e) {
7213  // If error, invalid HTML string with no way to clean it
7214  //print $e->getMessage();
7215  $out = 'InvalidHTMLString';
7216  }
7217  }
7218 
7219  // Clean some html entities that are useless so text is cleaner
7220  $out = preg_replace('/&(tab|newline);/i', ' ', $out);
7221 
7222  // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are
7223  // encoded using text entities) so we can then exclude all numeric entities.
7224  $out = preg_replace('/&#39;/i', '&apos;', $out);
7225 
7226  // 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).
7227  // 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
7228  // using a non coventionnel way to be encoded, to not have them sanitized just after)
7229  $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', function ($m) {
7230  return realCharForNumericEntities($m); }, $out);
7231 
7232 
7233  // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
7234  $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'.
7235 
7236  // Keep only some html tags and remove also some 'javascript:' strings
7237  $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
7238 
7239  // We should also exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
7240  if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) {
7241  // 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.
7243  }
7244 
7245  // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
7246  $out = preg_replace('/&apos;/i', "&#39;", $out);
7247  } while ($oldstringtoclean != $out);
7248 
7249  // Check the limit of external links in a Rich text content. We count '<img' and 'url('
7250  $reg = array();
7251  preg_match_all('/(<img|url\‍()/i', $out, $reg);
7252  if (count($reg[0]) > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
7253  $out = 'TooManyLinksIntoHTMLString';
7254  }
7255 
7256  return $out;
7257  }
7258 }
7259 
7277 function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
7278 {
7279  if (is_null($stringtoencode)) {
7280  return '';
7281  }
7282 
7283  $newstring = $stringtoencode;
7284  if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
7285  $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.
7286  if ($removelasteolbr) {
7287  $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
7288  }
7289  $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__'));
7290  $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
7291  $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"'));
7292  } else {
7293  if ($removelasteolbr) {
7294  $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
7295  }
7296  $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
7297  }
7298  // Other substitutions that htmlentities does not do
7299  //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
7300  return $newstring;
7301 }
7302 
7310 function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
7311 {
7312  $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7313  $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
7314  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
7315  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
7316  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
7317  return $ret;
7318 }
7319 
7326 function dol_htmlcleanlastbr($stringtodecode)
7327 {
7328  $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
7329  $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
7330  return $ret;
7331 }
7332 
7342 function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
7343 {
7344  $newstring = $a;
7345  if ($keepsomeentities) {
7346  $newstring = strtr($newstring, array('&amp;'=>'__andamp__', '&lt;'=>'__andlt__', '&gt;'=>'__andgt__', '"'=>'__dquot__'));
7347  }
7348  $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
7349  if ($keepsomeentities) {
7350  $newstring = strtr($newstring, array('__andamp__'=>'&amp;', '__andlt__'=>'&lt;', '__andgt__'=>'&gt;', '__dquot__'=>'"'));
7351  }
7352  return $newstring;
7353 }
7354 
7365 function dol_htmlentities($string, $flags = ENT_QUOTES|ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
7366 {
7367  return htmlentities($string, $flags, $encoding, $double_encode);
7368 }
7369 
7379 function dol_string_is_good_iso($s, $clean = 0)
7380 {
7381  $len = dol_strlen($s);
7382  $out = '';
7383  $ok = 1;
7384  for ($scursor = 0; $scursor < $len; $scursor++) {
7385  $ordchar = ord($s[$scursor]);
7386  //print $scursor.'-'.$ordchar.'<br>';
7387  if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
7388  $ok = 0;
7389  break;
7390  } elseif ($ordchar > 126 && $ordchar < 160) {
7391  $ok = 0;
7392  break;
7393  } elseif ($clean) {
7394  $out .= $s[$scursor];
7395  }
7396  }
7397  if ($clean) {
7398  return $out;
7399  }
7400  return $ok;
7401 }
7402 
7411 function dol_nboflines($s, $maxchar = 0)
7412 {
7413  if ($s == '') {
7414  return 0;
7415  }
7416  $arraystring = explode("\n", $s);
7417  $nb = count($arraystring);
7418 
7419  return $nb;
7420 }
7421 
7422 
7432 function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
7433 {
7434  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7435  if (dol_textishtml($text)) {
7436  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7437  }
7438 
7439  $text = strtr($text, $repTable);
7440  if ($charset == 'UTF-8') {
7441  $pattern = '/(<br[^>]*>)/Uu';
7442  } else {
7443  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7444  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7445  }
7446  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7447 
7448  $nblines = (int) floor((count($a) + 1) / 2);
7449  // count possible auto line breaks
7450  if ($maxlinesize) {
7451  foreach ($a as $line) {
7452  if (dol_strlen($line) > $maxlinesize) {
7453  //$line_dec = html_entity_decode(strip_tags($line));
7454  $line_dec = html_entity_decode($line);
7455  if (dol_strlen($line_dec) > $maxlinesize) {
7456  $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
7457  $nblines += substr_count($line_dec, '\n');
7458  }
7459  }
7460  }
7461  }
7462 
7463  unset($a);
7464  return $nblines;
7465 }
7466 
7475 function dol_textishtml($msg, $option = 0)
7476 {
7477  if (is_null($msg)) {
7478  return false;
7479  }
7480 
7481  if ($option == 1) {
7482  if (preg_match('/<html/i', $msg)) {
7483  return true;
7484  } elseif (preg_match('/<body/i', $msg)) {
7485  return true;
7486  } elseif (preg_match('/<\/textarea/i', $msg)) {
7487  return true;
7488  } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
7489  return true;
7490  } elseif (preg_match('/<br/i', $msg)) {
7491  return true;
7492  }
7493  return false;
7494  } else {
7495  // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
7496  $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
7497  if (preg_match('/<html/i', $msg)) {
7498  return true;
7499  } elseif (preg_match('/<body/i', $msg)) {
7500  return true;
7501  } elseif (preg_match('/<\/textarea/i', $msg)) {
7502  return true;
7503  } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
7504  return true;
7505  } elseif (preg_match('/<br\/>/i', $msg)) {
7506  return true;
7507  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg)) {
7508  return true;
7509  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
7510  return true;
7511  } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
7512  return true; // must accept <img src="http://example.com/aaa.png" />
7513  } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
7514  return true; // must accept <a href="http://example.com/aaa.png" />
7515  } elseif (preg_match('/<h[0-9]>/i', $msg)) {
7516  return true;
7517  } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
7518  // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
7519  return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
7520  } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
7521  return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
7522  }
7523 
7524  return false;
7525  }
7526 }
7527 
7542 function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
7543 {
7544  if (!empty($invert)) {
7545  $tmp = $text1;
7546  $text1 = $text2;
7547  $text2 = $tmp;
7548  }
7549 
7550  $ret = '';
7551  $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
7552  $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
7553  $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
7554  return $ret;
7555 }
7556 
7557 
7558 
7569 function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
7570 {
7571  global $db, $conf, $mysoc, $user, $extrafields;
7572 
7573  $substitutionarray = array();
7574 
7575  if (empty($exclude) || !in_array('user', $exclude)) {
7576  // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
7577  // this will include signature content first and then replace var found into content of signature
7578  //var_dump($onlykey);
7579  $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()
7580  $usersignature = $user->signature;
7581  $substitutionarray = array_merge($substitutionarray, array(
7582  '__SENDEREMAIL_SIGNATURE__' => (string) ((empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
7583  '__USER_SIGNATURE__' => (string) (($usersignature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
7584  ));
7585 
7586  if (is_object($user)) {
7587  $substitutionarray = array_merge($substitutionarray, array(
7588  '__USER_ID__' => (string) $user->id,
7589  '__USER_LOGIN__' => (string) $user->login,
7590  '__USER_EMAIL__' => (string) $user->email,
7591  '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
7592  '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
7593  '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
7594  '__USER_FAX__' => (string) $user->office_fax,
7595  '__USER_LASTNAME__' => (string) $user->lastname,
7596  '__USER_FIRSTNAME__' => (string) $user->firstname,
7597  '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
7598  '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
7599  '__USER_JOB__' => (string) $user->job,
7600  '__USER_REMOTE_IP__' => (string) getUserRemoteIP()
7601  ));
7602  }
7603  }
7604  if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) {
7605  $substitutionarray = array_merge($substitutionarray, array(
7606  '__MYCOMPANY_NAME__' => $mysoc->name,
7607  '__MYCOMPANY_EMAIL__' => $mysoc->email,
7608  '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
7609  '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
7610  '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
7611  '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
7612  '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
7613  '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
7614  '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
7615  '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
7616  '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
7617  '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
7618  '__MYCOMPANY_ADDRESS__' => $mysoc->address,
7619  '__MYCOMPANY_ZIP__' => $mysoc->zip,
7620  '__MYCOMPANY_TOWN__' => $mysoc->town,
7621  '__MYCOMPANY_COUNTRY__' => $mysoc->country,
7622  '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
7623  '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
7624  '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
7625  ));
7626  }
7627 
7628  if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) {
7629  if ($onlykey) {
7630  $substitutionarray['__ID__'] = '__ID__';
7631  $substitutionarray['__REF__'] = '__REF__';
7632  $substitutionarray['__NEWREF__'] = '__NEWREF__';
7633  $substitutionarray['__LABEL__'] = '__LABEL__';
7634  $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
7635  $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
7636  $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
7637  $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
7638  $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
7639 
7640  if (isModEnabled("societe")) { // Most objects are concerned
7641  $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
7642  $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
7643  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
7644  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
7645  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
7646  $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
7647  $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
7648  $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
7649  $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
7650  $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
7651  $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
7652  $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
7653  $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
7654  $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
7655  $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
7656  $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
7657  $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
7658  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
7659  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
7660  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
7661  }
7662  if (isModEnabled('adherent') && (!is_object($object) || $object->element == 'adherent')) {
7663  $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
7664  $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
7665  $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
7666  $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
7667  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
7668  /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
7669  $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
7670  }
7671  // add variables subtitutions ticket
7672  if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket')) {
7673  $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
7674  $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
7675  $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
7676  $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
7677  $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
7678  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
7679  $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
7680  $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
7681  $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
7682  }
7683 
7684  if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature')) {
7685  $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
7686  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
7687  $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
7688  }
7689  if (isModEnabled('project')) { // Most objects
7690  $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
7691  $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
7692  $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
7693  /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
7694  $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
7695  }
7696  if (isModEnabled('contrat') && (!is_object($object) || $object->element == 'contract')) {
7697  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
7698  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
7699  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
7700  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
7701  }
7702  if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal')) {
7703  $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
7704  }
7705  $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
7706  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
7707  $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
7708  $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
7709  $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
7710  $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
7711  $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
7712 
7713  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
7714  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
7715  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
7716  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
7717  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
7718 
7719  if (isModEnabled("expedition") && (!is_object($object) || $object->element == 'shipping')) {
7720  $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
7721  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
7722  }
7723  if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
7724  $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment';
7725  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
7726  }
7727  } else {
7728  $substitutionarray['__ID__'] = $object->id;
7729  $substitutionarray['__REF__'] = $object->ref;
7730  $substitutionarray['__NEWREF__'] = $object->newref;
7731  $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
7732  $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7733  $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7734  $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
7735  $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
7736  if ($object->element == "shipping") {
7737  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', 0, $outputlangs) : '');
7738  } else {
7739  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7740  }
7741  $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%d") : '');
7742  $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%A") : '');
7743  $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%m") : '');
7744  $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%b") : '');
7745  $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%Y") : '');
7746  $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%H") : '');
7747  $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%M") : '');
7748  $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%S") : '');
7749 
7750  // For backward compatibility (deprecated)
7751  $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7752  $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7753  $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7754  $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 : '')) : '');
7755 
7756  if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
7757  $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
7758 
7759  $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
7760  if (method_exists($object, 'getCivilityLabel')) {
7761  $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
7762  }
7763  $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
7764  $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
7765  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
7766  if (method_exists($object, 'getFullName')) {
7767  $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
7768  }
7769  $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
7770  $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
7771  $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
7772  $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
7773  $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
7774  $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
7775  $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
7776  $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
7777  $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
7778  $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
7779  $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
7780  $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
7781  $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
7782  $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
7783  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
7784  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
7785  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
7786  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
7787  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
7788  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
7789  }
7790 
7791  if (is_object($object) && $object->element == 'societe') {
7792  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
7793  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
7794  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
7795  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
7796  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
7797  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
7798  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
7799  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
7800  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
7801  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
7802  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
7803  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
7804  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
7805  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
7806  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
7807  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
7808  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
7809  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
7810  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
7811  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
7812  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
7813  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
7814  } elseif (is_object($object->thirdparty)) {
7815  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
7816  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
7817  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
7818  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
7819  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
7820  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
7821  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
7822  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
7823  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
7824  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
7825  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
7826  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
7827  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
7828  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
7829  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
7830  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
7831  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
7832  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
7833  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
7834  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
7835  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
7836  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
7837  }
7838 
7839  if (is_object($object) && $object->element == 'recruitmentcandidature') {
7840  $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
7841  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
7842  $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
7843  }
7844  if (is_object($object) && $object->element == 'conferenceorboothattendee') {
7845  $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
7846  $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
7847  $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
7848  }
7849 
7850  $project = null;
7851  if (is_object($object->project)) {
7852  $project = $object->project;
7853  } elseif (is_object($object->projet)) { // Deprecated, for backward compatibility
7854  $project = $object->projet;
7855  }
7856  if ($project) {
7857  $substitutionarray['__PROJECT_ID__'] = $project->id;
7858  $substitutionarray['__PROJECT_REF__'] = $project->ref;
7859  $substitutionarray['__PROJECT_NAME__'] = $project->title;
7860  }
7861  if (is_object($object) && $object->element == 'project') {
7862  $substitutionarray['__PROJECT_NAME__'] = $object->title;
7863  }
7864 
7865  if (is_object($object) && $object->element == 'shipping') {
7866  $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
7867  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
7868  }
7869  if (is_object($object) && $object->element == 'reception') {
7870  $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
7871  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
7872  }
7873 
7874  if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
7875  $dateplannedstart = '';
7876  $datenextexpiration = '';
7877  foreach ($object->lines as $line) {
7878  if ($line->date_start > $dateplannedstart) {
7879  $dateplannedstart = $line->date_start;
7880  }
7881  if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
7882  $datenextexpiration = $line->date_end;
7883  }
7884  }
7885  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
7886  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
7887  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
7888  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
7889  }
7890  // add substition variable for ticket
7891  if (is_object($object) && $object->element == 'ticket') {
7892  $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
7893  $substitutionarray['__REF__'] = $object->ref;
7894  $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
7895  $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
7896  $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
7897  $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
7898  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
7899  $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
7900  $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
7901  $userstat = new User($db);
7902  if ($object->fk_user_assign > 0) {
7903  $userstat->fetch($object->fk_user_assign);
7904  $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7905  }
7906 
7907  if ($object->fk_user_create > 0) {
7908  $userstat->fetch($object->fk_user_create);
7909  $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7910  }
7911  }
7912 
7913  // Create dynamic tags for __EXTRAFIELD_FIELD__
7914  if ($object->table_element && $object->id > 0) {
7915  if (!is_object($extrafields)) {
7916  $extrafields = new ExtraFields($db);
7917  }
7918  $extrafields->fetch_name_optionals_label($object->table_element, true);
7919 
7920  if ($object->fetch_optionals() > 0) {
7921  if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
7922  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
7923  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7924  if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
7925  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
7926  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
7927  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
7928  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
7929  $datetime = $object->array_options['options_'.$key];
7930  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
7931  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
7932  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
7933  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
7934  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
7935  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
7936  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
7937  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7938  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]);
7939  }
7940  }
7941  }
7942  }
7943  }
7944 
7945  // Complete substitution array with the url to make online payment
7946  $paymenturl = '';
7947  if (empty($substitutionarray['__REF__'])) {
7948  $paymenturl = '';
7949  } else {
7950  // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
7951  require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7952  $outputlangs->loadLangs(array('paypal', 'other'));
7953 
7954  $amounttouse = 0;
7955  $typeforonlinepayment = 'free';
7956  if (is_object($object) && $object->element == 'commande') {
7957  $typeforonlinepayment = 'order';
7958  }
7959  if (is_object($object) && $object->element == 'facture') {
7960  $typeforonlinepayment = 'invoice';
7961  }
7962  if (is_object($object) && $object->element == 'member') {
7963  $typeforonlinepayment = 'member';
7964  if (!empty($object->last_subscription_amount)) {
7965  $amounttouse = $object->last_subscription_amount;
7966  }
7967  }
7968  if (is_object($object) && $object->element == 'contrat') {
7969  $typeforonlinepayment = 'contract';
7970  }
7971  if (is_object($object) && $object->element == 'fichinter') {
7972  $typeforonlinepayment = 'ficheinter';
7973  }
7974 
7975  $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
7976  $paymenturl = $url;
7977  }
7978 
7979  if ($object->id > 0) {
7980  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
7981  $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
7982 
7983  if (is_object($object) && $object->element == 'propal') {
7984  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
7985  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
7986  }
7987  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
7988  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
7989  } else {
7990  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
7991  }
7992  if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') {
7993  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
7994  } else {
7995  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
7996  }
7997  if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') {
7998  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
7999  } else {
8000  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
8001  }
8002  if (!empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'contrat') {
8003  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
8004  } else {
8005  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
8006  }
8007  if (!empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'fichinter') {
8008  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
8009  } else {
8010  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
8011  }
8012  if (!empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'supplier_proposal') {
8013  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
8014  } else {
8015  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
8016  }
8017 
8018  if (is_object($object) && $object->element == 'propal') {
8019  $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
8020  }
8021  if (is_object($object) && $object->element == 'commande') {
8022  $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
8023  }
8024  if (is_object($object) && $object->element == 'facture') {
8025  $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
8026  }
8027  if (is_object($object) && $object->element == 'contrat') {
8028  $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
8029  }
8030  if (is_object($object) && $object->element == 'fichinter') {
8031  $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
8032  }
8033  if (is_object($object) && $object->element == 'supplier_proposal') {
8034  $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
8035  }
8036  if (is_object($object) && $object->element == 'shipping') {
8037  $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
8038  }
8039  }
8040 
8041  if (is_object($object) && $object->element == 'action') {
8042  $substitutionarray['__EVENT_LABEL__'] = $object->label;
8043  $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y');
8044  $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S');
8045  }
8046  }
8047  }
8048  if (empty($exclude) || !in_array('objectamount', $exclude)) {
8049  include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
8050 
8051  $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
8052  $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
8053 
8054  $already_payed_all = 0;
8055  if (is_object($object) && ($object instanceof Facture)) {
8056  $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
8057  }
8058 
8059  $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
8060 
8061  $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
8062  $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
8063  $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
8064 
8065  $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
8066 
8067  $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8068  $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)) : '';
8069  $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)) : '';
8070 
8071  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8072  $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
8073  }
8074  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8075  $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
8076  }
8077 
8078  // Amount keys formated in a currency
8079  $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8080  $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8081  $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) : '';
8082  $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)) : '';
8083  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8084  $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8085  }
8086  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8087  $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8088  }
8089 
8090  $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
8091  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
8092  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
8093  // TODO Add other keys for foreign multicurrency
8094 
8095  // For backward compatibility
8096  if ($onlykey != 2) {
8097  $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
8098  $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
8099  $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8100  }
8101  }
8102 
8103  //var_dump($substitutionarray['__AMOUNT_FORMATED__']);
8104  if (empty($exclude) || !in_array('date', $exclude)) {
8105  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8106 
8107  $now = dol_now();
8108 
8109  $tmp = dol_getdate($now, true);
8110  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8111  $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
8112  $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8113  $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
8114 
8115  $daytext = $outputlangs->trans('Day'.$tmp['wday']);
8116 
8117  $substitutionarray = array_merge($substitutionarray, array(
8118  '__NOW_TMS__' => (int) $now,
8119  '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 0, $outputlangs),
8120  '__DAY__' => (string) $tmp['mday'],
8121  '__DAY_TEXT__' => $daytext, // Monday
8122  '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
8123  '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
8124  '__MONTH__' => (string) $tmp['mon'],
8125  '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
8126  '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
8127  '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
8128  '__YEAR__' => (string) $tmp['year'],
8129  '__PREVIOUS_DAY__' => (string) $tmp2['day'],
8130  '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
8131  '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
8132  '__NEXT_DAY__' => (string) $tmp4['day'],
8133  '__NEXT_MONTH__' => (string) $tmp5['month'],
8134  '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
8135  ));
8136  }
8137 
8138  if (isModEnabled('multicompany')) {
8139  $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
8140  }
8141  if (empty($exclude) || !in_array('system', $exclude)) {
8142  $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
8143  $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
8144  $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
8145  $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
8146  }
8147 
8148  return $substitutionarray;
8149 }
8150 
8167 function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
8168 {
8169  global $conf, $langs;
8170 
8171  if (!is_array($substitutionarray)) {
8172  return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
8173  }
8174 
8175  if (empty($outputlangs)) {
8176  $outputlangs = $langs;
8177  }
8178 
8179  // Is initial text HTML or simple text ?
8180  $msgishtml = 0;
8181  if (dol_textishtml($text, 1)) {
8182  $msgishtml = 1;
8183  }
8184 
8185  // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
8186  if (is_object($outputlangs)) {
8187  $reg = array();
8188  while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
8189  // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
8190  $tmp = explode('|', $reg[1]);
8191  if (!empty($tmp[1])) {
8192  $outputlangs->load($tmp[1]);
8193  }
8194 
8195  $value = $outputlangs->transnoentitiesnoconv($reg[1]);
8196 
8197  if (empty($converttextinhtmlifnecessary)) {
8198  // convert $newval into HTML is necessary
8199  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8200  } else {
8201  if (! $msgishtml) {
8202  $valueishtml = dol_textishtml($value, 1);
8203  //var_dump("valueishtml=".$valueishtml);
8204 
8205  if ($valueishtml) {
8206  $text = dol_htmlentitiesbr($text);
8207  $msgishtml = 1;
8208  }
8209  } else {
8210  $value = dol_nl2br("$value");
8211  }
8212 
8213  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
8214  }
8215  }
8216  }
8217 
8218  // Make substitution for constant keys.
8219  // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
8220  $reg = array();
8221  while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
8222  $keyfound = $reg[1];
8223  if (isASecretKey($keyfound)) {
8224  $value = '*****forbidden*****';
8225  } else {
8226  $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
8227  }
8228 
8229  if (empty($converttextinhtmlifnecessary)) {
8230  // convert $newval into HTML is necessary
8231  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8232  } else {
8233  if (! $msgishtml) {
8234  $valueishtml = dol_textishtml($value, 1);
8235 
8236  if ($valueishtml) {
8237  $text = dol_htmlentitiesbr($text);
8238  $msgishtml = 1;
8239  }
8240  } else {
8241  $value = dol_nl2br("$value");
8242  }
8243 
8244  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
8245  }
8246  }
8247 
8248  // Make substitution for array $substitutionarray
8249  foreach ($substitutionarray as $key => $value) {
8250  if (!isset($value)) {
8251  continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
8252  }
8253 
8254  if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) {
8255  $value = ''; // Protection
8256  }
8257 
8258  if (empty($converttextinhtmlifnecessary)) {
8259  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8260  } else {
8261  if (! $msgishtml) {
8262  $valueishtml = dol_textishtml($value, 1);
8263 
8264  if ($valueishtml) {
8265  $text = dol_htmlentitiesbr($text);
8266  $msgishtml = 1;
8267  }
8268  } else {
8269  $value = dol_nl2br("$value");
8270  }
8271  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8272  }
8273  }
8274 
8275  return $text;
8276 }
8277 
8290 function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
8291 {
8292  global $conf, $user;
8293 
8294  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8295 
8296  // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
8297 
8298  // Check if there is external substitution to do, requested by plugins
8299  $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
8300 
8301  foreach ($dirsubstitutions as $reldir) {
8302  $dir = dol_buildpath($reldir, 0);
8303 
8304  // Check if directory exists
8305  if (!dol_is_dir($dir)) {
8306  continue;
8307  }
8308 
8309  $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
8310  foreach ($substitfiles as $substitfile) {
8311  $reg = array();
8312  if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
8313  $module = $reg[1];
8314 
8315  dol_syslog("Library ".$substitfile['name']." found into ".$dir);
8316  // Include the user's functions file
8317  require_once $dir.$substitfile['name'];
8318  // Call the user's function, and only if it is defined
8319  $function_name = $module."_".$callfunc;
8320  if (function_exists($function_name)) {
8321  $function_name($substitutionarray, $outputlangs, $object, $parameters);
8322  }
8323  }
8324  }
8325  }
8326  if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) {
8327  // to list all tags in odt template
8328  $tags = '';
8329  foreach ($substitutionarray as $key => $value) {
8330  $tags .= '{'.$key.'} => '.$value."\n";
8331  }
8332  $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
8333  }
8334 }
8335 
8345 function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
8346 {
8347  print get_date_range($date_start, $date_end, $format, $outputlangs);
8348 }
8349 
8360 function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
8361 {
8362  global $langs;
8363 
8364  $out = '';
8365 
8366  if (!is_object($outputlangs)) {
8367  $outputlangs = $langs;
8368  }
8369 
8370  if ($date_start && $date_end) {
8371  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8372  }
8373  if ($date_start && !$date_end) {
8374  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8375  }
8376  if (!$date_start && $date_end) {
8377  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8378  }
8379 
8380  return $out;
8381 }
8382 
8391 function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
8392 {
8393  global $conf;
8394 
8395  $ret = '';
8396  // If order not defined, we use the setup
8397  if ($nameorder < 0) {
8398  $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
8399  }
8400  if ($nameorder == 1) {
8401  $ret .= $firstname;
8402  if ($firstname && $lastname) {
8403  $ret .= ' ';
8404  }
8405  $ret .= $lastname;
8406  } elseif ($nameorder == 2 || $nameorder == 3) {
8407  $ret .= $firstname;
8408  if (empty($ret) && $nameorder == 3) {
8409  $ret .= $lastname;
8410  }
8411  } else { // 0, 4 or 5
8412  $ret .= $lastname;
8413  if (empty($ret) && $nameorder == 5) {
8414  $ret .= $firstname;
8415  }
8416  if ($nameorder == 0) {
8417  if ($firstname && $lastname) {
8418  $ret .= ' ';
8419  }
8420  $ret .= $firstname;
8421  }
8422  }
8423  return $ret;
8424 }
8425 
8426 
8437 function setEventMessage($mesgs, $style = 'mesgs')
8438 {
8439  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
8440  if (!is_array($mesgs)) {
8441  // If mesgs is a string
8442  if ($mesgs) {
8443  $_SESSION['dol_events'][$style][] = $mesgs;
8444  }
8445  } else {
8446  // If mesgs is an array
8447  foreach ($mesgs as $mesg) {
8448  if ($mesg) {
8449  $_SESSION['dol_events'][$style][] = $mesg;
8450  }
8451  }
8452  }
8453 }
8454 
8466 function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '')
8467 {
8468  if (empty($mesg) && empty($mesgs)) {
8469  dol_syslog("Try to add a message in stack with empty message", LOG_WARNING);
8470  } else {
8471  if ($messagekey) {
8472  // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
8473  // TODO
8474  $mesg .= '';
8475  }
8476  if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
8477  if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
8478  dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages');
8479  }
8480  if (empty($mesgs)) {
8481  setEventMessage($mesg, $style);
8482  } else {
8483  if (!empty($mesg) && !in_array($mesg, $mesgs)) {
8484  setEventMessage($mesg, $style); // Add message string if not already into array
8485  }
8486  setEventMessage($mesgs, $style);
8487  }
8488  }
8489  }
8490 }
8491 
8501 function dol_htmloutput_events($disabledoutputofmessages = 0)
8502 {
8503  // Show mesgs
8504  if (isset($_SESSION['dol_events']['mesgs'])) {
8505  if (empty($disabledoutputofmessages)) {
8506  dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
8507  }
8508  unset($_SESSION['dol_events']['mesgs']);
8509  }
8510  // Show errors
8511  if (isset($_SESSION['dol_events']['errors'])) {
8512  if (empty($disabledoutputofmessages)) {
8513  dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
8514  }
8515  unset($_SESSION['dol_events']['errors']);
8516  }
8517 
8518  // Show warnings
8519  if (isset($_SESSION['dol_events']['warnings'])) {
8520  if (empty($disabledoutputofmessages)) {
8521  dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
8522  }
8523  unset($_SESSION['dol_events']['warnings']);
8524  }
8525 }
8526 
8541 function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
8542 {
8543  global $conf, $langs;
8544 
8545  $ret = 0;
8546  $return = '';
8547  $out = '';
8548  $divstart = $divend = '';
8549 
8550  // If inline message with no format, we add it.
8551  if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
8552  $divstart = '<div class="'.$style.' clearboth">';
8553  $divend = '</div>';
8554  }
8555 
8556  if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
8557  $langs->load("errors");
8558  $out .= $divstart;
8559  if (is_array($mesgarray) && count($mesgarray)) {
8560  foreach ($mesgarray as $message) {
8561  $ret++;
8562  $out .= $langs->trans($message);
8563  if ($ret < count($mesgarray)) {
8564  $out .= "<br>\n";
8565  }
8566  }
8567  }
8568  if ($mesgstring) {
8569  $ret++;
8570  $out .= $langs->trans($mesgstring);
8571  }
8572  $out .= $divend;
8573  }
8574 
8575  if ($out) {
8576  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) {
8577  $return = '<script>
8578  $(document).ready(function() {
8579  var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").'
8580  if (block) {
8581  $.dolEventValid("","'.dol_escape_js($out).'");
8582  } else {
8583  /* jnotify(message, preset of message type, keepmessage) */
8584  $.jnotify("'.dol_escape_js($out).'",
8585  "'.($style == "ok" ? 3000 : $style).'",
8586  '.($style == "ok" ? "false" : "true").',
8587  { remove: function (){} } );
8588  }
8589  });
8590  </script>';
8591  } else {
8592  $return = $out;
8593  }
8594  }
8595 
8596  return $return;
8597 }
8598 
8610 function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8611 {
8612  return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8613 }
8614 
8628 function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
8629 {
8630  if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
8631  return;
8632  }
8633 
8634  $iserror = 0;
8635  $iswarning = 0;
8636  if (is_array($mesgarray)) {
8637  foreach ($mesgarray as $val) {
8638  if ($val && preg_match('/class="error"/i', $val)) {
8639  $iserror++;
8640  break;
8641  }
8642  if ($val && preg_match('/class="warning"/i', $val)) {
8643  $iswarning++;
8644  break;
8645  }
8646  }
8647  } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
8648  $iserror++;
8649  } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
8650  $iswarning++;
8651  }
8652  if ($style == 'error') {
8653  $iserror++;
8654  }
8655  if ($style == 'warning') {
8656  $iswarning++;
8657  }
8658 
8659  if ($iserror || $iswarning) {
8660  // Remove div from texts
8661  $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
8662  $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
8663  $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
8664  // Remove div from texts array
8665  if (is_array($mesgarray)) {
8666  $newmesgarray = array();
8667  foreach ($mesgarray as $val) {
8668  if (is_string($val)) {
8669  $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
8670  $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
8671  $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
8672  $newmesgarray[] = $tmpmesgstring;
8673  } else {
8674  dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
8675  }
8676  }
8677  $mesgarray = $newmesgarray;
8678  }
8679  print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
8680  } else {
8681  print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
8682  }
8683 }
8684 
8696 function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8697 {
8698  dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8699 }
8700 
8714 function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
8715 {
8716  // Clean parameters
8717  $order = strtolower($order);
8718 
8719  if (is_array($array)) {
8720  $sizearray = count($array);
8721  if ($sizearray > 0) {
8722  $temp = array();
8723  foreach (array_keys($array) as $key) {
8724  if (is_object($array[$key])) {
8725  $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
8726  } else {
8727  $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
8728  }
8729  }
8730 
8731  if (!$natsort) {
8732  if ($order == 'asc') {
8733  asort($temp);
8734  } else {
8735  arsort($temp);
8736  }
8737  } else {
8738  if ($case_sensitive) {
8739  natsort($temp);
8740  } else {
8741  natcasesort($temp); // natecasesort is not sensible to case
8742  }
8743  if ($order != 'asc') {
8744  $temp = array_reverse($temp, true);
8745  }
8746  }
8747 
8748  $sorted = array();
8749 
8750  foreach (array_keys($temp) as $key) {
8751  (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
8752  }
8753 
8754  return $sorted;
8755  }
8756  }
8757  return $array;
8758 }
8759 
8760 
8767 function utf8_check($str)
8768 {
8769  $str = (string) $str; // Sometimes string is an int.
8770 
8771  // We must use here a binary strlen function (so not dol_strlen)
8772  $strLength = dol_strlen($str);
8773  for ($i = 0; $i < $strLength; $i++) {
8774  if (ord($str[$i]) < 0x80) {
8775  continue; // 0bbbbbbb
8776  } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
8777  $n = 1; // 110bbbbb
8778  } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
8779  $n = 2; // 1110bbbb
8780  } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
8781  $n = 3; // 11110bbb
8782  } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
8783  $n = 4; // 111110bb
8784  } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
8785  $n = 5; // 1111110b
8786  } else {
8787  return false; // Does not match any model
8788  }
8789  for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
8790  if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
8791  return false;
8792  }
8793  }
8794  }
8795  return true;
8796 }
8797 
8804 function utf8_valid($str)
8805 {
8806  /* 2 other methods to test if string is utf8
8807  $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
8808  $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
8809  */
8810  return preg_match('//u', $str) ? true : false;
8811 }
8812 
8813 
8820 function ascii_check($str)
8821 {
8822  if (function_exists('mb_check_encoding')) {
8823  //if (mb_detect_encoding($str, 'ASCII', true) return false;
8824  if (!mb_check_encoding($str, 'ASCII')) {
8825  return false;
8826  }
8827  } else {
8828  if (preg_match('/[^\x00-\x7f]/', $str)) {
8829  return false; // Contains a byte > 7f
8830  }
8831  }
8832 
8833  return true;
8834 }
8835 
8836 
8844 function dol_osencode($str)
8845 {
8846  global $conf;
8847 
8848  $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
8849  if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
8850  $tmp = 'iso-8859-1'; // By default for windows
8851  }
8852  if (empty($tmp)) {
8853  $tmp = 'utf-8'; // By default for other
8854  }
8855  if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) {
8856  $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING;
8857  }
8858 
8859  if ($tmp == 'iso-8859-1') {
8860  return utf8_decode($str);
8861  }
8862  return $str;
8863 }
8864 
8865 
8880 function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
8881 {
8882  global $cache_codes;
8883 
8884  // If key empty
8885  if ($key == '') {
8886  return '';
8887  }
8888 
8889  // Check in cache
8890  if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
8891  return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
8892  }
8893 
8894  dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
8895 
8896  $sql = "SELECT ".$fieldid." as valuetoget";
8897  $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
8898  $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
8899  if (!empty($entityfilter)) {
8900  $sql .= " AND entity IN (".getEntity($tablename).")";
8901  }
8902  if ($filters) {
8903  $sql .= $filters;
8904  }
8905 
8906  $resql = $db->query($sql);
8907  if ($resql) {
8908  $obj = $db->fetch_object($resql);
8909  if ($obj) {
8910  $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
8911  } else {
8912  $cache_codes[$tablename][$key][$fieldid] = '';
8913  }
8914  $db->free($resql);
8915  return $cache_codes[$tablename][$key][$fieldid];
8916  } else {
8917  return -1;
8918  }
8919 }
8920 
8927 function verifCond($strToEvaluate)
8928 {
8929  global $user, $conf, $langs;
8930  global $leftmenu;
8931  global $rights; // To export to dol_eval function
8932 
8933  //print $strToEvaluate."<br>\n";
8934  $rights = true;
8935  if (isset($strToEvaluate) && $strToEvaluate !== '') {
8936  //var_dump($strToEvaluate);
8937  $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
8938  $rights = $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
8939  //var_dump($rights);
8940  }
8941  return $rights;
8942 }
8943 
8954 function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
8955 {
8956  // Only global variables can be changed by eval function and returned to caller
8957  global $db, $langs, $user, $conf, $website, $websitepage;
8958  global $action, $mainmenu, $leftmenu;
8959  global $mysoc;
8960  global $objectoffield;
8961 
8962  // Old variables used
8963  global $rights;
8964  global $object;
8965  global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
8966  global $soc; // For backward compatibility
8967 
8968  try {
8969  // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
8970  if ($onlysimplestring == '1') {
8971  // We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
8972  // We must accept: '$conf->barcode->enabled || preg_match(\'/^AAA/\',$leftmenu)'
8973  // We must accept: '$user->rights->cabinetmed->read && !$object->canvas=="patient@cabinetmed"'
8974  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@', '/').']/i', $s)) {
8975  if ($returnvalue) {
8976  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8977  } else {
8978  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8979  return '';
8980  }
8981  // TODO
8982  // We can exclude all parenthesis ( that are not '($db' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match(' and 'isModEnabled('
8983  // ...
8984  }
8985  } elseif ($onlysimplestring == '2') {
8986  // 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"
8987  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@;[]', '/').']/i', $s)) {
8988  if ($returnvalue) {
8989  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8990  } else {
8991  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8992  return '';
8993  }
8994  }
8995  }
8996  if (is_array($s) || $s === 'Array') {
8997  return 'Bad string syntax to evaluate (value is Array) '.var_export($s, true);
8998  }
8999  if (strpos($s, '::') !== false) {
9000  if ($returnvalue) {
9001  return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
9002  } else {
9003  dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s);
9004  return '';
9005  }
9006  }
9007  if (strpos($s, '`') !== false) {
9008  if ($returnvalue) {
9009  return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
9010  } else {
9011  dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s);
9012  return '';
9013  }
9014  }
9015  if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
9016  if ($returnvalue) {
9017  return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
9018  } else {
9019  dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s);
9020  return '';
9021  }
9022  }
9023 
9024  // We block use of php exec or php file functions
9025  $forbiddenphpstrings = array('$$');
9026  $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST'));
9027 
9028  $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen");
9029  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
9030  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64_decode", "rawurldecode", "urldecode")); // decode string functions
9031  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
9032  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
9033  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
9034  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
9035 
9036  $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
9037 
9038  do {
9039  $oldstringtoclean = $s;
9040  $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
9041  $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
9042  //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
9043  } while ($oldstringtoclean != $s);
9044 
9045  if (strpos($s, '__forbiddenstring__') !== false) {
9046  dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
9047  if ($returnvalue) {
9048  return 'Bad string syntax to evaluate: '.$s;
9049  } else {
9050  dol_syslog('Bad string syntax to evaluate: '.$s);
9051  return '';
9052  }
9053  }
9054 
9055  //print $s."<br>\n";
9056  if ($returnvalue) {
9057  if ($hideerrors) {
9058  return @eval('return '.$s.';');
9059  } else {
9060  return eval('return '.$s.';');
9061  }
9062  } else {
9063  if ($hideerrors) {
9064  @eval($s);
9065  } else {
9066  eval($s);
9067  }
9068  }
9069  } catch (Error $e) {
9070  $error = 'dol_eval try/catch error : ';
9071  $error .= $e->getMessage();
9072  dol_syslog($error);
9073  }
9074  if ($returnvalue) {
9075  return '';
9076  }
9077 }
9078 
9085 function dol_validElement($element)
9086 {
9087  return (trim($element) != '');
9088 }
9089 
9098 function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
9099 {
9100  if (empty($codelang)) {
9101  return '';
9102  }
9103 
9104  if ($codelang == 'auto') {
9105  return '<span class="fa fa-language"></span>';
9106  }
9107 
9108  $langtocountryflag = array(
9109  'ar_AR' => '',
9110  'ca_ES' => 'catalonia',
9111  'da_DA' => 'dk',
9112  'fr_CA' => 'mq',
9113  'sv_SV' => 'se',
9114  'sw_SW' => 'unknown',
9115  'AQ' => 'unknown',
9116  'CW' => 'unknown',
9117  'IM' => 'unknown',
9118  'JE' => 'unknown',
9119  'MF' => 'unknown',
9120  'BL' => 'unknown',
9121  'SX' => 'unknown'
9122  );
9123 
9124  if (isset($langtocountryflag[$codelang])) {
9125  $flagImage = $langtocountryflag[$codelang];
9126  } else {
9127  $tmparray = explode('_', $codelang);
9128  $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
9129  }
9130 
9131  return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
9132 }
9133 
9141 function getLanguageCodeFromCountryCode($countrycode)
9142 {
9143  global $mysoc;
9144 
9145  if (empty($countrycode)) {
9146  return null;
9147  }
9148 
9149  if (strtoupper($countrycode) == 'MQ') {
9150  return 'fr_CA';
9151  }
9152  if (strtoupper($countrycode) == 'SE') {
9153  return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
9154  }
9155  if (strtoupper($countrycode) == 'CH') {
9156  if ($mysoc->country_code == 'FR') {
9157  return 'fr_CH';
9158  }
9159  if ($mysoc->country_code == 'DE') {
9160  return 'de_CH';
9161  }
9162  if ($mysoc->country_code == 'IT') {
9163  return 'it_CH';
9164  }
9165  }
9166 
9167  // Locale list taken from:
9168  // http://stackoverflow.com/questions/3191664/
9169  // list-of-all-locales-and-their-short-codes
9170  $locales = array(
9171  'af-ZA',
9172  'am-ET',
9173  'ar-AE',
9174  'ar-BH',
9175  'ar-DZ',
9176  'ar-EG',
9177  'ar-IQ',
9178  'ar-JO',
9179  'ar-KW',
9180  'ar-LB',
9181  'ar-LY',
9182  'ar-MA',
9183  'ar-OM',
9184  'ar-QA',
9185  'ar-SA',
9186  'ar-SY',
9187  'ar-TN',
9188  'ar-YE',
9189  //'as-IN', // Moved after en-IN
9190  'ba-RU',
9191  'be-BY',
9192  'bg-BG',
9193  'bn-BD',
9194  //'bn-IN', // Moved after en-IN
9195  'bo-CN',
9196  'br-FR',
9197  'ca-ES',
9198  'co-FR',
9199  'cs-CZ',
9200  'cy-GB',
9201  'da-DK',
9202  'de-AT',
9203  'de-CH',
9204  'de-DE',
9205  'de-LI',
9206  'de-LU',
9207  'dv-MV',
9208  'el-GR',
9209  'en-AU',
9210  'en-BZ',
9211  'en-CA',
9212  'en-GB',
9213  'en-IE',
9214  'en-IN',
9215  'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
9216  'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
9217  'en-JM',
9218  'en-MY',
9219  'en-NZ',
9220  'en-PH',
9221  'en-SG',
9222  'en-TT',
9223  'en-US',
9224  'en-ZA',
9225  'en-ZW',
9226  'es-AR',
9227  'es-BO',
9228  'es-CL',
9229  'es-CO',
9230  'es-CR',
9231  'es-DO',
9232  'es-EC',
9233  'es-ES',
9234  'es-GT',
9235  'es-HN',
9236  'es-MX',
9237  'es-NI',
9238  'es-PA',
9239  'es-PE',
9240  'es-PR',
9241  'es-PY',
9242  'es-SV',
9243  'es-US',
9244  'es-UY',
9245  'es-VE',
9246  'et-EE',
9247  'eu-ES',
9248  'fa-IR',
9249  'fi-FI',
9250  'fo-FO',
9251  'fr-BE',
9252  'fr-CA',
9253  'fr-CH',
9254  'fr-FR',
9255  'fr-LU',
9256  'fr-MC',
9257  'fy-NL',
9258  'ga-IE',
9259  'gd-GB',
9260  'gl-ES',
9261  'gu-IN',
9262  'he-IL',
9263  'hi-IN',
9264  'hr-BA',
9265  'hr-HR',
9266  'hu-HU',
9267  'hy-AM',
9268  'id-ID',
9269  'ig-NG',
9270  'ii-CN',
9271  'is-IS',
9272  'it-CH',
9273  'it-IT',
9274  'ja-JP',
9275  'ka-GE',
9276  'kk-KZ',
9277  'kl-GL',
9278  'km-KH',
9279  'kn-IN',
9280  'ko-KR',
9281  'ky-KG',
9282  'lb-LU',
9283  'lo-LA',
9284  'lt-LT',
9285  'lv-LV',
9286  'mi-NZ',
9287  'mk-MK',
9288  'ml-IN',
9289  'mn-MN',
9290  'mr-IN',
9291  'ms-BN',
9292  'ms-MY',
9293  'mt-MT',
9294  'nb-NO',
9295  'ne-NP',
9296  'nl-BE',
9297  'nl-NL',
9298  'nn-NO',
9299  'oc-FR',
9300  'or-IN',
9301  'pa-IN',
9302  'pl-PL',
9303  'ps-AF',
9304  'pt-BR',
9305  'pt-PT',
9306  'rm-CH',
9307  'ro-MD',
9308  'ro-RO',
9309  'ru-RU',
9310  'rw-RW',
9311  'sa-IN',
9312  'se-FI',
9313  'se-NO',
9314  'se-SE',
9315  'si-LK',
9316  'sk-SK',
9317  'sl-SI',
9318  'sq-AL',
9319  'sv-FI',
9320  'sv-SE',
9321  'sw-KE',
9322  'ta-IN',
9323  'te-IN',
9324  'th-TH',
9325  'tk-TM',
9326  'tn-ZA',
9327  'tr-TR',
9328  'tt-RU',
9329  'ug-CN',
9330  'uk-UA',
9331  'ur-PK',
9332  'vi-VN',
9333  'wo-SN',
9334  'xh-ZA',
9335  'yo-NG',
9336  'zh-CN',
9337  'zh-HK',
9338  'zh-MO',
9339  'zh-SG',
9340  'zh-TW',
9341  'zu-ZA',
9342  );
9343 
9344  $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
9345  if (in_array($buildprimarykeytotest, $locales)) {
9346  return strtolower($countrycode).'_'.strtoupper($countrycode);
9347  }
9348 
9349  if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
9350  foreach ($locales as $locale) {
9351  $locale_language = locale_get_primary_language($locale);
9352  $locale_region = locale_get_region($locale);
9353  if (strtoupper($countrycode) == $locale_region) {
9354  //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
9355  return strtolower($locale_language).'_'.strtoupper($locale_region);
9356  }
9357  }
9358  } else {
9359  dol_syslog("Warning Exention php-intl is not available", LOG_WARNING);
9360  }
9361 
9362  return null;
9363 }
9364 
9395 function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
9396 {
9397  global $hookmanager, $db;
9398 
9399  if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
9400  foreach ($conf->modules_parts['tabs'][$type] as $value) {
9401  $values = explode(':', $value);
9402 
9403  $reg = array();
9404  if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
9405  if (count($values) == 6) {
9406  // new declaration with permissions:
9407  // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9408  // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9409  if ($values[0] != $type) {
9410  continue;
9411  }
9412 
9413  if (verifCond($values[4])) {
9414  if ($values[3]) {
9415  if ($filterorigmodule) { // If a filter of module origin has been requested
9416  if (strpos($values[3], '@')) { // This is an external module
9417  if ($filterorigmodule != 'external') {
9418  continue;
9419  }
9420  } else { // This looks a core module
9421  if ($filterorigmodule != 'core') {
9422  continue;
9423  }
9424  }
9425  }
9426  $langs->load($values[3]);
9427  }
9428  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9429  // If label is "SUBSTITUION_..."
9430  $substitutionarray = array();
9431  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9432  $label = make_substitutions($reg[1], $substitutionarray);
9433  } else {
9434  // If label is "Label,Class,File,Method", we call the method to show content inside the badge
9435  $labeltemp = explode(',', $values[2]);
9436  $label = $langs->trans($labeltemp[0]);
9437 
9438  if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
9439  dol_include_once($labeltemp[2]);
9440  $classtoload = $labeltemp[1];
9441  if (class_exists($classtoload)) {
9442  $obj = new $classtoload($db);
9443  $function = $labeltemp[3];
9444  if ($obj && $function && method_exists($obj, $function)) {
9445  $nbrec = $obj->$function($object->id, $obj);
9446  $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
9447  }
9448  }
9449  }
9450  }
9451 
9452  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
9453  $head[$h][1] = $label;
9454  $head[$h][2] = str_replace('+', '', $values[1]);
9455  $h++;
9456  }
9457  } elseif (count($values) == 5) { // case deprecated
9458  dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
9459 
9460  if ($values[0] != $type) {
9461  continue;
9462  }
9463  if ($values[3]) {
9464  if ($filterorigmodule) { // If a filter of module origin has been requested
9465  if (strpos($values[3], '@')) { // This is an external module
9466  if ($filterorigmodule != 'external') {
9467  continue;
9468  }
9469  } else { // This looks a core module
9470  if ($filterorigmodule != 'core') {
9471  continue;
9472  }
9473  }
9474  }
9475  $langs->load($values[3]);
9476  }
9477  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9478  $substitutionarray = array();
9479  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9480  $label = make_substitutions($reg[1], $substitutionarray);
9481  } else {
9482  $label = $langs->trans($values[2]);
9483  }
9484 
9485  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
9486  $head[$h][1] = $label;
9487  $head[$h][2] = str_replace('+', '', $values[1]);
9488  $h++;
9489  }
9490  } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
9491  if ($values[0] != $type) {
9492  continue;
9493  }
9494  $tabname = str_replace('-', '', $values[1]);
9495  foreach ($head as $key => $val) {
9496  $condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
9497  //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
9498  if ($head[$key][2] == $tabname && $condition) {
9499  unset($head[$key]);
9500  break;
9501  }
9502  }
9503  }
9504  }
9505  }
9506 
9507  // No need to make a return $head. Var is modified as a reference
9508  if (!empty($hookmanager)) {
9509  $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
9510  $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters);
9511  if ($reshook > 0) { // Hook ask to replace completely the array
9512  $head = $hookmanager->resArray;
9513  } else { // Hook
9514  $head = array_merge($head, $hookmanager->resArray);
9515  }
9516  $h = count($head);
9517  }
9518 }
9519 
9531 function printCommonFooter($zone = 'private')
9532 {
9533  global $conf, $hookmanager, $user, $debugbar;
9534  global $action;
9535  global $micro_start_time;
9536 
9537  if ($zone == 'private') {
9538  print "\n".'<!-- Common footer for private page -->'."\n";
9539  } else {
9540  print "\n".'<!-- Common footer for public page -->'."\n";
9541  }
9542 
9543  // A div to store page_y POST parameter so we can read it using javascript
9544  print "\n<!-- A div to store page_y POST parameter -->\n";
9545  print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
9546 
9547  $parameters = array();
9548  $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
9549  if (empty($reshook)) {
9550  if (!empty($conf->global->MAIN_HTML_FOOTER)) {
9551  print $conf->global->MAIN_HTML_FOOTER."\n";
9552  }
9553 
9554  print "\n";
9555  if (!empty($conf->use_javascript_ajax)) {
9556  print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and madatory fields, tuning info, ... -->\n";
9557  print '<script>'."\n";
9558  print 'jQuery(document).ready(function() {'."\n";
9559 
9560  if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
9561  print "\n";
9562  print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
9563  print 'jQuery("li.menuhider").click(function(event) {';
9564  print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
9565  print ' console.log("We click on .menuhider");'."\n";
9566  print ' $("body").toggleClass("sidebar-collapse")'."\n";
9567  print '});'."\n";
9568  }
9569 
9570  // Management of focus and mandatory for fields
9571  if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"])))) {
9572  print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
9573  $relativepathstring = $_SERVER["PHP_SELF"];
9574  // Clean $relativepathstring
9575  if (constant('DOL_URL_ROOT')) {
9576  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
9577  }
9578  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
9579  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
9580  //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
9581  if (!empty($user->default_values[$relativepathstring]['focus'])) {
9582  foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
9583  $qualified = 0;
9584  if ($defkey != '_noquery_') {
9585  $tmpqueryarraytohave = explode('&', $defkey);
9586  $foundintru = 0;
9587  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9588  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9589  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9590  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9591  $foundintru = 1;
9592  }
9593  }
9594  if (!$foundintru) {
9595  $qualified = 1;
9596  }
9597  //var_dump($defkey.'-'.$qualified);
9598  } else {
9599  $qualified = 1;
9600  }
9601 
9602  if ($qualified) {
9603  foreach ($defval as $paramkey => $paramval) {
9604  // Set focus on field
9605  print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
9606  print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
9607  print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
9608  }
9609  }
9610  }
9611  }
9612  if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
9613  foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
9614  $qualified = 0;
9615  if ($defkey != '_noquery_') {
9616  $tmpqueryarraytohave = explode('&', $defkey);
9617  $foundintru = 0;
9618  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9619  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9620  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9621  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9622  $foundintru = 1;
9623  }
9624  }
9625  if (!$foundintru) {
9626  $qualified = 1;
9627  }
9628  //var_dump($defkey.'-'.$qualified);
9629  } else {
9630  $qualified = 1;
9631  }
9632 
9633  if ($qualified) {
9634  foreach ($defval as $paramkey => $paramval) {
9635  // Add property 'required' on input
9636  print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9637  print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9638  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";
9639  print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9640  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
9641  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
9642 
9643  // Add 'field required' class on closest td for all input elements : input, textarea and select
9644  print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
9645  }
9646  }
9647  }
9648  }
9649  }
9650 
9651  print '});'."\n";
9652 
9653  // End of tuning
9654  if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) {
9655  print "\n";
9656  print "/* JS CODE TO ENABLE to add memory info */\n";
9657  print 'window.console && console.log("';
9658  if (!empty($conf->global->MEMCACHED_SERVER)) {
9659  print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
9660  }
9661  print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off');
9662  if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
9663  $micro_end_time = microtime(true);
9664  print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
9665  }
9666 
9667  if (function_exists("memory_get_usage")) {
9668  print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
9669  }
9670  if (function_exists("memory_get_peak_usage")) {
9671  print ' - Real mem peak: '.memory_get_peak_usage(true);
9672  }
9673  if (function_exists("zend_loader_file_encoded")) {
9674  print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
9675  }
9676  print '");'."\n";
9677  }
9678 
9679  print "\n".'</script>'."\n";
9680 
9681  // Google Analytics
9682  // TODO Add a hook here
9683  if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID)) {
9684  $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID);
9685  foreach ($tmptagarray as $tmptag) {
9686  print "\n";
9687  print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
9688  print "
9689  <!-- Global site tag (gtag.js) - Google Analytics -->
9690  <script async src=\"https://www.googletagmanager.com/gtag/js?id=".trim($tmptag)."\"></script>
9691  <script>
9692  window.dataLayer = window.dataLayer || [];
9693  function gtag(){dataLayer.push(arguments);}
9694  gtag('js', new Date());
9695 
9696  gtag('config', '".trim($tmptag)."');
9697  </script>";
9698  print "\n";
9699  }
9700  }
9701  }
9702 
9703  // Add Xdebug coverage of code
9704  if (defined('XDEBUGCOVERAGE')) {
9705  print_r(xdebug_get_code_coverage());
9706  }
9707 
9708  // Add DebugBar data
9709  if (!empty($user->rights->debugbar->read) && is_object($debugbar)) {
9710  $debugbar['time']->stopMeasure('pageaftermaster');
9711  print '<!-- Output debugbar data -->'."\n";
9712  $renderer = $debugbar->getRenderer();
9713  print $debugbar->getRenderer()->render();
9714  } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
9715  print "\n";
9716  print "<!-- Start of log output\n";
9717  //print '<div class="hidden">'."\n";
9718  foreach ($conf->logbuffer as $logline) {
9719  print $logline."<br>\n";
9720  }
9721  //print '</div>'."\n";
9722  print "End of log output -->\n";
9723  }
9724  }
9725 }
9726 
9736 function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
9737 {
9738  if ($a = explode($delimiter, $string)) {
9739  $ka = array();
9740  foreach ($a as $s) { // each part
9741  if ($s) {
9742  if ($pos = strpos($s, $kv)) { // key/value delimiter
9743  $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
9744  } else { // key delimiter not found
9745  $ka[] = trim($s);
9746  }
9747  }
9748  }
9749  return $ka;
9750  }
9751  return array();
9752 }
9753 
9754 
9761 function dol_set_focus($selector)
9762 {
9763  print "\n".'<!-- Set focus onto a specific field -->'."\n";
9764  print '<script>jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
9765 }
9766 
9767 
9775 function dol_getmypid()
9776 {
9777  if (!function_exists('getmypid')) {
9778  return mt_rand(99900000, 99965535);
9779  } else {
9780  return getmypid(); // May be a number on 64 bits (depending on OS)
9781  }
9782 }
9783 
9784 
9802 function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
9803 {
9804  global $db, $langs;
9805 
9806  $value = trim($value);
9807 
9808  if ($mode == 0) {
9809  $value = preg_replace('/\*/', '%', $value); // Replace * with %
9810  }
9811  if ($mode == 1) {
9812  $value = preg_replace('/([!<>=]+)\s+([0-9'.preg_quote($langs->trans("DecimalSeparator"), '/').'\-])/', '\1\2', $value); // Clean string '< 10' into '<10' so we can then explode on space to get all tests to do
9813  }
9814 
9815  $value = preg_replace('/\s*\|\s*/', '|', $value);
9816 
9817  $crits = explode(' ', $value);
9818  $res = '';
9819  if (!is_array($fields)) {
9820  $fields = array($fields);
9821  }
9822 
9823  $i1 = 0; // count the nb of and criteria added (all fields / criterias)
9824  foreach ($crits as $crit) { // Loop on each AND criteria
9825  $crit = trim($crit);
9826  $i2 = 0; // count the nb of valid criteria added for this this first criteria
9827  $newres = '';
9828  foreach ($fields as $field) {
9829  if ($mode == 1) {
9830  $tmpcrits = explode('|', $crit);
9831  $i3 = 0; // count the nb of valid criteria added for this current field
9832  foreach ($tmpcrits as $tmpcrit) {
9833  if ($tmpcrit !== '0' && empty($tmpcrit)) {
9834  continue;
9835  }
9836  $tmpcrit = trim($tmpcrit);
9837 
9838  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
9839 
9840  $operator = '=';
9841  $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
9842 
9843  $reg = array();
9844  preg_match('/([!<>=]+)/', $tmpcrit, $reg);
9845  if (!empty($reg[1])) {
9846  $operator = $reg[1];
9847  }
9848  if ($newcrit != '') {
9849  $numnewcrit = price2num($newcrit);
9850  if (is_numeric($numnewcrit)) {
9851  $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
9852  } else {
9853  $newres .= '1 = 2'; // force false, we received a corrupted data
9854  }
9855  $i3++; // a criteria was added to string
9856  }
9857  }
9858  $i2++; // a criteria for 1 more field was added to string
9859  } elseif ($mode == 2 || $mode == -2) {
9860  $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
9861  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
9862  $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
9863  if ($mode == -2) {
9864  $newres .= ' OR '.$field.' IS NULL';
9865  }
9866  $i2++; // a criteria for 1 more field was added to string
9867  } elseif ($mode == 3 || $mode == -3) {
9868  $tmparray = explode(',', $crit);
9869  if (count($tmparray)) {
9870  $listofcodes = '';
9871  foreach ($tmparray as $val) {
9872  $val = trim($val);
9873  if ($val) {
9874  $listofcodes .= ($listofcodes ? ',' : '');
9875  $listofcodes .= "'".$db->escape($val)."'";
9876  }
9877  }
9878  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
9879  $i2++; // a criteria for 1 more field was added to string
9880  }
9881  if ($mode == -3) {
9882  $newres .= ' OR '.$field.' IS NULL';
9883  }
9884  } elseif ($mode == 4) {
9885  $tmparray = explode(',', $crit);
9886  if (count($tmparray)) {
9887  $listofcodes = '';
9888  foreach ($tmparray as $val) {
9889  $val = trim($val);
9890  if ($val) {
9891  $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
9892  $newres .= ' OR '.$field." = '".$db->escape($val)."'";
9893  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
9894  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
9895  $newres .= ')';
9896  $i2++; // a criteria for 1 more field was added to string (we can add several citeria for the same field as it is a multiselect search criteria)
9897  }
9898  }
9899  }
9900  } else { // $mode=0
9901  $tmpcrits = explode('|', $crit);
9902  $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
9903  foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
9904  if ($tmpcrit !== '0' && empty($tmpcrit)) {
9905  continue;
9906  }
9907  $tmpcrit = trim($tmpcrit);
9908 
9909  if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
9910  $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
9911  } else {
9912  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
9913  }
9914 
9915  if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
9916  $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
9917  } else {
9918  $tmpcrit2 = $tmpcrit;
9919  $tmpbefore = '%';
9920  $tmpafter = '%';
9921  $tmps = '';
9922 
9923  if (preg_match('/^!/', $tmpcrit)) {
9924  $tmps .= $field." NOT LIKE "; // ! as exclude character
9925  $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
9926  } else {
9927  $tmps .= $field." LIKE ";
9928  }
9929  $tmps .= "'";
9930 
9931  if (preg_match('/^[\^\$]/', $tmpcrit)) {
9932  $tmpbefore = '';
9933  $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
9934  }
9935  if (preg_match('/[\^\$]$/', $tmpcrit)) {
9936  $tmpafter = '';
9937  $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
9938  }
9939 
9940  if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
9941  $tmps = "(".$tmps;
9942  }
9943  $newres .= $tmps;
9944  $newres .= $tmpbefore;
9945  $newres .= $db->escape($tmpcrit2);
9946  $newres .= $tmpafter;
9947  $newres .= "'";
9948  if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
9949  $newres .= " OR ".$field." IS NULL)";
9950  }
9951  }
9952 
9953  $i3++;
9954  }
9955 
9956  $i2++; // a criteria for 1 more field was added to string
9957  }
9958  }
9959 
9960  if ($newres) {
9961  $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
9962  }
9963  $i1++;
9964  }
9965  $res = ($nofirstand ? "" : " AND ")."(".$res.")";
9966 
9967  return $res;
9968 }
9969 
9976 function showDirectDownloadLink($object)
9977 {
9978  global $conf, $langs;
9979 
9980  $out = '';
9981  $url = $object->getLastMainDocLink($object->element);
9982 
9983  $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
9984  if ($url) {
9985  $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
9986  $out .= ajax_autoselect("directdownloadlink", 0);
9987  } else {
9988  $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
9989  }
9990 
9991  return $out;
9992 }
9993 
10002 function getImageFileNameForSize($file, $extName, $extImgTarget = '')
10003 {
10004  $dirName = dirname($file);
10005  if ($dirName == '.') {
10006  $dirName = '';
10007  }
10008 
10009  $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
10010  $fileName = basename($fileName);
10011 
10012  if (empty($extImgTarget)) {
10013  $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
10014  }
10015  if (empty($extImgTarget)) {
10016  $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
10017  }
10018  if (empty($extImgTarget)) {
10019  $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
10020  }
10021  if (empty($extImgTarget)) {
10022  $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
10023  }
10024  if (empty($extImgTarget)) {
10025  $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
10026  }
10027  if (empty($extImgTarget)) {
10028  $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
10029  }
10030 
10031  if (!$extImgTarget) {
10032  return $file;
10033  }
10034 
10035  $subdir = '';
10036  if ($extName) {
10037  $subdir = 'thumbs/';
10038  }
10039 
10040  return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
10041 }
10042 
10043 
10053 function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
10054 {
10055  global $conf, $langs;
10056 
10057  if (empty($conf->use_javascript_ajax)) {
10058  return '';
10059  }
10060 
10061  $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
10062 
10063  if ($alldata == 1) {
10064  if ($isAllowedForPreview) {
10065  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));
10066  } else {
10067  return array();
10068  }
10069  }
10070 
10071  // old behavior, return a string
10072  if ($isAllowedForPreview) {
10073  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')).'\')';
10074  } else {
10075  return '';
10076  }
10077 }
10078 
10079 
10088 function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
10089 {
10090  global $langs;
10091  $out = '<script>
10092  jQuery(document).ready(function () {
10093  jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
10094  });
10095  </script>';
10096  if ($addlink) {
10097  if ($textonlink === 'image') {
10098  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
10099  } else {
10100  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
10101  }
10102  }
10103  return $out;
10104 }
10105 
10113 function dolIsAllowedForPreview($file)
10114 {
10115  global $conf;
10116 
10117  // Check .noexe extension in filename
10118  if (preg_match('/\.noexe$/i', $file)) {
10119  return 0;
10120  }
10121 
10122  // Check mime types
10123  $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
10124  if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) {
10125  $mime_preview[] = 'svg+xml';
10126  }
10127  //$mime_preview[]='vnd.oasis.opendocument.presentation';
10128  //$mime_preview[]='archive';
10129  $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
10130  if ($num_mime !== false) {
10131  return 1;
10132  }
10133 
10134  // By default, not allowed for preview
10135  return 0;
10136 }
10137 
10138 
10148 function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
10149 {
10150  $mime = $default;
10151  $imgmime = 'other.png';
10152  $famime = 'file-o';
10153  $srclang = '';
10154 
10155  $tmpfile = preg_replace('/\.noexe$/', '', $file);
10156 
10157  // Plain text files
10158  if (preg_match('/\.txt$/i', $tmpfile)) {
10159  $mime = 'text/plain';
10160  $imgmime = 'text.png';
10161  $famime = 'file-text-o';
10162  }
10163  if (preg_match('/\.rtx$/i', $tmpfile)) {
10164  $mime = 'text/richtext';
10165  $imgmime = 'text.png';
10166  $famime = 'file-text-o';
10167  }
10168  if (preg_match('/\.csv$/i', $tmpfile)) {
10169  $mime = 'text/csv';
10170  $imgmime = 'text.png';
10171  $famime = 'file-text-o';
10172  }
10173  if (preg_match('/\.tsv$/i', $tmpfile)) {
10174  $mime = 'text/tab-separated-values';
10175  $imgmime = 'text.png';
10176  $famime = 'file-text-o';
10177  }
10178  if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
10179  $mime = 'text/plain';
10180  $imgmime = 'text.png';
10181  $famime = 'file-text-o';
10182  }
10183  if (preg_match('/\.ini$/i', $tmpfile)) {
10184  $mime = 'text/plain';
10185  $imgmime = 'text.png';
10186  $srclang = 'ini';
10187  $famime = 'file-text-o';
10188  }
10189  if (preg_match('/\.md$/i', $tmpfile)) {
10190  $mime = 'text/plain';
10191  $imgmime = 'text.png';
10192  $srclang = 'md';
10193  $famime = 'file-text-o';
10194  }
10195  if (preg_match('/\.css$/i', $tmpfile)) {
10196  $mime = 'text/css';
10197  $imgmime = 'css.png';
10198  $srclang = 'css';
10199  $famime = 'file-text-o';
10200  }
10201  if (preg_match('/\.lang$/i', $tmpfile)) {
10202  $mime = 'text/plain';
10203  $imgmime = 'text.png';
10204  $srclang = 'lang';
10205  $famime = 'file-text-o';
10206  }
10207  // Certificate files
10208  if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) {
10209  $mime = 'text/plain';
10210  $imgmime = 'text.png';
10211  $famime = 'file-text-o';
10212  }
10213  // XML based (HTML/XML/XAML)
10214  if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) {
10215  $mime = 'text/html';
10216  $imgmime = 'html.png';
10217  $srclang = 'html';
10218  $famime = 'file-text-o';
10219  }
10220  if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
10221  $mime = 'text/xml';
10222  $imgmime = 'other.png';
10223  $srclang = 'xml';
10224  $famime = 'file-text-o';
10225  }
10226  if (preg_match('/\.xaml$/i', $tmpfile)) {
10227  $mime = 'text/xml';
10228  $imgmime = 'other.png';
10229  $srclang = 'xaml';
10230  $famime = 'file-text-o';
10231  }
10232  // Languages
10233  if (preg_match('/\.bas$/i', $tmpfile)) {
10234  $mime = 'text/plain';
10235  $imgmime = 'text.png';
10236  $srclang = 'bas';
10237  $famime = 'file-code-o';
10238  }
10239  if (preg_match('/\.(c)$/i', $tmpfile)) {
10240  $mime = 'text/plain';
10241  $imgmime = 'text.png';
10242  $srclang = 'c';
10243  $famime = 'file-code-o';
10244  }
10245  if (preg_match('/\.(cpp)$/i', $tmpfile)) {
10246  $mime = 'text/plain';
10247  $imgmime = 'text.png';
10248  $srclang = 'cpp';
10249  $famime = 'file-code-o';
10250  }
10251  if (preg_match('/\.cs$/i', $tmpfile)) {
10252  $mime = 'text/plain';
10253  $imgmime = 'text.png';
10254  $srclang = 'cs';
10255  $famime = 'file-code-o';
10256  }
10257  if (preg_match('/\.(h)$/i', $tmpfile)) {
10258  $mime = 'text/plain';
10259  $imgmime = 'text.png';
10260  $srclang = 'h';
10261  $famime = 'file-code-o';
10262  }
10263  if (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
10264  $mime = 'text/plain';
10265  $imgmime = 'text.png';
10266  $srclang = 'java';
10267  $famime = 'file-code-o';
10268  }
10269  if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
10270  $mime = 'text/plain';
10271  $imgmime = 'php.png';
10272  $srclang = 'php';
10273  $famime = 'file-code-o';
10274  }
10275  if (preg_match('/\.phtml$/i', $tmpfile)) {
10276  $mime = 'text/plain';
10277  $imgmime = 'php.png';
10278  $srclang = 'php';
10279  $famime = 'file-code-o';
10280  }
10281  if (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
10282  $mime = 'text/plain';
10283  $imgmime = 'pl.png';
10284  $srclang = 'perl';
10285  $famime = 'file-code-o';
10286  }
10287  if (preg_match('/\.sql$/i', $tmpfile)) {
10288  $mime = 'text/plain';
10289  $imgmime = 'text.png';
10290  $srclang = 'sql';
10291  $famime = 'file-code-o';
10292  }
10293  if (preg_match('/\.js$/i', $tmpfile)) {
10294  $mime = 'text/x-javascript';
10295  $imgmime = 'jscript.png';
10296  $srclang = 'js';
10297  $famime = 'file-code-o';
10298  }
10299  // Open office
10300  if (preg_match('/\.odp$/i', $tmpfile)) {
10301  $mime = 'application/vnd.oasis.opendocument.presentation';
10302  $imgmime = 'ooffice.png';
10303  $famime = 'file-powerpoint-o';
10304  }
10305  if (preg_match('/\.ods$/i', $tmpfile)) {
10306  $mime = 'application/vnd.oasis.opendocument.spreadsheet';
10307  $imgmime = 'ooffice.png';
10308  $famime = 'file-excel-o';
10309  }
10310  if (preg_match('/\.odt$/i', $tmpfile)) {
10311  $mime = 'application/vnd.oasis.opendocument.text';
10312  $imgmime = 'ooffice.png';
10313  $famime = 'file-word-o';
10314  }
10315  // MS Office
10316  if (preg_match('/\.mdb$/i', $tmpfile)) {
10317  $mime = 'application/msaccess';
10318  $imgmime = 'mdb.png';
10319  $famime = 'file-o';
10320  }
10321  if (preg_match('/\.doc(x|m)?$/i', $tmpfile)) {
10322  $mime = 'application/msword';
10323  $imgmime = 'doc.png';
10324  $famime = 'file-word-o';
10325  }
10326  if (preg_match('/\.dot(x|m)?$/i', $tmpfile)) {
10327  $mime = 'application/msword';
10328  $imgmime = 'doc.png';
10329  $famime = 'file-word-o';
10330  }
10331  if (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
10332  $mime = 'application/vnd.ms-excel';
10333  $imgmime = 'xls.png';
10334  $famime = 'file-excel-o';
10335  }
10336  if (preg_match('/\.xla(m)?$/i', $tmpfile)) {
10337  $mime = 'application/vnd.ms-excel';
10338  $imgmime = 'xls.png';
10339  $famime = 'file-excel-o';
10340  }
10341  if (preg_match('/\.xls$/i', $tmpfile)) {
10342  $mime = 'application/vnd.ms-excel';
10343  $imgmime = 'xls.png';
10344  $famime = 'file-excel-o';
10345  }
10346  if (preg_match('/\.xls(b|m|x)$/i', $tmpfile)) {
10347  $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
10348  $imgmime = 'xls.png';
10349  $famime = 'file-excel-o';
10350  }
10351  if (preg_match('/\.pps(m|x)?$/i', $tmpfile)) {
10352  $mime = 'application/vnd.ms-powerpoint';
10353  $imgmime = 'ppt.png';
10354  $famime = 'file-powerpoint-o';
10355  }
10356  if (preg_match('/\.ppt(m|x)?$/i', $tmpfile)) {
10357  $mime = 'application/x-mspowerpoint';
10358  $imgmime = 'ppt.png';
10359  $famime = 'file-powerpoint-o';
10360  }
10361  // Other
10362  if (preg_match('/\.pdf$/i', $tmpfile)) {
10363  $mime = 'application/pdf';
10364  $imgmime = 'pdf.png';
10365  $famime = 'file-pdf-o';
10366  }
10367  // Scripts
10368  if (preg_match('/\.bat$/i', $tmpfile)) {
10369  $mime = 'text/x-bat';
10370  $imgmime = 'script.png';
10371  $srclang = 'dos';
10372  $famime = 'file-code-o';
10373  }
10374  if (preg_match('/\.sh$/i', $tmpfile)) {
10375  $mime = 'text/x-sh';
10376  $imgmime = 'script.png';
10377  $srclang = 'bash';
10378  $famime = 'file-code-o';
10379  }
10380  if (preg_match('/\.ksh$/i', $tmpfile)) {
10381  $mime = 'text/x-ksh';
10382  $imgmime = 'script.png';
10383  $srclang = 'bash';
10384  $famime = 'file-code-o';
10385  }
10386  if (preg_match('/\.bash$/i', $tmpfile)) {
10387  $mime = 'text/x-bash';
10388  $imgmime = 'script.png';
10389  $srclang = 'bash';
10390  $famime = 'file-code-o';
10391  }
10392  // Images
10393  if (preg_match('/\.ico$/i', $tmpfile)) {
10394  $mime = 'image/x-icon';
10395  $imgmime = 'image.png';
10396  $famime = 'file-image-o';
10397  }
10398  if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
10399  $mime = 'image/jpeg';
10400  $imgmime = 'image.png';
10401  $famime = 'file-image-o';
10402  }
10403  if (preg_match('/\.png$/i', $tmpfile)) {
10404  $mime = 'image/png';
10405  $imgmime = 'image.png';
10406  $famime = 'file-image-o';
10407  }
10408  if (preg_match('/\.gif$/i', $tmpfile)) {
10409  $mime = 'image/gif';
10410  $imgmime = 'image.png';
10411  $famime = 'file-image-o';
10412  }
10413  if (preg_match('/\.bmp$/i', $tmpfile)) {
10414  $mime = 'image/bmp';
10415  $imgmime = 'image.png';
10416  $famime = 'file-image-o';
10417  }
10418  if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
10419  $mime = 'image/tiff';
10420  $imgmime = 'image.png';
10421  $famime = 'file-image-o';
10422  }
10423  if (preg_match('/\.svg$/i', $tmpfile)) {
10424  $mime = 'image/svg+xml';
10425  $imgmime = 'image.png';
10426  $famime = 'file-image-o';
10427  }
10428  if (preg_match('/\.webp$/i', $tmpfile)) {
10429  $mime = 'image/webp';
10430  $imgmime = 'image.png';
10431  $famime = 'file-image-o';
10432  }
10433  // Calendar
10434  if (preg_match('/\.vcs$/i', $tmpfile)) {
10435  $mime = 'text/calendar';
10436  $imgmime = 'other.png';
10437  $famime = 'file-text-o';
10438  }
10439  if (preg_match('/\.ics$/i', $tmpfile)) {
10440  $mime = 'text/calendar';
10441  $imgmime = 'other.png';
10442  $famime = 'file-text-o';
10443  }
10444  // Other
10445  if (preg_match('/\.torrent$/i', $tmpfile)) {
10446  $mime = 'application/x-bittorrent';
10447  $imgmime = 'other.png';
10448  $famime = 'file-o';
10449  }
10450  // Audio
10451  if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) {
10452  $mime = 'audio';
10453  $imgmime = 'audio.png';
10454  $famime = 'file-audio-o';
10455  }
10456  // Video
10457  if (preg_match('/\.mp4$/i', $tmpfile)) {
10458  $mime = 'video/mp4';
10459  $imgmime = 'video.png';
10460  $famime = 'file-video-o';
10461  }
10462  if (preg_match('/\.ogv$/i', $tmpfile)) {
10463  $mime = 'video/ogg';
10464  $imgmime = 'video.png';
10465  $famime = 'file-video-o';
10466  }
10467  if (preg_match('/\.webm$/i', $tmpfile)) {
10468  $mime = 'video/webm';
10469  $imgmime = 'video.png';
10470  $famime = 'file-video-o';
10471  }
10472  if (preg_match('/\.avi$/i', $tmpfile)) {
10473  $mime = 'video/x-msvideo';
10474  $imgmime = 'video.png';
10475  $famime = 'file-video-o';
10476  }
10477  if (preg_match('/\.divx$/i', $tmpfile)) {
10478  $mime = 'video/divx';
10479  $imgmime = 'video.png';
10480  $famime = 'file-video-o';
10481  }
10482  if (preg_match('/\.xvid$/i', $tmpfile)) {
10483  $mime = 'video/xvid';
10484  $imgmime = 'video.png';
10485  $famime = 'file-video-o';
10486  }
10487  if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
10488  $mime = 'video';
10489  $imgmime = 'video.png';
10490  $famime = 'file-video-o';
10491  }
10492  // Archive
10493  if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) {
10494  $mime = 'archive';
10495  $imgmime = 'archive.png';
10496  $famime = 'file-archive-o';
10497  } // application/xxx where zzz is zip, ...
10498  // Exe
10499  if (preg_match('/\.(exe|com)$/i', $tmpfile)) {
10500  $mime = 'application/octet-stream';
10501  $imgmime = 'other.png';
10502  $famime = 'file-o';
10503  }
10504  // Lib
10505  if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) {
10506  $mime = 'library';
10507  $imgmime = 'library.png';
10508  $famime = 'file-o';
10509  }
10510  // Err
10511  if (preg_match('/\.err$/i', $tmpfile)) {
10512  $mime = 'error';
10513  $imgmime = 'error.png';
10514  $famime = 'file-text-o';
10515  }
10516 
10517  // Return string
10518  if ($mode == 1) {
10519  $tmp = explode('/', $mime);
10520  return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
10521  }
10522  if ($mode == 2) {
10523  return $imgmime;
10524  }
10525  if ($mode == 3) {
10526  return $srclang;
10527  }
10528  if ($mode == 4) {
10529  return $famime;
10530  }
10531  return $mime;
10532 }
10533 
10545 function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
10546 {
10547  global $conf, $db;
10548 
10549  $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
10550 
10551  $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
10552 
10553  if (is_null($dictvalues)) {
10554  $dictvalues = array();
10555 
10556  $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
10557  if ($checkentity) {
10558  $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
10559  }
10560 
10561  $resql = $db->query($sql);
10562  if ($resql) {
10563  while ($obj = $db->fetch_object($resql)) {
10564  $dictvalues[$obj->{$rowidfield}] = $obj; // $obj is stdClass
10565  }
10566  } else {
10567  dol_print_error($db);
10568  }
10569 
10570  $conf->cache['dictvalues_'.$tablename] = $dictvalues;
10571  }
10572 
10573  if (!empty($dictvalues[$id])) {
10574  // Found
10575  $tmp = $dictvalues[$id];
10576  return (property_exists($tmp, $field) ? $tmp->$field : '');
10577  } else {
10578  // Not found
10579  return '';
10580  }
10581 }
10582 
10589 function colorIsLight($stringcolor)
10590 {
10591  $stringcolor = str_replace('#', '', $stringcolor);
10592  $res = -1;
10593  if (!empty($stringcolor)) {
10594  $res = 0;
10595  $tmp = explode(',', $stringcolor);
10596  if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
10597  $r = $tmp[0];
10598  $g = $tmp[1];
10599  $b = $tmp[2];
10600  } else {
10601  $hexr = $stringcolor[0].$stringcolor[1];
10602  $hexg = $stringcolor[2].$stringcolor[3];
10603  $hexb = $stringcolor[4].$stringcolor[5];
10604  $r = hexdec($hexr);
10605  $g = hexdec($hexg);
10606  $b = hexdec($hexb);
10607  }
10608  $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
10609  if ($bright > 0.6) {
10610  $res = 1;
10611  }
10612  }
10613  return $res;
10614 }
10615 
10624 function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
10625 {
10626  global $conf;
10627 
10628  //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
10629  //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
10630  if (empty($menuentry['enabled'])) {
10631  return 0; // Entry disabled by condition
10632  }
10633  if ($type_user && $menuentry['module']) {
10634  $tmploops = explode('|', $menuentry['module']);
10635  $found = 0;
10636  foreach ($tmploops as $tmploop) {
10637  if (in_array($tmploop, $listofmodulesforexternal)) {
10638  $found++;
10639  break;
10640  }
10641  }
10642  if (!$found) {
10643  return 0; // Entry is for menus all excluded to external users
10644  }
10645  }
10646  if (!$menuentry['perms'] && $type_user) {
10647  return 0; // No permissions and user is external
10648  }
10649  if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) {
10650  return 0; // No permissions and option to hide when not allowed, even for internal user, is on
10651  }
10652  if (!$menuentry['perms']) {
10653  return 2; // No permissions and user is external
10654  }
10655  return 1;
10656 }
10657 
10665 function roundUpToNextMultiple($n, $x = 5)
10666 {
10667  return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
10668 }
10669 
10681 function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
10682 {
10683  $attr = array(
10684  'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css'])
10685  );
10686 
10687  if (empty($html)) {
10688  $html = $label;
10689  }
10690 
10691  if (!empty($url)) {
10692  $attr['href'] = $url;
10693  }
10694 
10695  if ($mode === 'dot') {
10696  $attr['class'] .= ' classfortooltip';
10697  $attr['title'] = $html;
10698  $attr['aria-label'] = $label;
10699  $html = '';
10700  }
10701 
10702  // Override attr
10703  if (!empty($params['attr']) && is_array($params['attr'])) {
10704  foreach ($params['attr'] as $key => $value) {
10705  if ($key == 'class') {
10706  $attr['class'] .= ' '.$value;
10707  } elseif ($key == 'classOverride') {
10708  $attr['class'] = $value;
10709  } else {
10710  $attr[$key] = $value;
10711  }
10712  }
10713  }
10714 
10715  // TODO: add hook
10716 
10717  // escape all attribute
10718  $attr = array_map('dol_escape_htmltag', $attr);
10719 
10720  $TCompiledAttr = array();
10721  foreach ($attr as $key => $value) {
10722  $TCompiledAttr[] = $key.'="'.$value.'"';
10723  }
10724 
10725  $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
10726 
10727  $tag = !empty($url) ? 'a' : 'span';
10728 
10729  return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
10730 }
10731 
10732 
10745 function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
10746 {
10747  global $conf;
10748 
10749  $return = '';
10750  $dolGetBadgeParams = array();
10751 
10752  if (!empty($params['badgeParams'])) {
10753  $dolGetBadgeParams = $params['badgeParams'];
10754  }
10755 
10756  // TODO : add a hook
10757  if ($displayMode == 0) {
10758  $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
10759  } elseif ($displayMode == 1) {
10760  $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10761  } elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) {
10762  // Use status with images (for backward compatibility)
10763  $return = '';
10764  $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
10765  $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>' : '');
10766 
10767  // For small screen, we always use the short label instead of long label.
10768  if (!empty($conf->dol_optimize_smallscreen)) {
10769  if ($displayMode == 0) {
10770  $displayMode = 1;
10771  } elseif ($displayMode == 4) {
10772  $displayMode = 2;
10773  } elseif ($displayMode == 6) {
10774  $displayMode = 5;
10775  }
10776  }
10777 
10778  // For backward compatibility. Image's filename are still in French, so we use this array to convert
10779  $statusImg = array(
10780  'status0' => 'statut0',
10781  'status1' => 'statut1',
10782  'status2' => 'statut2',
10783  'status3' => 'statut3',
10784  'status4' => 'statut4',
10785  'status5' => 'statut5',
10786  'status6' => 'statut6',
10787  'status7' => 'statut7',
10788  'status8' => 'statut8',
10789  'status9' => 'statut9'
10790  );
10791 
10792  if (!empty($statusImg[$statusType])) {
10793  $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
10794  } else {
10795  $htmlImg = img_picto($statusLabel, $statusType);
10796  }
10797 
10798  if ($displayMode === 2) {
10799  $return = $htmlImg.' '.$htmlLabelShort;
10800  } elseif ($displayMode === 3) {
10801  $return = $htmlImg;
10802  } elseif ($displayMode === 4) {
10803  $return = $htmlImg.' '.$htmlLabel;
10804  } elseif ($displayMode === 5) {
10805  $return = $htmlLabelShort.' '.$htmlImg;
10806  } else { // $displayMode >= 6
10807  $return = $htmlLabel.' '.$htmlImg;
10808  }
10809  } elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) {
10810  // Use new badge
10811  $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10812 
10813  $dolGetBadgeParams['attr']['class'] = 'badge-status';
10814  $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
10815 
10816  if ($displayMode == 3) {
10817  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
10818  } elseif ($displayMode === 5) {
10819  $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
10820  } else {
10821  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
10822  }
10823  }
10824 
10825  return $return;
10826 }
10827 
10828 
10858 function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
10859 {
10860  global $hookmanager, $action, $object, $langs;
10861 
10862  //var_dump($params);
10863  if (!empty($params['isDropdown']))
10864  $class = "dropdown-item";
10865  else {
10866  $class = 'butAction';
10867  if ($actionType == 'danger' || $actionType == 'delete') {
10868  $class = 'butActionDelete';
10869  if (!empty($url) && strpos($url, 'token=') === false) $url .= '&token='.newToken();
10870  }
10871  }
10872  $attr = array(
10873  'class' => $class,
10874  'href' => empty($url) ? '' : $url,
10875  'title' => $label
10876  );
10877 
10878  if (empty($text)) {
10879  $text = $label;
10880  $attr['title'] = ''; // if html not set, leave label on title is redundant
10881  } else {
10882  $attr['title'] = $label;
10883  $attr['aria-label'] = $label;
10884  }
10885 
10886  if (empty($userRight)) {
10887  $attr['class'] = 'butActionRefused';
10888  $attr['href'] = '';
10889  $attr['title'] = (($label && $text && $label != $text) ? $label : $langs->trans('NotEnoughPermissions'));
10890  }
10891 
10892  if (!empty($id)) {
10893  $attr['id'] = $id;
10894  }
10895 
10896  // Override attr
10897  if (!empty($params['attr']) && is_array($params['attr'])) {
10898  foreach ($params['attr'] as $key => $value) {
10899  if ($key == 'class') {
10900  $attr['class'] .= ' '.$value;
10901  } elseif ($key == 'classOverride') {
10902  $attr['class'] = $value;
10903  } else {
10904  $attr[$key] = $value;
10905  }
10906  }
10907  }
10908 
10909  // automatic add tooltip when title is detected
10910  if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
10911  $attr['class'].= ' classfortooltip';
10912  }
10913 
10914  // Js Confirm button
10915  if ($userRight && !empty($params['confirm'])) {
10916  if (!is_array($params['confirm'])) {
10917  $params['confirm'] = array();
10918  }
10919 
10920  if (empty($params['confirm']['url'])) {
10921  $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
10922  }
10923 
10924  // for js desabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
10925  $attr['data-confirm-url'] = $params['confirm']['url'];
10926  $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
10927  $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
10928  $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
10929  $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
10930  $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
10931  $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
10932 
10933  $attr['class'].= ' butActionConfirm';
10934  }
10935 
10936  if (isset($attr['href']) && empty($attr['href'])) {
10937  unset($attr['href']);
10938  }
10939 
10940  // escape all attribute
10941  $attr = array_map('dol_escape_htmltag', $attr);
10942 
10943  $TCompiledAttr = array();
10944  foreach ($attr as $key => $value) {
10945  $TCompiledAttr[] = $key.'= "'.$value.'"';
10946  }
10947 
10948  $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
10949 
10950  $tag = !empty($attr['href']) ? 'a' : 'span';
10951 
10952 
10953  $parameters = array(
10954  'TCompiledAttr' => $TCompiledAttr, // array
10955  'compiledAttributes' => $compiledAttributes, // string
10956  'attr' => $attr,
10957  'tag' => $tag,
10958  'label' => $label,
10959  'html' => $text,
10960  'actionType' => $actionType,
10961  'url' => $url,
10962  'id' => $id,
10963  'userRight' => $userRight,
10964  'params' => $params
10965  );
10966 
10967  $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
10968  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
10969 
10970  if (empty($reshook)) {
10971  if (dol_textishtml($text)) { // If content already HTML encoded
10972  return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
10973  } else {
10974  return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
10975  }
10976  } else {
10977  return $hookmanager->resPrint;
10978  }
10979 }
10980 
10987 function dolGetButtonTitleSeparator($moreClass = "")
10988 {
10989  return '<span class="button-title-separator '.$moreClass.'" ></span>';
10990 }
10991 
10998 function getFieldErrorIcon($fieldValidationErrorMsg)
10999 {
11000  $out = '';
11001  if (!empty($fieldValidationErrorMsg)) {
11002  $out.= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
11003  $out.= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
11004  $out.= '</span>';
11005  }
11006 
11007  return $out;
11008 }
11009 
11022 function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
11023 {
11024  global $langs, $conf, $user;
11025 
11026  // Actually this conf is used in css too for external module compatibility and smooth transition to this function
11027  if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) {
11028  return '';
11029  }
11030 
11031  $class = 'btnTitle';
11032  if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
11033  $class .= ' btnTitlePlus';
11034  }
11035  $useclassfortooltip = 1;
11036 
11037  if (!empty($params['morecss'])) {
11038  $class .= ' '.$params['morecss'];
11039  }
11040 
11041  $attr = array(
11042  'class' => $class,
11043  'href' => empty($url) ? '' : $url
11044  );
11045 
11046  if (!empty($helpText)) {
11047  $attr['title'] = dol_escape_htmltag($helpText);
11048  } elseif (empty($attr['title']) && $label) {
11049  $attr['title'] = $label;
11050  $useclassfortooltip = 0;
11051  }
11052 
11053  if ($status == 2) {
11054  $attr['class'] .= ' btnTitleSelected';
11055  } elseif ($status <= 0) {
11056  $attr['class'] .= ' refused';
11057 
11058  $attr['href'] = '';
11059 
11060  if ($status == -1) { // disable
11061  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
11062  } elseif ($status == 0) { // Not enough permissions
11063  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
11064  }
11065  }
11066 
11067  if (!empty($attr['title']) && $useclassfortooltip) {
11068  $attr['class'] .= ' classfortooltip';
11069  }
11070 
11071  if (!empty($id)) {
11072  $attr['id'] = $id;
11073  }
11074 
11075  // Override attr
11076  if (!empty($params['attr']) && is_array($params['attr'])) {
11077  foreach ($params['attr'] as $key => $value) {
11078  if ($key == 'class') {
11079  $attr['class'] .= ' '.$value;
11080  } elseif ($key == 'classOverride') {
11081  $attr['class'] = $value;
11082  } else {
11083  $attr[$key] = $value;
11084  }
11085  }
11086  }
11087 
11088  if (isset($attr['href']) && empty($attr['href'])) {
11089  unset($attr['href']);
11090  }
11091 
11092  // TODO : add a hook
11093 
11094  // escape all attribute
11095  $attr = array_map('dol_escape_htmltag', $attr);
11096 
11097  $TCompiledAttr = array();
11098  foreach ($attr as $key => $value) {
11099  $TCompiledAttr[] = $key.'="'.$value.'"';
11100  }
11101 
11102  $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
11103 
11104  $tag = (empty($attr['href']) ? 'span' : 'a');
11105 
11106  $button = '<'.$tag.' '.$compiledAttributes.'>';
11107  $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
11108  if (!empty($params['forcenohideoftext'])) {
11109  $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
11110  }
11111  $button .= '</'.$tag.'>';
11112 
11113  return $button;
11114 }
11115 
11123 function getElementProperties($element_type)
11124 {
11125  $regs = array();
11126 
11127  $classfile = $classname = $classpath = '';
11128 
11129  // Parse element/subelement (ex: project_task)
11130  $module = $element_type;
11131  $element = $element_type;
11132  $subelement = $element_type;
11133 
11134  // If we ask an resource form external module (instead of default path)
11135  if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) {
11136  $element = $subelement = $regs[1];
11137  $module = $regs[2];
11138  }
11139 
11140  //print '<br>1. element : '.$element.' - module : '.$module .'<br>';
11141  if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) {
11142  $module = $element = $regs[1];
11143  $subelement = $regs[2];
11144  }
11145 
11146  // For compat
11147  if ($element_type == "action") {
11148  $classpath = 'comm/action/class';
11149  $subelement = 'Actioncomm';
11150  $module = 'agenda';
11151  }
11152 
11153  // To work with non standard path
11154  if ($element_type == 'facture' || $element_type == 'invoice') {
11155  $classpath = 'compta/facture/class';
11156  $module = 'facture';
11157  $subelement = 'facture';
11158  }
11159  if ($element_type == 'commande' || $element_type == 'order') {
11160  $classpath = 'commande/class';
11161  $module = 'commande';
11162  $subelement = 'commande';
11163  }
11164  if ($element_type == 'propal') {
11165  $classpath = 'comm/propal/class';
11166  }
11167  if ($element_type == 'supplier_proposal') {
11168  $classpath = 'supplier_proposal/class';
11169  }
11170  if ($element_type == 'shipping') {
11171  $classpath = 'expedition/class';
11172  $subelement = 'expedition';
11173  $module = 'expedition_bon';
11174  }
11175  if ($element_type == 'delivery') {
11176  $classpath = 'delivery/class';
11177  $subelement = 'delivery';
11178  $module = 'delivery_note';
11179  }
11180  if ($element_type == 'contract') {
11181  $classpath = 'contrat/class';
11182  $module = 'contrat';
11183  $subelement = 'contrat';
11184  }
11185  if ($element_type == 'member') {
11186  $classpath = 'adherents/class';
11187  $module = 'adherent';
11188  $subelement = 'adherent';
11189  }
11190  if ($element_type == 'cabinetmed_cons') {
11191  $classpath = 'cabinetmed/class';
11192  $module = 'cabinetmed';
11193  $subelement = 'cabinetmedcons';
11194  }
11195  if ($element_type == 'fichinter') {
11196  $classpath = 'fichinter/class';
11197  $module = 'ficheinter';
11198  $subelement = 'fichinter';
11199  }
11200  if ($element_type == 'dolresource' || $element_type == 'resource') {
11201  $classpath = 'resource/class';
11202  $module = 'resource';
11203  $subelement = 'dolresource';
11204  }
11205  if ($element_type == 'propaldet') {
11206  $classpath = 'comm/propal/class';
11207  $module = 'propal';
11208  $subelement = 'propaleligne';
11209  }
11210  if ($element_type == 'order_supplier') {
11211  $classpath = 'fourn/class';
11212  $module = 'fournisseur';
11213  $subelement = 'commandefournisseur';
11214  $classfile = 'fournisseur.commande';
11215  }
11216  if ($element_type == 'invoice_supplier') {
11217  $classpath = 'fourn/class';
11218  $module = 'fournisseur';
11219  $subelement = 'facturefournisseur';
11220  $classfile = 'fournisseur.facture';
11221  }
11222  if ($element_type == "service") {
11223  $classpath = 'product/class';
11224  $subelement = 'product';
11225  }
11226 
11227  if (empty($classfile)) {
11228  $classfile = strtolower($subelement);
11229  }
11230  if (empty($classname)) {
11231  $classname = ucfirst($subelement);
11232  }
11233  if (empty($classpath)) {
11234  $classpath = $module.'/class';
11235  }
11236 
11237  $element_properties = array(
11238  'module' => $module,
11239  'classpath' => $classpath,
11240  'element' => $element,
11241  'subelement' => $subelement,
11242  'classfile' => $classfile,
11243  'classname' => $classname
11244  );
11245  return $element_properties;
11246 }
11247 
11257 function fetchObjectByElement($element_id, $element_type, $element_ref = '')
11258 {
11259  global $conf, $db;
11260 
11261  $element_prop = getElementProperties($element_type);
11262  if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled) {
11263  dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
11264 
11265  $objecttmp = new $element_prop['classname']($db);
11266  $ret = $objecttmp->fetch($element_id, $element_ref);
11267  if ($ret >= 0) {
11268  return $objecttmp;
11269  }
11270  }
11271  return 0;
11272 }
11273 
11280 function isAFileWithExecutableContent($filename)
11281 {
11282  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)) {
11283  return true;
11284  }
11285 
11286  return false;
11287 }
11288 
11295 function newToken()
11296 {
11297  return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
11298 }
11299 
11306 function currentToken()
11307 {
11308  return isset($_SESSION['token']) ? $_SESSION['token'] : '';
11309 }
11310 
11316 function getNonce()
11317 {
11318  global $conf;
11319 
11320  if (empty($conf->cache['nonce'])) {
11321  $conf->cache['nonce'] = dolGetRandomBytes(8);
11322  }
11323 
11324  return $conf->cache['nonce'];
11325 }
11326 
11339 function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1)
11340 {
11341  global $langs;
11342 
11343  print '<div class="div-table-responsive-no-min">';
11344  print '<table class="noborder centpercent">';
11345  print '<tr class="liste_titre">';
11346 
11347  print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">';
11348 
11349  print $langs->trans($header);
11350 
11351  // extra space between the first header and the number
11352  if ($number > -1) {
11353  print ' ';
11354  }
11355 
11356  if (!empty($link)) {
11357  if (!empty($arguments)) {
11358  print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11359  } else {
11360  print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
11361  }
11362  }
11363 
11364  if ($number > -1) {
11365  print '<span class="badge">'.$number.'</span>';
11366  }
11367 
11368  if (!empty($link)) {
11369  print '</a>';
11370  }
11371 
11372  print '</th>';
11373 
11374  if ($number < 0 && !empty($link)) {
11375  print '<th class="right">';
11376 
11377  if (!empty($arguments)) {
11378  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11379  } else {
11380  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">';
11381  }
11382 
11383  print $langs->trans("FullList");
11384  print '</a>';
11385  print '</th>';
11386  }
11387 
11388  print '</tr>';
11389 }
11390 
11399 function finishSimpleTable($addLineBreak = false)
11400 {
11401  print '</table>';
11402  print '</div>';
11403 
11404  if ($addLineBreak) {
11405  print '<br>';
11406  }
11407 }
11408 
11420 function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
11421 {
11422  global $langs;
11423 
11424  if ($num === 0) {
11425  print '<tr class="oddeven">';
11426  print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>';
11427  print '</tr>';
11428  return;
11429  }
11430 
11431  if ($nbofloop === 0) {
11432  // don't show a summary line
11433  return;
11434  }
11435 
11436  if ($num === 0) {
11437  $colspan = $tableColumnCount;
11438  } elseif ($num > $nbofloop) {
11439  $colspan = $tableColumnCount;
11440  } else {
11441  $colspan = $tableColumnCount - 1;
11442  }
11443 
11444  if ($extraRightColumn) {
11445  $colspan--;
11446  }
11447 
11448  print '<tr class="liste_total">';
11449 
11450  if ($nbofloop > 0 && $num > $nbofloop) {
11451  print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
11452  } else {
11453  print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
11454  print '<td class="right" width="100">'.price($total).'</td>';
11455  }
11456 
11457  if ($extraRightColumn) {
11458  print '<td></td>';
11459  }
11460 
11461  print '</tr>';
11462 }
11463 
11472 function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
11473 {
11474  global $conf;
11475 
11476  if ($method == -1) {
11477  $method = 0;
11478  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) {
11479  $method = 1;
11480  }
11481  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) {
11482  $method = 2;
11483  }
11484  }
11485 
11486  // Be sure we don't have output buffering enabled to have readfile working correctly
11487  while (ob_get_level()) {
11488  ob_end_flush();
11489  }
11490 
11491  // Solution 0
11492  if ($method == 0) {
11493  readfile($fullpath_original_file_osencoded);
11494  } elseif ($method == 1) {
11495  // Solution 1
11496  $handle = fopen($fullpath_original_file_osencoded, "rb");
11497  while (!feof($handle)) {
11498  print fread($handle, 8192);
11499  }
11500  fclose($handle);
11501  } elseif ($method == 2) {
11502  // Solution 2
11503  $handle1 = fopen($fullpath_original_file_osencoded, "rb");
11504  $handle2 = fopen("php://output", "wb");
11505  stream_copy_to_stream($handle1, $handle2);
11506  fclose($handle1);
11507  fclose($handle2);
11508  }
11509 }
11510 
11520 function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
11521 {
11522  /*
11523  global $conf;
11524 
11525  if (!empty($conf->dol_no_mouse_hover)) {
11526  $showonlyonhover = 0;
11527  }*/
11528 
11529  $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
11530  if ($texttoshow === 'none') {
11531  $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>';
11532  } elseif ($texttoshow) {
11533  $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>';
11534  } else {
11535  $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>';
11536  }
11537 
11538  return $result;
11539 }
11540 
11541 
11548 function jsonOrUnserialize($stringtodecode)
11549 {
11550  $result = json_decode($stringtodecode);
11551  if ($result === null) {
11552  $result = unserialize($stringtodecode);
11553  }
11554 
11555  return $result;
11556 }
11557 
11558 
11566 function forgeSQLFromUniversalSearchCriteria($filter, &$error = '')
11567 {
11568  $regexstring = '\‍(([a-zA-Z0-9_\.]+:[<>!=insotlke]+:[^\‍(\‍)]+)\‍)'; // Must be (aaa:bbb:...) with aaa is a field name (with alias or not) and bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot'
11569 
11570  if (!dolCheckFilters($filter, $error)) {
11571  return '1 = 2'; // Bad balance of parenthesis, we force a SQL not found
11572  }
11573 
11574  // Test the filter syntax
11575  $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
11576  $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
11577  // If the string result contains something else than '()', the syntax was wrong
11578  if (preg_match('/[^\‍(\‍)]/', $t)) {
11579  $error = 'Bad syntax of the search string, filter criteria is invalidated';
11580  return '1 = 3'; // Bad syntax of the search string, we force a SQL not found
11581  }
11582 
11583  return " AND (".preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).")";
11584 }
11585 
11593 function dolCheckFilters($sqlfilters, &$error = '')
11594 {
11595  //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
11596  //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
11597  $tmp = $sqlfilters;
11598  $i = 0; $nb = strlen($tmp);
11599  $counter = 0;
11600  while ($i < $nb) {
11601  if ($tmp[$i] == '(') {
11602  $counter++;
11603  }
11604  if ($tmp[$i] == ')') {
11605  $counter--;
11606  }
11607  if ($counter < 0) {
11608  $error = "Wrond balance of parenthesis in sqlfilters=".$sqlfilters;
11609  dol_syslog($error, LOG_WARNING);
11610  return false;
11611  }
11612  $i++;
11613  }
11614  return true;
11615 }
11616 
11625 {
11626  //dol_syslog("Convert matches ".$matches[1]);
11627  if (empty($matches[1])) {
11628  return '';
11629  }
11630  $tmp = explode(':', $matches[1]);
11631  if (count($tmp) < 3) {
11632  return '';
11633  }
11634 
11635  return '()'; // An empty criteria
11636 }
11637 
11646 function dolForgeCriteriaCallback($matches)
11647 {
11648  global $db;
11649 
11650  //dol_syslog("Convert matches ".$matches[1]);
11651  if (empty($matches[1])) {
11652  return '';
11653  }
11654  $tmp = explode(':', $matches[1]);
11655  if (count($tmp) < 3) {
11656  return '';
11657  }
11658 
11659  $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
11660 
11661  $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
11662 
11663  if ($operator == 'NOTLIKE') {
11664  $operator = 'NOT LIKE';
11665  }
11666  if ($operator == 'ISNOT') {
11667  $operator = 'IS NOT';
11668  }
11669  if ($operator == '!=') {
11670  $operator = '<>';
11671  }
11672 
11673  $tmpescaped = $tmp[2];
11674  $regbis = array();
11675 
11676  if ($operator == 'IN') { // IN is allowed for list of ID or code only
11677  //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
11678  $tmpescaped = '('.$db->escape($db->sanitize($tmpescaped, 1, 0)).')';
11679  //} else {
11680  // $tmpescaped = $db->escape($db->sanitize($tmpescaped, 1));
11681  //}
11682  } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
11683  if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
11684  $tmpescaped = $regbis[1];
11685  }
11686  //$tmpescaped = "'".$db->escapeforlike($db->escape($regbis[1]))."'";
11687  $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the like will works
11688  } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
11689  $tmpescaped = "'".$db->escape($regbis[1])."'";
11690  } else {
11691  if (strtoupper($tmpescaped) == 'NULL') {
11692  $tmpescaped = 'NULL';
11693  } elseif (is_int($tmpescaped)) {
11694  $tmpescaped = (int) $tmpescaped;
11695  } else {
11696  $tmpescaped = (float) $tmpescaped;
11697  }
11698  }
11699 
11700  return $db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped;
11701 }
11702 
11703 
11712 function getTimelineIcon($actionstatic, &$histo, $key)
11713 {
11714  global $conf, $langs;
11715  $out = '<!-- timeline icon -->'."\n";
11716  $iconClass = 'fa fa-comments';
11717  $img_picto = '';
11718  $colorClass = '';
11719  $pictoTitle = '';
11720 
11721  if ($histo[$key]['percent'] == -1) {
11722  $colorClass = 'timeline-icon-not-applicble';
11723  $pictoTitle = $langs->trans('StatusNotApplicable');
11724  } elseif ($histo[$key]['percent'] == 0) {
11725  $colorClass = 'timeline-icon-todo';
11726  $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
11727  } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
11728  $colorClass = 'timeline-icon-in-progress';
11729  $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
11730  } elseif ($histo[$key]['percent'] >= 100) {
11731  $colorClass = 'timeline-icon-done';
11732  $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
11733  }
11734 
11735  if ($actionstatic->code == 'AC_TICKET_CREATE') {
11736  $iconClass = 'fa fa-ticket';
11737  } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
11738  $iconClass = 'fa fa-pencilxxx';
11739  } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
11740  $iconClass = 'fa fa-comments';
11741  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
11742  $iconClass = 'fa fa-mask';
11743  } elseif (!empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
11744  if ($actionstatic->type_picto) {
11745  $img_picto = img_picto('', $actionstatic->type_picto);
11746  } else {
11747  if ($actionstatic->type_code == 'AC_RDV') {
11748  $iconClass = 'fa fa-handshake';
11749  } elseif ($actionstatic->type_code == 'AC_TEL') {
11750  $iconClass = 'fa fa-phone';
11751  } elseif ($actionstatic->type_code == 'AC_FAX') {
11752  $iconClass = 'fa fa-fax';
11753  } elseif ($actionstatic->type_code == 'AC_EMAIL') {
11754  $iconClass = 'fa fa-envelope';
11755  } elseif ($actionstatic->type_code == 'AC_INT') {
11756  $iconClass = 'fa fa-shipping-fast';
11757  } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
11758  $iconClass = 'fa fa-robot';
11759  } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
11760  $iconClass = 'fa fa-robot';
11761  }
11762  }
11763  }
11764 
11765  $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
11766  return $out;
11767 }
11768 
11775 function getActionCommEcmList($object)
11776 {
11777  global $conf, $db;
11778 
11779  $documents = array();
11780 
11781  $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
11782  $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
11783  $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
11784  //$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
11785  $sql .= ' ORDER BY ecm.position ASC';
11786 
11787  $resql = $db->query($sql);
11788  if ($resql) {
11789  if ($db->num_rows($resql)) {
11790  while ($obj = $db->fetch_object($resql)) {
11791  $documents[$obj->id] = $obj;
11792  }
11793  }
11794  }
11795 
11796  return $documents;
11797 }
11798 
11799 
11800 
11818 function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = '', $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
11819 {
11820  global $user, $conf;
11821  global $form;
11822 
11823  global $param, $massactionbutton;
11824 
11825  dol_include_once('/comm/action/class/actioncomm.class.php');
11826 
11827  // Check parameters
11828  if (!is_object($filterobj) && !is_object($objcon)) {
11829  dol_print_error('', 'BadParameter');
11830  }
11831 
11832  $histo = array();
11833  $numaction = 0;
11834  $now = dol_now();
11835 
11836  $sortfield_list = explode(',', $sortfield);
11837  $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
11838  $sortfield_new_list = array();
11839  foreach ($sortfield_list as $sortfield_value) {
11840  $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
11841  }
11842  $sortfield_new = implode(',', $sortfield_new_list);
11843 
11844  if (isModEnabled('agenda')) {
11845  // Search histo on actioncomm
11846  if (is_object($objcon) && $objcon->id > 0) {
11847  $sql = "SELECT DISTINCT a.id, a.label as label,";
11848  } else {
11849  $sql = "SELECT a.id, a.label as label,";
11850  }
11851  $sql .= " a.datep as dp,";
11852  $sql .= " a.note as message,";
11853  $sql .= " a.datep2 as dp2,";
11854  $sql .= " a.percent as percent, 'action' as type,";
11855  $sql .= " a.fk_element, a.elementtype,";
11856  $sql .= " a.fk_contact,";
11857  $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
11858  $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";
11859  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
11860  $sql .= ", sp.lastname, sp.firstname";
11861  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11862  $sql .= ", m.lastname, m.firstname";
11863  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11864  $sql .= ", o.ref";
11865  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11866  $sql .= ", o.ref";
11867  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11868  $sql .= ", o.ref";
11869  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
11870  $sql .= ", o.ref";
11871  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
11872  $sql .= ", o.ref";
11873  }
11874  $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
11875  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
11876  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
11877 
11878  $force_filter_contact = false;
11879  if (is_object($objcon) && $objcon->id > 0) {
11880  $force_filter_contact = true;
11881  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
11882  $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
11883  }
11884 
11885  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
11886  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
11887  } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
11888  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
11889  $sql .= " ON er.resource_type = 'dolresource'";
11890  $sql .= " AND er.element_id = a.id";
11891  $sql .= " AND er.resource_id = ".((int) $filterobj->id);
11892  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11893  $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
11894  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11895  $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
11896  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11897  $sql .= ", ".MAIN_DB_PREFIX."product as o";
11898  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11899  $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
11900  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
11901  $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
11902  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
11903  $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
11904  }
11905 
11906  $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
11907  if ($force_filter_contact === false) {
11908  if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
11909  $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
11910  } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
11911  $sql .= " AND a.fk_project = ".((int) $filterobj->id);
11912  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11913  $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
11914  if ($filterobj->id) {
11915  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11916  }
11917  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11918  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
11919  if ($filterobj->id) {
11920  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11921  }
11922  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11923  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
11924  if ($filterobj->id) {
11925  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11926  }
11927  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11928  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
11929  if ($filterobj->id) {
11930  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11931  }
11932  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
11933  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
11934  if ($filterobj->id) {
11935  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11936  }
11937  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
11938  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
11939  if ($filterobj->id) {
11940  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
11941  }
11942  }
11943  }
11944 
11945  // Condition on actioncode
11946  if (!empty($actioncode)) {
11947  if (empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
11948  if ($actioncode == 'AC_NON_AUTO') {
11949  $sql .= " AND c.type != 'systemauto'";
11950  } elseif ($actioncode == 'AC_ALL_AUTO') {
11951  $sql .= " AND c.type = 'systemauto'";
11952  } else {
11953  if ($actioncode == 'AC_OTH') {
11954  $sql .= " AND c.type != 'systemauto'";
11955  } elseif ($actioncode == 'AC_OTH_AUTO') {
11956  $sql .= " AND c.type = 'systemauto'";
11957  }
11958  }
11959  } else {
11960  if ($actioncode == 'AC_NON_AUTO') {
11961  $sql .= " AND c.type != 'systemauto'";
11962  } elseif ($actioncode == 'AC_ALL_AUTO') {
11963  $sql .= " AND c.type = 'systemauto'";
11964  } else {
11965  $sql .= " AND c.code = '".$db->escape($actioncode)."'";
11966  }
11967  }
11968  }
11969  if ($donetodo == 'todo') {
11970  $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
11971  } elseif ($donetodo == 'done') {
11972  $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
11973  }
11974  if (is_array($filters) && $filters['search_agenda_label']) {
11975  $sql .= natural_search('a.label', $filters['search_agenda_label']);
11976  }
11977  }
11978 
11979  // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
11980  if (isModEnabled('mailing') && !empty($objcon->email)
11981  && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
11982  $langs->load("mails");
11983 
11984  $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";
11985  $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
11986  $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
11987  $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
11988  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
11989  $sql2 .= ", '' as lastname, '' as firstname";
11990  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
11991  $sql2 .= ", '' as lastname, '' as firstname";
11992  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
11993  $sql2 .= ", '' as ref";
11994  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
11995  $sql2 .= ", '' as ref";
11996  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
11997  $sql2 .= ", '' as ref";
11998  }
11999  $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
12000  $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
12001  $sql2 .= " AND mc.statut = 1";
12002  $sql2 .= " AND u.rowid = m.fk_user_valid";
12003  $sql2 .= " AND mc.fk_mailing=m.rowid";
12004  }
12005 
12006  if (!empty($sql) && !empty($sql2)) {
12007  $sql = $sql." UNION ".$sql2;
12008  } elseif (empty($sql) && !empty($sql2)) {
12009  $sql = $sql2;
12010  }
12011 
12012  // TODO Add limit in nb of results
12013  if ($sql) { // May not be defined if module Agenda is not enabled and mailing module disabled too
12014  $sql .= $db->order($sortfield_new, $sortorder);
12015 
12016  dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
12017  $resql = $db->query($sql);
12018  if ($resql) {
12019  $i = 0;
12020  $num = $db->num_rows($resql);
12021 
12022  while ($i < $num) {
12023  $obj = $db->fetch_object($resql);
12024 
12025  if ($obj->type == 'action') {
12026  $contactaction = new ActionComm($db);
12027  $contactaction->id = $obj->id;
12028  $result = $contactaction->fetchResources();
12029  if ($result < 0) {
12030  dol_print_error($db);
12031  setEventMessage("actions.lib::show_actions_messaging Error fetch ressource", 'errors');
12032  }
12033 
12034  //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
12035  //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
12036  $tododone = '';
12037  if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
12038  $tododone = 'todo';
12039  }
12040 
12041  $histo[$numaction] = array(
12042  'type'=>$obj->type,
12043  'tododone'=>$tododone,
12044  'id'=>$obj->id,
12045  'datestart'=>$db->jdate($obj->dp),
12046  'dateend'=>$db->jdate($obj->dp2),
12047  'note'=>$obj->label,
12048  'message'=>$obj->message,
12049  'percent'=>$obj->percent,
12050 
12051  'userid'=>$obj->user_id,
12052  'login'=>$obj->user_login,
12053  'userfirstname'=>$obj->user_firstname,
12054  'userlastname'=>$obj->user_lastname,
12055  'userphoto'=>$obj->user_photo,
12056 
12057  'contact_id'=>$obj->fk_contact,
12058  'socpeopleassigned' => $contactaction->socpeopleassigned,
12059  'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
12060  'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
12061  'fk_element'=>$obj->fk_element,
12062  'elementtype'=>$obj->elementtype,
12063  // Type of event
12064  'acode'=>$obj->acode,
12065  'alabel'=>$obj->alabel,
12066  'libelle'=>$obj->alabel, // deprecated
12067  'apicto'=>$obj->apicto
12068  );
12069  } else {
12070  $histo[$numaction] = array(
12071  'type'=>$obj->type,
12072  'tododone'=>'done',
12073  'id'=>$obj->id,
12074  'datestart'=>$db->jdate($obj->dp),
12075  'dateend'=>$db->jdate($obj->dp2),
12076  'note'=>$obj->label,
12077  'message'=>$obj->message,
12078  'percent'=>$obj->percent,
12079  'acode'=>$obj->acode,
12080 
12081  'userid'=>$obj->user_id,
12082  'login'=>$obj->user_login,
12083  'userfirstname'=>$obj->user_firstname,
12084  'userlastname'=>$obj->user_lastname,
12085  'userphoto'=>$obj->user_photo
12086  );
12087  }
12088 
12089  $numaction++;
12090  $i++;
12091  }
12092  } else {
12093  dol_print_error($db);
12094  }
12095  }
12096 
12097  // Set $out to show events
12098  $out = '';
12099 
12100  if (!isModEnabled('agenda')) {
12101  $langs->loadLangs(array("admin", "errors"));
12102  $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
12103  }
12104 
12105  if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
12106  $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
12107 
12108  require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
12109  include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12110  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
12111  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
12112 
12113  $formactions = new FormActions($db);
12114 
12115  $actionstatic = new ActionComm($db);
12116  $userstatic = new User($db);
12117  $contactstatic = new Contact($db);
12118  $userGetNomUrlCache = array();
12119 
12120  $out .= '<div class="filters-container" >';
12121  $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
12122  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
12123 
12124  if ($objcon && get_class($objcon) == 'Contact' &&
12125  (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
12126  $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
12127  } else {
12128  $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
12129  }
12130  if ($filterobj && get_class($filterobj) == 'Societe') {
12131  $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
12132  }
12133 
12134  $out .= "\n";
12135 
12136  $out .= '<div class="div-table-responsive-no-min">';
12137  $out .= '<table class="noborder borderbottom centpercent">';
12138 
12139  $out .= '<tr class="liste_titre">';
12140 
12141  $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
12142 
12143  $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
12144  if ($donetodo) {
12145  $out .= '<th class="liste_titre"></th>';
12146  }
12147  $out .= '<th class="liste_titre">';
12148  $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
12149  //$out .= img_picto($langs->trans("Type"), 'type');
12150  $out .= $formactions->select_type_actions($actioncode, "actioncode", '', empty($conf->global->AGENDA_USE_EVENT_TYPE) ? 1 : -1, 0, 0, 1, 'minwidth200imp');
12151  $out .= '</th>';
12152  $out .= '<th class="liste_titre maxwidth100onsmartphone">';
12153  $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
12154  $out .= '</th>';
12155 
12156  $out .= '<th class="liste_titre width50 middle">';
12157  $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
12158  $out .= $searchpicto;
12159  $out .= '</th>';
12160  $out .= '</tr>';
12161 
12162 
12163  $out .= '</table>';
12164 
12165  $out .= '</form>';
12166  $out .= '</div>';
12167 
12168  $out .= "\n";
12169 
12170  $out .= '<ul class="timeline">';
12171 
12172  if ($donetodo) {
12173  $tmp = '';
12174  if (get_class($filterobj) == 'Societe') {
12175  $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
12176  }
12177  $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
12178  $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
12179  $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
12180  //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
12181  if (get_class($filterobj) == 'Societe') {
12182  $tmp .= '</a>';
12183  }
12184  $out .= getTitleFieldOfList($tmp);
12185  }
12186 
12187 
12188  //require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
12189  //$caction=new CActionComm($db);
12190  //$arraylist=$caction->liste_array(1, 'code', '', (empty($conf->global->AGENDA_USE_EVENT_TYPE)?1:0), '', 1);
12191 
12192  $actualCycleDate = false;
12193 
12194  // Loop on each event to show it
12195  foreach ($histo as $key => $value) {
12196  $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
12197 
12198  $actionstatic->type_picto = $histo[$key]['apicto'];
12199  $actionstatic->type_code = $histo[$key]['acode'];
12200 
12201  $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
12202 
12203  $tmpa = dol_getdate($histo[$key]['datestart'], false);
12204  if ($actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
12205  $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
12206  $out .= '<!-- timeline time label -->';
12207  $out .= '<li class="time-label">';
12208  $out .= '<span class="timeline-badge-date">';
12209  $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
12210  $out .= '</span>';
12211  $out .= '</li>';
12212  $out .= '<!-- /.timeline-label -->';
12213  }
12214 
12215 
12216  $out .= '<!-- timeline item -->'."\n";
12217  $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
12218 
12219  $out .= getTimelineIcon($actionstatic, $histo, $key);
12220 
12221  $out .= '<div class="timeline-item">'."\n";
12222 
12223  $out .= '<span class="timeline-header-action">';
12224 
12225  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
12226  $out .= '<a class="timeline-btn" href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12227  $out .= $histo[$key]['id'];
12228  $out .= '</a> ';
12229  } else {
12230  $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
12231  }
12232 
12233  if (!empty($user->rights->agenda->allactions->create) ||
12234  (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && !empty($user->rights->agenda->myactions->create))) {
12235  $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>';
12236  }
12237 
12238  $out .= '</span>';
12239  // Date
12240  $out .= '<span class="time"><i class="fa fa-clock-o"></i> ';
12241  $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
12242  if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
12243  $tmpa = dol_getdate($histo[$key]['datestart'], true);
12244  $tmpb = dol_getdate($histo[$key]['dateend'], true);
12245  if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
12246  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
12247  } else {
12248  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
12249  }
12250  }
12251  $late = 0;
12252  if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12253  $late = 1;
12254  }
12255  if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12256  $late = 1;
12257  }
12258  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
12259  $late = 1;
12260  }
12261  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12262  $late = 1;
12263  }
12264  if ($late) {
12265  $out .= img_warning($langs->trans("Late")).' ';
12266  }
12267  $out .= "</span>\n";
12268 
12269  // Ref
12270  $out .= '<h3 class="timeline-header">';
12271 
12272  // Author of event
12273  $out .= '<div class="messaging-author inline-block">';
12274  if ($histo[$key]['userid'] > 0) {
12275  if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
12276  $userstatic->fetch($histo[$key]['userid']);
12277  $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
12278  }
12279  $out .= $userGetNomUrlCache[$histo[$key]['userid']];
12280  }
12281  $out .= '</div>';
12282 
12283  // Title
12284  $out .= ' <div class="messaging-title inline-block">';
12285 
12286  if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
12287  $out .= $langs->trans('TicketNewMessage');
12288  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
12289  $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
12290  } else {
12291  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'action') {
12292  $transcode = $langs->trans("Action".$histo[$key]['acode']);
12293  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
12294  $libelle = $histo[$key]['note'];
12295  $actionstatic->id = $histo[$key]['id'];
12296  $out .= dol_trunc($libelle, 120);
12297  }
12298  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
12299  $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12300  $transcode = $langs->trans("Action".$histo[$key]['acode']);
12301  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
12302  $out .= dol_trunc($libelle, 120);
12303  }
12304  }
12305 
12306  $out .= '</div>';
12307 
12308  $out .= '</h3>';
12309 
12310  if (!empty($histo[$key]['message'])
12311  && $actionstatic->code != 'AC_TICKET_CREATE'
12312  && $actionstatic->code != 'AC_TICKET_MODIFY'
12313  ) {
12314  $out .= '<div class="timeline-body">';
12315  $out .= $histo[$key]['message'];
12316  $out .= '</div>';
12317  }
12318 
12319  // Timeline footer
12320  $footer = '';
12321 
12322  // Contact for this action
12323  if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
12324  $contactList = '';
12325  foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
12326  $contact = new Contact($db);
12327  $result = $contact->fetch($cid);
12328 
12329  if ($result < 0) {
12330  dol_print_error($db, $contact->error);
12331  }
12332 
12333  if ($result > 0) {
12334  $contactList .= !empty($contactList) ? ', ' : '';
12335  $contactList .= $contact->getNomUrl(1);
12336  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12337  if (!empty($contact->phone_pro)) {
12338  $contactList .= '('.dol_print_phone($contact->phone_pro).')';
12339  }
12340  }
12341  }
12342  }
12343 
12344  $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
12345  } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
12346  $contact = new Contact($db);
12347  $result = $contact->fetch($histo[$key]['contact_id']);
12348 
12349  if ($result < 0) {
12350  dol_print_error($db, $contact->error);
12351  }
12352 
12353  if ($result > 0) {
12354  $footer .= $contact->getNomUrl(1);
12355  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12356  if (!empty($contact->phone_pro)) {
12357  $footer .= '('.dol_print_phone($contact->phone_pro).')';
12358  }
12359  }
12360  }
12361  }
12362 
12363  $documents = getActionCommEcmList($actionstatic);
12364  if (!empty($documents)) {
12365  $footer .= '<div class="timeline-documents-container">';
12366  foreach ($documents as $doc) {
12367  $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
12368  $footer .= ' data-id="'.$doc->id.'" ';
12369  $footer .= ' data-path="'.$doc->filepath.'"';
12370  $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
12371  $footer .= '>';
12372 
12373  $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
12374  $mime = dol_mimetype($filePath);
12375  $file = $actionstatic->id.'/'.$doc->filename;
12376  $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
12377  $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
12378  $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
12379 
12380  $mimeAttr = ' mime="'.$mime.'" ';
12381  $class = '';
12382  if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
12383  $class .= ' documentpreview';
12384  }
12385 
12386  $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
12387  $footer .= img_mime($filePath).' '.$doc->filename;
12388  $footer .= '</a>';
12389 
12390  $footer .= '</span>';
12391  }
12392  $footer .= '</div>';
12393  }
12394 
12395  if (!empty($footer)) {
12396  $out .= '<div class="timeline-footer">'.$footer.'</div>';
12397  }
12398 
12399  $out .= '</div>'."\n"; // end timeline-item
12400 
12401  $out .= '</li>';
12402  $out .= '<!-- END timeline item -->';
12403 
12404  $i++;
12405  }
12406 
12407  $out .= "</ul>\n";
12408 
12409  if (empty($histo)) {
12410  $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
12411  }
12412  }
12413 
12414  if ($noprint) {
12415  return $out;
12416  } else {
12417  print $out;
12418  }
12419 }
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:680
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:47
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:1996
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:451
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 has a valid balance of parenthesis.
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.
utf8_valid($str)
Check if a string is in UTF8.
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.
forgeSQLFromUniversalSearchCriteria($filter, &$error='')
forgeSQLFromUniversalSearchCriteria
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...
getNonce()
Return a random string to be used as a nonce value for js.
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.
dolForgeDummyCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax string.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
dolForgeCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax 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:1440
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:140
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:122
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length fro cryptographic purposes.
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge,...