dolibarr  19.0.0-dev
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-2023 Frédéric France <frederic.france@netlogic.fr>
17  * Copyright (C) 2019-2023 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  * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
24  *
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation; either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program. If not, see <https://www.gnu.org/licenses/>.
37  * or see https://www.gnu.org/
38  */
39 
46 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
47 
48 // Function for better PHP x compatibility
49 if (!function_exists('utf8_encode')) {
56  function utf8_encode($elements)
57  {
58  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
59  }
60 }
61 
62 if (!function_exists('utf8_decode')) {
69  function utf8_decode($elements)
70  {
71  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
72  }
73 }
74 if (!function_exists('str_starts_with')) {
82  function str_starts_with($haystack, $needle)
83  {
84  return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
85  }
86 }
87 if (!function_exists('str_ends_with')) {
95  function str_ends_with($haystack, $needle)
96  {
97  return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
98  }
99 }
100 if (!function_exists('str_contains')) {
108  function str_contains($haystack, $needle)
109  {
110  return $needle !== '' && mb_strpos($haystack, $needle) !== false;
111  }
112 }
113 
114 
123 function getMultidirOutput($object, $module = '')
124 {
125  global $conf;
126  if (!is_object($object) && empty($module)) {
127  return null;
128  }
129  if (empty($module) && !empty($object->element)) {
130  $module = $object->element;
131  }
132  return $conf->$module->multidir_output[(!empty($object->entity) ? $object->entity : $conf->entity)];
133 }
134 
142 function getDolGlobalString($key, $default = '')
143 {
144  global $conf;
145  // return $conf->global->$key ?? $default;
146  return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
147 }
148 
156 function getDolGlobalInt($key, $default = 0)
157 {
158  global $conf;
159  // return $conf->global->$key ?? $default;
160  return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
161 }
162 
171 function getDolUserString($key, $default = '', $tmpuser = null)
172 {
173  if (empty($tmpuser)) {
174  global $user;
175  $tmpuser = $user;
176  }
177 
178  // return $conf->global->$key ?? $default;
179  return (string) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
180 }
181 
190 function getDolUserInt($key, $default = 0, $tmpuser = null)
191 {
192  if (empty($tmpuser)) {
193  global $user;
194  $tmpuser = $user;
195  }
196 
197  // return $conf->global->$key ?? $default;
198  return (int) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
199 }
200 
207 function isModEnabled($module)
208 {
209  global $conf;
210 
211  // Fix special cases
212  $arrayconv = array(
213  'project' => 'projet',
214  'contract' => 'contrat',
215  'bank' => 'banque'
216  );
217  if (empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) {
218  $arrayconv['supplier_order'] = 'fournisseur';
219  $arrayconv['supplier_invoice'] = 'fournisseur';
220  }
221  if (!empty($arrayconv[$module])) {
222  $module = $arrayconv[$module];
223  }
224 
225  return !empty($conf->modules[$module]);
226  //return !empty($conf->$module->enabled);
227 }
228 
240 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
241 {
242  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
243 
244  $class = 'DoliDB'.ucfirst($type);
245  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
246  return $dolidb;
247 }
248 
266 function getEntity($element, $shared = 1, $currentobject = null)
267 {
268  global $conf, $mc, $hookmanager, $object, $action, $db;
269 
270  if (!is_object($hookmanager)) {
271  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
272  $hookmanager = new HookManager($db);
273  }
274 
275  // fix different element names (France to English)
276  switch ($element) {
277  case 'contrat':
278  $element = 'contract';
279  break; // "/contrat/class/contrat.class.php"
280  case 'order_supplier':
281  $element = 'supplier_order';
282  break; // "/fourn/class/fournisseur.commande.class.php"
283  case 'invoice_supplier':
284  $element = 'supplier_invoice';
285  break; // "/fourn/class/fournisseur.facture.class.php"
286  }
287 
288  if (is_object($mc)) {
289  $out = $mc->getEntity($element, $shared, $currentobject);
290  } else {
291  $out = '';
292  $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values');
293  if (in_array($element, $addzero)) {
294  $out .= '0,';
295  }
296  $out .= ((int) $conf->entity);
297  }
298 
299  // Manipulate entities to query on the fly
300  $parameters = array(
301  'element' => $element,
302  'shared' => $shared,
303  'object' => $object,
304  'currentobject' => $currentobject,
305  'out' => $out
306  );
307  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
308 
309  if (is_numeric($reshook)) {
310  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
311  $out .= ','.$hookmanager->resPrint; // add
312  } elseif ($reshook == 1) {
313  $out = $hookmanager->resPrint; // replace
314  }
315  }
316 
317  return $out;
318 }
319 
326 function setEntity($currentobject)
327 {
328  global $conf, $mc;
329 
330  if (is_object($mc) && method_exists($mc, 'setEntity')) {
331  return $mc->setEntity($currentobject);
332  } else {
333  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
334  }
335 }
336 
343 function isASecretKey($keyname)
344 {
345  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
346 }
347 
348 
355 function num2Alpha($n)
356 {
357  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
358  $r = chr($n % 26 + 0x41) . $r;
359  return $r;
360 }
361 
362 
379 function getBrowserInfo($user_agent)
380 {
381  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
382 
383  $name = 'unknown';
384  $version = '';
385  $os = 'unknown';
386  $phone = '';
387 
388  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
389 
390  $detectmobile = new Mobile_Detect(null, $user_agent);
391  $tablet = $detectmobile->isTablet();
392 
393  if ($detectmobile->isMobile()) {
394  $phone = 'unknown';
395 
396  // If phone/smartphone, we set phone os name.
397  if ($detectmobile->is('AndroidOS')) {
398  $os = $phone = 'android';
399  } elseif ($detectmobile->is('BlackBerryOS')) {
400  $os = $phone = 'blackberry';
401  } elseif ($detectmobile->is('iOS')) {
402  $os = 'ios';
403  $phone = 'iphone';
404  } elseif ($detectmobile->is('PalmOS')) {
405  $os = $phone = 'palm';
406  } elseif ($detectmobile->is('SymbianOS')) {
407  $os = 'symbian';
408  } elseif ($detectmobile->is('webOS')) {
409  $os = 'webos';
410  } elseif ($detectmobile->is('MaemoOS')) {
411  $os = 'maemo';
412  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
413  $os = 'windows';
414  }
415  }
416 
417  // OS
418  if (preg_match('/linux/i', $user_agent)) {
419  $os = 'linux';
420  } elseif (preg_match('/macintosh/i', $user_agent)) {
421  $os = 'macintosh';
422  } elseif (preg_match('/windows/i', $user_agent)) {
423  $os = 'windows';
424  }
425 
426  // Name
427  $reg = array();
428  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
429  $name = 'firefox';
430  $version = empty($reg[2]) ? '' : $reg[2];
431  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
432  $name = 'edge';
433  $version = empty($reg[2]) ? '' : $reg[2];
434  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
435  $name = 'chrome';
436  $version = empty($reg[2]) ? '' : $reg[2];
437  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
438  // we can have 'chrome (Mozilla...) chrome x.y' in one string
439  $name = 'chrome';
440  } elseif (preg_match('/iceweasel/i', $user_agent)) {
441  $name = 'iceweasel';
442  } elseif (preg_match('/epiphany/i', $user_agent)) {
443  $name = 'epiphany';
444  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
445  $name = 'safari';
446  $version = empty($reg[2]) ? '' : $reg[2];
447  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
448  // Safari is often present in string for mobile but its not.
449  $name = 'opera';
450  $version = empty($reg[2]) ? '' : $reg[2];
451  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
452  $name = 'ie';
453  $version = end($reg);
454  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
455  // MS products at end
456  $name = 'ie';
457  $version = end($reg);
458  } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
459  // MS products at end
460  $name = 'lynxlinks';
461  $version = empty($reg[3]) ? '' : $reg[3];
462  }
463 
464  if ($tablet) {
465  $layout = 'tablet';
466  } elseif ($phone) {
467  $layout = 'phone';
468  } else {
469  $layout = 'classic';
470  }
471 
472  return array(
473  'browsername' => $name,
474  'browserversion' => $version,
475  'browseros' => $os,
476  'browserua' => $user_agent,
477  'layout' => $layout, // tablet, phone, classic
478  'phone' => $phone, // deprecated
479  'tablet' => $tablet // deprecated
480  );
481 }
482 
488 function dol_shutdown()
489 {
490  global $user, $langs, $db;
491  $disconnectdone = false;
492  $depth = 0;
493  if (is_object($db) && !empty($db->connected)) {
494  $depth = $db->transaction_opened;
495  $disconnectdone = $db->close();
496  }
497  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
498 }
499 
509 function GETPOSTISSET($paramname)
510 {
511  $isset = false;
512 
513  $relativepathstring = $_SERVER["PHP_SELF"];
514  // Clean $relativepathstring
515  if (constant('DOL_URL_ROOT')) {
516  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
517  }
518  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
519  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
520  //var_dump($relativepathstring);
521  //var_dump($user->default_values);
522 
523  // Code for search criteria persistence.
524  // Retrieve values if restore_lastsearch_values
525  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
526  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
527  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
528  if (is_array($tmp)) {
529  foreach ($tmp as $key => $val) {
530  if ($key == $paramname) { // We are on the requested parameter
531  $isset = true;
532  break;
533  }
534  }
535  }
536  }
537  // If there is saved contextpage, limit, page or mode
538  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
539  $isset = true;
540  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
541  $isset = true;
542  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
543  $isset = true;
544  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
545  $isset = true;
546  }
547  } else {
548  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
549  }
550 
551  return $isset;
552 }
553 
562 function GETPOSTISARRAY($paramname, $method = 0)
563 {
564  // for $method test need return the same $val as GETPOST
565  if (empty($method)) {
566  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
567  } elseif ($method == 1) {
568  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
569  } elseif ($method == 2) {
570  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
571  } elseif ($method == 3) {
572  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
573  } else {
574  $val = 'BadFirstParameterForGETPOST';
575  }
576 
577  return is_array($val);
578 }
579 
609 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
610 {
611  global $mysoc, $user, $conf;
612 
613  if (empty($paramname)) {
614  return 'BadFirstParameterForGETPOST';
615  }
616  if (empty($check)) {
617  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
618  // Enable this line to know who call the GETPOST with '' $check parameter.
619  //var_dump(debug_backtrace()[0]);
620  }
621 
622  if (empty($method)) {
623  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
624  } elseif ($method == 1) {
625  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
626  } elseif ($method == 2) {
627  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
628  } elseif ($method == 3) {
629  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
630  } else {
631  return 'BadThirdParameterForGETPOST';
632  }
633 
634  if (empty($method) || $method == 3 || $method == 4) {
635  $relativepathstring = $_SERVER["PHP_SELF"];
636  // Clean $relativepathstring
637  if (constant('DOL_URL_ROOT')) {
638  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
639  }
640  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
641  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
642  //var_dump($relativepathstring);
643  //var_dump($user->default_values);
644 
645  // Code for search criteria persistence.
646  // Retrieve values if restore_lastsearch_values
647  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
648  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
649  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
650  if (is_array($tmp)) {
651  foreach ($tmp as $key => $val) {
652  if ($key == $paramname) { // We are on the requested parameter
653  $out = $val;
654  break;
655  }
656  }
657  }
658  }
659  // If there is saved contextpage, page or limit
660  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
661  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
662  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
663  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
664  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
665  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
666  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
667  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
668  }
669  } elseif (!isset($_GET['sortfield'])) {
670  // Else, retrieve default values if we are not doing a sort
671  // 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
672  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
673  // Search default value from $object->field
674  global $object;
675  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
676  $out = $object->fields[$paramname]['default'];
677  }
678  }
679  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
680  if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
681  // Now search in setup to overwrite default values
682  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
683  if (isset($user->default_values[$relativepathstring]['createform'])) {
684  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
685  $qualified = 0;
686  if ($defkey != '_noquery_') {
687  $tmpqueryarraytohave = explode('&', $defkey);
688  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
689  $foundintru = 0;
690  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
691  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
692  $foundintru = 1;
693  }
694  }
695  if (!$foundintru) {
696  $qualified = 1;
697  }
698  //var_dump($defkey.'-'.$qualified);
699  } else {
700  $qualified = 1;
701  }
702 
703  if ($qualified) {
704  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
705  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
706  break;
707  }
708  }
709  }
710  }
711  }
712  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
713  // Management of default search_filters and sort order
714  if (!empty($user->default_values)) {
715  // $user->default_values defined from menu 'Setup - Default values'
716  //var_dump($user->default_values[$relativepathstring]);
717  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
718  // Sorted on which fields ? ASC or DESC ?
719  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
720  // Even if paramname is sortfield, data are stored into ['sortorder...']
721  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
722  $qualified = 0;
723  if ($defkey != '_noquery_') {
724  $tmpqueryarraytohave = explode('&', $defkey);
725  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
726  $foundintru = 0;
727  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
728  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
729  $foundintru = 1;
730  }
731  }
732  if (!$foundintru) {
733  $qualified = 1;
734  }
735  //var_dump($defkey.'-'.$qualified);
736  } else {
737  $qualified = 1;
738  }
739 
740  if ($qualified) {
741  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
742  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
743  if ($out) {
744  $out .= ', ';
745  }
746  if ($paramname == 'sortfield') {
747  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
748  }
749  if ($paramname == 'sortorder') {
750  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
751  }
752  }
753  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
754  }
755  }
756  }
757  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
758  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
759  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
760  continue;
761  }
762  $qualified = 0;
763  if ($defkey != '_noquery_') {
764  $tmpqueryarraytohave = explode('&', $defkey);
765  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
766  $foundintru = 0;
767  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
768  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
769  $foundintru = 1;
770  }
771  }
772  if (!$foundintru) {
773  $qualified = 1;
774  }
775  //var_dump($defkey.'-'.$qualified);
776  } else {
777  $qualified = 1;
778  }
779 
780  if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
781  // We must keep $_POST and $_GET here
782  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
783  // We made a search from quick search menu, do we still use default filter ?
784  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
785  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
786  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
787  }
788  } else {
789  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
790  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
791  }
792  break;
793  }
794  }
795  }
796  }
797  }
798  }
799  }
800  }
801 
802  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
803  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
804  // 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.
805  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
806  $reg = array();
807  $maxloop = 20;
808  $loopnb = 0; // Protection against infinite loop
809  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.
810  $loopnb++;
811  $newout = '';
812 
813  if ($reg[1] == 'DAY') {
814  $tmp = dol_getdate(dol_now(), true);
815  $newout = $tmp['mday'];
816  } elseif ($reg[1] == 'MONTH') {
817  $tmp = dol_getdate(dol_now(), true);
818  $newout = $tmp['mon'];
819  } elseif ($reg[1] == 'YEAR') {
820  $tmp = dol_getdate(dol_now(), true);
821  $newout = $tmp['year'];
822  } elseif ($reg[1] == 'PREVIOUS_DAY') {
823  $tmp = dol_getdate(dol_now(), true);
824  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
825  $newout = $tmp2['day'];
826  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
827  $tmp = dol_getdate(dol_now(), true);
828  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
829  $newout = $tmp2['month'];
830  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
831  $tmp = dol_getdate(dol_now(), true);
832  $newout = ($tmp['year'] - 1);
833  } elseif ($reg[1] == 'NEXT_DAY') {
834  $tmp = dol_getdate(dol_now(), true);
835  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
836  $newout = $tmp2['day'];
837  } elseif ($reg[1] == 'NEXT_MONTH') {
838  $tmp = dol_getdate(dol_now(), true);
839  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
840  $newout = $tmp2['month'];
841  } elseif ($reg[1] == 'NEXT_YEAR') {
842  $tmp = dol_getdate(dol_now(), true);
843  $newout = ($tmp['year'] + 1);
844  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
845  $newout = $mysoc->country_id;
846  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
847  $newout = $user->id;
848  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
849  $newout = $user->fk_user;
850  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
851  $newout = $conf->entity;
852  } else {
853  $newout = ''; // Key not found, we replace with empty string
854  }
855  //var_dump('__'.$reg[1].'__ -> '.$newout);
856  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
857  }
858  }
859 
860  // Check rule
861  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
862  if (!is_array($out) || empty($out)) {
863  $out = array();
864  } else {
865  $tmparray = explode(':', $check);
866  if (!empty($tmparray[1])) {
867  $tmpcheck = $tmparray[1];
868  } else {
869  $tmpcheck = 'alphanohtml';
870  }
871  foreach ($out as $outkey => $outval) {
872  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
873  }
874  }
875  } else {
876  // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
877  // 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
878  if (strpos($paramname, 'search_') === 0) {
879  $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
880  }
881 
882  $out = sanitizeVal($out, $check, $filter, $options);
883  }
884 
885  // Sanitizing for special parameters.
886  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
887  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
888  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
889  $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.
890  do {
891  $oldstringtoclean = $out;
892  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
893  $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'
894  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
895  } while ($oldstringtoclean != $out);
896  }
897 
898  // Code for search criteria persistence.
899  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
900  if (empty($method) || $method == 3 || $method == 4) {
901  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
902  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
903 
904  // We save search key only if $out not empty that means:
905  // - posted value not empty, or
906  // - 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).
907 
908  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
909  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
910  }
911  }
912  }
913 
914  return $out;
915 }
916 
926 function GETPOSTINT($paramname, $method = 0)
927 {
928  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
929 }
930 
931 
942 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
943 {
944  return sanitizeVal($out, $check, $filter, $options);
945 }
946 
956 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
957 {
958  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
959  // Check is done after replacement
960  switch ($check) {
961  case 'none':
962  break;
963  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
964  if (!is_numeric($out)) {
965  $out = '';
966  }
967  break;
968  case 'intcomma':
969  if (preg_match('/[^0-9,-]+/i', $out)) {
970  $out = '';
971  }
972  break;
973  case 'san_alpha':
974  $out = filter_var($out, FILTER_SANITIZE_STRING);
975  break;
976  case 'email':
977  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
978  break;
979  case 'aZ':
980  if (!is_array($out)) {
981  $out = trim($out);
982  if (preg_match('/[^a-z]+/i', $out)) {
983  $out = '';
984  }
985  }
986  break;
987  case 'aZ09':
988  if (!is_array($out)) {
989  $out = trim($out);
990  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
991  $out = '';
992  }
993  }
994  break;
995  case 'aZ09arobase': // great to sanitize $objecttype parameter
996  if (!is_array($out)) {
997  $out = trim($out);
998  if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
999  $out = '';
1000  }
1001  }
1002  break;
1003  case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1004  if (!is_array($out)) {
1005  $out = trim($out);
1006  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1007  $out = '';
1008  }
1009  }
1010  break;
1011  case 'alpha': // No html and no ../ and "
1012  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1013  if (!is_array($out)) {
1014  $out = trim($out);
1015  do {
1016  $oldstringtoclean = $out;
1017  // Remove html tags
1018  $out = dol_string_nohtmltag($out, 0);
1019  // Remove also other dangerous string sequences
1020  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
1021  // '../' or '..\' is dangerous because it allows dir transversals
1022  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
1023  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
1024  } while ($oldstringtoclean != $out);
1025  // keep lines feed
1026  }
1027  break;
1028  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'
1029  if (!is_array($out)) {
1030  $out = trim($out);
1031  do {
1032  $oldstringtoclean = $out;
1033  // Remove html tags
1034  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1035  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
1036  // '../' or '..\' is dangerous because it allows dir transversals
1037  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
1038  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
1039  } while ($oldstringtoclean != $out);
1040  }
1041  break;
1042  case 'nohtml': // No html
1043  $out = dol_string_nohtmltag($out, 0);
1044  break;
1045  case 'restricthtmlnolink':
1046  case 'restricthtml': // Recommended for most html textarea
1047  case 'restricthtmlallowclass':
1048  case 'restricthtmlallowunvalid':
1049  $out = dol_htmlwithnojs($out, 1, $check);
1050  break;
1051  case 'custom':
1052  if (!empty($out)) {
1053  if (empty($filter)) {
1054  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1055  }
1056  /*if (empty($options)) {
1057  return 'BadParameterForGETPOST - Param 4 of sanitizeVal()';
1058  }*/
1059  $out = filter_var($out, $filter, $options);
1060  }
1061  break;
1062  }
1063 
1064  return $out;
1065 }
1066 
1067 
1068 if (!function_exists('dol_getprefix')) {
1078  function dol_getprefix($mode = '')
1079  {
1080  // If prefix is for email (we need to have $conf already loaded for this case)
1081  if ($mode == 'email') {
1082  global $conf;
1083 
1084  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1085  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
1086  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1087  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1088  return $_SERVER["SERVER_NAME"];
1089  }
1090  }
1091 
1092  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1093  if (!empty($conf->file->instance_unique_id)) {
1094  return sha1('dolibarr'.$conf->file->instance_unique_id);
1095  }
1096 
1097  // For backward compatibility when instance_unique_id is not set
1098  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1099  }
1100 
1101  // If prefix is for session (no need to have $conf loaded)
1102  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1103  $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
1104 
1105  // The recommended value (may be not defined for old versions)
1106  if (!empty($tmp_instance_unique_id)) {
1107  return sha1('dolibarr'.$tmp_instance_unique_id);
1108  }
1109 
1110  // For backward compatibility when instance_unique_id is not set
1111  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1112  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1113  } else {
1114  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1115  }
1116  }
1117 }
1118 
1129 function dol_include_once($relpath, $classname = '')
1130 {
1131  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']
1132 
1133  $fullpath = dol_buildpath($relpath);
1134 
1135  if (!file_exists($fullpath)) {
1136  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1137  return false;
1138  }
1139 
1140  if (!empty($classname) && !class_exists($classname)) {
1141  return include $fullpath;
1142  } else {
1143  return include_once $fullpath;
1144  }
1145 }
1146 
1147 
1158 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1159 {
1160  global $conf;
1161 
1162  $path = preg_replace('/^\//', '', $path);
1163 
1164  if (empty($type)) { // For a filesystem path
1165  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1166  if (is_array($conf->file->dol_document_root)) {
1167  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1168  if ($key == 'main') {
1169  continue;
1170  }
1171  if (file_exists($dirroot.'/'.$path)) {
1172  $res = $dirroot.'/'.$path;
1173  return $res;
1174  }
1175  }
1176  }
1177  if ($returnemptyifnotfound) {
1178  // Not found into alternate dir
1179  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1180  return '';
1181  }
1182  }
1183  } else {
1184  // For an url path
1185  // We try to get local path of file on filesystem from url
1186  // Note that trying to know if a file on disk exist by forging path on disk from url
1187  // works only for some web server and some setup. This is bugged when
1188  // using proxy, rewriting, virtual path, etc...
1189  $res = '';
1190  if ($type == 1) {
1191  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1192  }
1193  if ($type == 2) {
1194  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1195  }
1196  if ($type == 3) {
1197  $res = DOL_URL_ROOT.'/'.$path;
1198  }
1199 
1200  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1201  if ($key == 'main') {
1202  if ($type == 3) {
1203  global $dolibarr_main_url_root;
1204 
1205  // Define $urlwithroot
1206  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1207  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1208  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1209 
1210  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1211  }
1212  continue;
1213  }
1214  $regs = array();
1215  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1216  if (!empty($regs[1])) {
1217  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1218  if (file_exists($dirroot.'/'.$regs[1])) {
1219  if ($type == 1) {
1220  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1221  }
1222  if ($type == 2) {
1223  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1224  }
1225  if ($type == 3) {
1226  global $dolibarr_main_url_root;
1227 
1228  // Define $urlwithroot
1229  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1230  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1231  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1232 
1233  $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
1234  }
1235  break;
1236  }
1237  }
1238  }
1239  }
1240 
1241  return $res;
1242 }
1243 
1255 function dol_clone($object, $native = 0)
1256 {
1257  if ($native == 0) {
1258  // deprecated method, use the method with native = 2 instead
1259  $tmpsavdb = null;
1260  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1261  $tmpsavdb = $object->db;
1262  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1263  }
1264 
1265  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1266 
1267  if (!empty($tmpsavdb)) {
1268  $object->db = $tmpsavdb;
1269  }
1270  } elseif ($native == 2) {
1271  // recommended method to have a full isolated cloned object
1272  $myclone = new stdClass();
1273  $tmparray = get_object_vars($object); // return only public properties
1274 
1275  if (is_array($tmparray)) {
1276  foreach ($tmparray as $propertykey => $propertyval) {
1277  if (is_scalar($propertyval) || is_array($propertyval)) {
1278  $myclone->$propertykey = $propertyval;
1279  }
1280  }
1281  }
1282  } else {
1283  $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)
1284  }
1285 
1286  return $myclone;
1287 }
1288 
1298 function dol_size($size, $type = '')
1299 {
1300  global $conf;
1301  if (empty($conf->dol_optimize_smallscreen)) {
1302  return $size;
1303  }
1304  if ($type == 'width' && $size > 250) {
1305  return 250;
1306  } else {
1307  return 10;
1308  }
1309 }
1310 
1311 
1323 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1324 {
1325  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1326  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1327  // Char '/' and '\' are file delimiters.
1328  // 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
1329  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1330  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1331  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1332  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1333  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1334  $tmp = str_replace('..', '', $tmp);
1335  return $tmp;
1336 }
1337 
1349 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1350 {
1351  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1352  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1353  // 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
1354  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1355  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1356  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1357  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1358  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1359  $tmp = str_replace('..', '', $tmp);
1360  return $tmp;
1361 }
1362 
1370 function dol_sanitizeUrl($stringtoclean, $type = 1)
1371 {
1372  // 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)
1373  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1374  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1375  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1376  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1377 
1378  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1379  if ($type == 1) {
1380  // removing : should disable links to external url like http:aaa)
1381  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1382  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1383  }
1384 
1385  do {
1386  $oldstringtoclean = $stringtoclean;
1387  // removing '&colon' should disable links to external url like http:aaa)
1388  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1389  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1390  } while ($oldstringtoclean != $stringtoclean);
1391 
1392  if ($type == 1) {
1393  // removing '//' should disable links to external url like //aaa or http//)
1394  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1395  }
1396 
1397  return $stringtoclean;
1398 }
1399 
1406 function dol_sanitizeEmail($stringtoclean)
1407 {
1408  do {
1409  $oldstringtoclean = $stringtoclean;
1410  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1411  } while ($oldstringtoclean != $stringtoclean);
1412 
1413  return $stringtoclean;
1414 }
1415 
1424 function dol_string_unaccent($str)
1425 {
1426  global $conf;
1427 
1428  if (is_null($str)) {
1429  return '';
1430  }
1431 
1432  if (utf8_check($str)) {
1433  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1434  $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1435  return $transliterator->transliterate($str);
1436  }
1437  // See http://www.utf8-chartable.de/
1438  $string = rawurlencode($str);
1439  $replacements = array(
1440  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1441  '%C3%87' => 'C',
1442  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1443  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1444  '%C3%91' => 'N',
1445  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1446  '%C5%A0' => 'S',
1447  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1448  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1449  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1450  '%C3%A7' => 'c',
1451  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1452  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1453  '%C3%B1' => 'n',
1454  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1455  '%C5%A1' => 's',
1456  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1457  '%C3%BD' => 'y', '%C3%BF' => 'y'
1458  );
1459  $string = strtr($string, $replacements);
1460  return rawurldecode($string);
1461  } else {
1462  // See http://www.ascii-code.com/
1463  $string = strtr(
1464  $str,
1465  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1466  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1467  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1468  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1469  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1470  \xF9\xFA\xFB\xFC\xFD\xFF",
1471  "AAAAAAC
1472  EEEEIIIIDN
1473  OOOOOUUUY
1474  aaaaaaceeee
1475  iiiidnooooo
1476  uuuuyy"
1477  );
1478  $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"));
1479  return $string;
1480  }
1481 }
1482 
1496 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1497 {
1498  $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1499  if (empty($keepspaces)) {
1500  $forbidden_chars_to_replace[] = " ";
1501  }
1502  $forbidden_chars_to_remove = array();
1503  //$forbidden_chars_to_remove=array("(",")");
1504 
1505  if (is_array($badcharstoreplace)) {
1506  $forbidden_chars_to_replace = $badcharstoreplace;
1507  }
1508  if (is_array($badcharstoremove)) {
1509  $forbidden_chars_to_remove = $badcharstoremove;
1510  }
1511 
1512  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1513 }
1514 
1515 
1529 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1530 {
1531  if ($removetabcrlf) {
1532  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1533  } else {
1534  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
1535  }
1536 }
1537 
1546 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1547 {
1548  if (is_null($stringtoescape)) {
1549  return '';
1550  }
1551 
1552  // escape quotes and backslashes, newlines, etc.
1553  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1554  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1555  if (empty($noescapebackslashn)) {
1556  $substitjs["\n"] = '\\n';
1557  $substitjs['\\'] = '\\\\';
1558  }
1559  if (empty($mode)) {
1560  $substitjs["'"] = "\\'";
1561  $substitjs['"'] = "\\'";
1562  } elseif ($mode == 1) {
1563  $substitjs["'"] = "\\'";
1564  } elseif ($mode == 2) {
1565  $substitjs['"'] = '\\"';
1566  } elseif ($mode == 3) {
1567  $substitjs["'"] = "\\'";
1568  $substitjs['"'] = "\\\"";
1569  }
1570  return strtr($stringtoescape, $substitjs);
1571 }
1572 
1579 function dol_escape_json($stringtoescape)
1580 {
1581  return str_replace('"', '\"', $stringtoescape);
1582 }
1583 
1600 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1601 {
1602  if ($noescapetags == 'common') {
1603  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1604  }
1605  if ($cleanalsojavascript) {
1606  $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1607  }
1608 
1609  // escape quotes and backslashes, newlines, etc.
1610  if ($escapeonlyhtmltags) {
1611  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1612  } else {
1613  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1614  }
1615  if (!$keepb) {
1616  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1617  }
1618  if (!$keepn) {
1619  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1620  }
1621 
1622  if ($escapeonlyhtmltags) {
1623  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1624  } else {
1625  // Escape tags to keep
1626  // TODO Does not works yet when there is attributes into tag
1627  $tmparrayoftags = array();
1628  if ($noescapetags) {
1629  $tmparrayoftags = explode(',', $noescapetags);
1630  }
1631  if (count($tmparrayoftags)) {
1632  foreach ($tmparrayoftags as $tagtoreplace) {
1633  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1634  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1635  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1636  }
1637  }
1638 
1639  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1640 
1641  if (count($tmparrayoftags)) {
1642  foreach ($tmparrayoftags as $tagtoreplace) {
1643  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1644  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1645  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1646  }
1647  }
1648 
1649  return $result;
1650  }
1651 }
1652 
1660 function dol_strtolower($string, $encoding = "UTF-8")
1661 {
1662  if (function_exists('mb_strtolower')) {
1663  return mb_strtolower($string, $encoding);
1664  } else {
1665  return strtolower($string);
1666  }
1667 }
1668 
1677 function dol_strtoupper($string, $encoding = "UTF-8")
1678 {
1679  if (function_exists('mb_strtoupper')) {
1680  return mb_strtoupper($string, $encoding);
1681  } else {
1682  return strtoupper($string);
1683  }
1684 }
1685 
1694 function dol_ucfirst($string, $encoding = "UTF-8")
1695 {
1696  if (function_exists('mb_substr')) {
1697  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1698  } else {
1699  return ucfirst($string);
1700  }
1701 }
1702 
1711 function dol_ucwords($string, $encoding = "UTF-8")
1712 {
1713  if (function_exists('mb_convert_case')) {
1714  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1715  } else {
1716  return ucwords($string);
1717  }
1718 }
1719 
1741 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1742 {
1743  global $conf, $user, $debugbar;
1744 
1745  // If syslog module enabled
1746  if (!isModEnabled('syslog')) {
1747  return;
1748  }
1749 
1750  // Check if we are into execution of code of a website
1751  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1752  global $website, $websitekey;
1753  if (is_object($website) && !empty($website->ref)) {
1754  $suffixinfilename .= '_website_'.$website->ref;
1755  } elseif (!empty($websitekey)) {
1756  $suffixinfilename .= '_website_'.$websitekey;
1757  }
1758  }
1759 
1760  // Check if we have a forced suffix
1761  if (defined('USESUFFIXINLOG')) {
1762  $suffixinfilename .= constant('USESUFFIXINLOG');
1763  }
1764 
1765  if ($ident < 0) {
1766  foreach ($conf->loghandlers as $loghandlerinstance) {
1767  $loghandlerinstance->setIdent($ident);
1768  }
1769  }
1770 
1771  if (!empty($message)) {
1772  // Test log level
1773  $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');
1774  if (!array_key_exists($level, $logLevels)) {
1775  throw new Exception('Incorrect log level');
1776  }
1777  if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
1778  return;
1779  }
1780 
1781  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1782  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1783  }
1784 
1785  // If adding log inside HTML page is required
1786  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1787  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1788  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1789  }
1790 
1791  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1792  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1793  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1794  print "\n\n<!-- Log start\n";
1795  print dol_escape_htmltag($message)."\n";
1796  print "Log end -->\n";
1797  }
1798 
1799  $data = array(
1800  'message' => $message,
1801  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1802  'level' => $level,
1803  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1804  'ip' => false
1805  );
1806 
1807  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1808  if (!empty($remoteip)) {
1809  $data['ip'] = $remoteip;
1810  // This is when server run behind a reverse proxy
1811  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1812  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1813  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1814  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1815  }
1816  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1817  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1818  $data['ip'] = $_SERVER['SERVER_ADDR'];
1819  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1820  // 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).
1821  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1822  } elseif (!empty($_SERVER['LOGNAME'])) {
1823  // 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).
1824  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1825  }
1826 
1827  // Loop on each log handler and send output
1828  foreach ($conf->loghandlers as $loghandlerinstance) {
1829  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1830  continue;
1831  }
1832  $loghandlerinstance->export($data, $suffixinfilename);
1833  }
1834  unset($data);
1835  }
1836 
1837  if ($ident > 0) {
1838  foreach ($conf->loghandlers as $loghandlerinstance) {
1839  $loghandlerinstance->setIdent($ident);
1840  }
1841  }
1842 }
1843 
1860 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
1861 {
1862  global $conf;
1863 
1864  if (strpos($url, '?') > 0) {
1865  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1866  } else {
1867  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1868  }
1869 
1870  $out = '';
1871 
1872  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1873  if ($backtopagejsfields) {
1874  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1875  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1876  $backtopagejsfields = $name.":".$backtopagejsfields;
1877  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1878  } else {
1879  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1880  }
1881  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1882  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1883  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1884  }
1885 
1886  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1887  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
1888  $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
1889  if (empty($conf->use_javascript_ajax)) {
1890  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
1891  } elseif ($jsonopen) {
1892  $out .= ' href="#" onclick="'.$jsonopen.'"';
1893  } else {
1894  $out .= ' href="#"';
1895  }
1896  $out .= '>'.$buttonstring.'</a>';
1897 
1898  if (!empty($conf->use_javascript_ajax)) {
1899  // Add code to open url using the popup. Add also hidden field to retreive the returned variables
1900  $out .= '<!-- code to open popup and variables to retreive returned variables -->';
1901  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1902  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1903  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1904  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1905  $out .= '<script nonce="'.getNonce().'" type="text/javascript">
1906  jQuery(document).ready(function () {
1907  jQuery(".button_'.$name.'").click(function () {
1908  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1909  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1910  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1911  $tmpdialog.dialog({
1912  autoOpen: false,
1913  modal: true,
1914  height: (window.innerHeight - 150),
1915  width: \'80%\',
1916  title: \''.dol_escape_js($label).'\',
1917  open: function (event, ui) {
1918  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1919  },
1920  close: function (event, ui) {
1921  var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1922  var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1923  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1924  if (returnedid != "" && returnedid != "div for returned id") {
1925  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1926  }
1927  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1928  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1929  }
1930  }
1931  });
1932 
1933  $tmpdialog.dialog(\'open\');
1934  return false;
1935  });
1936  });
1937  </script>';
1938  }
1939  return $out;
1940 }
1941 
1958 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1959 {
1960  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1961 }
1962 
1979 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
1980 {
1981  global $conf, $langs, $hookmanager;
1982 
1983  // Show title
1984  $showtitle = 1;
1985  if (!empty($conf->dol_optimize_smallscreen)) {
1986  $showtitle = 0;
1987  }
1988 
1989  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1990 
1991  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1992  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1993  }
1994 
1995  // Show right part
1996  if ($morehtmlright) {
1997  $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.
1998  }
1999 
2000  // Show title
2001  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2002  $limittitle = 30;
2003  $out .= '<a class="tabTitle">';
2004  if ($picto) {
2005  $noprefix = $pictoisfullpath;
2006  if (strpos($picto, 'fontawesome_') !== false) {
2007  $noprefix = 1;
2008  }
2009  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2010  }
2011  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2012  $out .= '</a>';
2013  }
2014 
2015  // Show tabs
2016 
2017  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2018  $maxkey = -1;
2019  if (is_array($links) && !empty($links)) {
2020  $keys = array_keys($links);
2021  if (count($keys)) {
2022  $maxkey = max($keys);
2023  }
2024  }
2025 
2026  // Show tabs
2027  // if =0 we don't use the feature
2028  if (empty($limittoshow)) {
2029  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2030  }
2031  if (!empty($conf->dol_optimize_smallscreen)) {
2032  $limittoshow = 2;
2033  }
2034 
2035  $displaytab = 0;
2036  $nbintab = 0;
2037  $popuptab = 0;
2038  $outmore = '';
2039  for ($i = 0; $i <= $maxkey; $i++) {
2040  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2041  // If active tab is already present
2042  if ($i >= $limittoshow) {
2043  $limittoshow--;
2044  }
2045  }
2046  }
2047 
2048  for ($i = 0; $i <= $maxkey; $i++) {
2049  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2050  $isactive = true;
2051  } else {
2052  $isactive = false;
2053  }
2054 
2055  if ($i < $limittoshow || $isactive) {
2056  // Output entry with a visible tab
2057  $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])).' -->';
2058 
2059  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2060  if (!empty($links[$i][0])) {
2061  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2062  } else {
2063  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2064  }
2065  } elseif (!empty($links[$i][1])) {
2066  //print "x $i $active ".$links[$i][2]." z";
2067  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
2068  if (!empty($links[$i][0])) {
2069  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2070  $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).'">';
2071  }
2072  $out .= $links[$i][1];
2073  if (!empty($links[$i][0])) {
2074  $out .= '</a>'."\n";
2075  }
2076  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2077  $out .= '</div>';
2078  }
2079 
2080  $out .= '</div>';
2081  } else {
2082  // Add entry into the combo popup with the other tabs
2083  if (!$popuptab) {
2084  $popuptab = 1;
2085  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2086  }
2087  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2088  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2089  if (!empty($links[$i][0])) {
2090  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2091  } else {
2092  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2093  }
2094  } elseif (!empty($links[$i][1])) {
2095  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2096  $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.
2097  $outmore .= '</a>'."\n";
2098  }
2099  $outmore .= '</div>';
2100 
2101  $nbintab++;
2102  }
2103  $displaytab = $i;
2104  }
2105  if ($popuptab) {
2106  $outmore .= '</div>';
2107  }
2108 
2109  if ($popuptab) { // If there is some tabs not shown
2110  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2111  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2112  $widthofpopup = 200;
2113 
2114  $tabsname = $moretabssuffix;
2115  if (empty($tabsname)) {
2116  $tabsname = str_replace("@", "", $picto);
2117  }
2118  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2119  $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".
2120  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2121  $out .= $outmore;
2122  $out .= '</div>';
2123  $out .= '<div></div>';
2124  $out .= "</div>\n";
2125 
2126  $out .= '<script nonce="'.getNonce().'">';
2127  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2128  var x = this.offsetLeft, y = this.offsetTop;
2129  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2130  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2131  $('#moretabsList".$tabsname."').css('".$right."','8px');
2132  }
2133  $('#moretabsList".$tabsname."').css('".$left."','auto');
2134  });
2135  ";
2136  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2137  $out .= "</script>";
2138  }
2139 
2140  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2141  $out .= "</div>\n";
2142  }
2143 
2144  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2145  $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom'))).'">'."\n";
2146  }
2147  if (!empty($dragdropfile)) {
2148  $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2149  }
2150  $parameters = array('tabname' => $active, 'out' => $out);
2151  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2152  if ($reshook > 0) {
2153  $out = $hookmanager->resPrint;
2154  }
2155 
2156  return $out;
2157 }
2158 
2166 function dol_fiche_end($notab = 0)
2167 {
2168  print dol_get_fiche_end($notab);
2169 }
2170 
2177 function dol_get_fiche_end($notab = 0)
2178 {
2179  if (!$notab || $notab == -1) {
2180  return "\n</div>\n";
2181  } else {
2182  return '';
2183  }
2184 }
2185 
2205 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2206 {
2207  global $conf, $form, $user, $langs, $hookmanager, $action;
2208 
2209  $error = 0;
2210 
2211  $maxvisiblephotos = 1;
2212  $showimage = 1;
2213  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2214  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2215  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2216  $showbarcode = 0;
2217  }
2218  $modulepart = 'unknown';
2219 
2220  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2221  $modulepart = $object->element;
2222  } elseif ($object->element == 'member') {
2223  $modulepart = 'memberphoto';
2224  } elseif ($object->element == 'user') {
2225  $modulepart = 'userphoto';
2226  }
2227 
2228  if (class_exists("Imagick")) {
2229  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2230  $modulepart = $object->element;
2231  } elseif ($object->element == 'fichinter') {
2232  $modulepart = 'ficheinter';
2233  } elseif ($object->element == 'contrat') {
2234  $modulepart = 'contract';
2235  } elseif ($object->element == 'order_supplier') {
2236  $modulepart = 'supplier_order';
2237  } elseif ($object->element == 'invoice_supplier') {
2238  $modulepart = 'supplier_invoice';
2239  }
2240  }
2241 
2242  if ($object->element == 'product') {
2243  $width = 80;
2244  $cssclass = 'photowithmargin photoref';
2245  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2246  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2247  if ($conf->browser->layout == 'phone') {
2248  $maxvisiblephotos = 1;
2249  }
2250  if ($showimage) {
2251  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2252  } else {
2253  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2254  $nophoto = '';
2255  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2256  } else { // Show no photo link
2257  $nophoto = '/public/theme/common/nophoto.png';
2258  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" title="'.dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))).'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2259  }
2260  }
2261  } elseif ($object->element == 'ticket') {
2262  $width = 80;
2263  $cssclass = 'photoref';
2264  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2265  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2266  if ($conf->browser->layout == 'phone') {
2267  $maxvisiblephotos = 1;
2268  }
2269 
2270  if ($showimage) {
2271  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2272  if ($object->nbphoto > 0) {
2273  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2274  } else {
2275  $showimage = 0;
2276  }
2277  }
2278  if (!$showimage) {
2279  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2280  $nophoto = '';
2281  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2282  } else { // Show no photo link
2283  $nophoto = img_picto('No photo', 'object_ticket');
2284  $morehtmlleft .= '<!-- No photo to show -->';
2285  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2286  $morehtmlleft .= $nophoto;
2287  $morehtmlleft .= '</div></div>';
2288  }
2289  }
2290  } else {
2291  if ($showimage) {
2292  if ($modulepart != 'unknown') {
2293  $phototoshow = '';
2294  // Check if a preview file is available
2295  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2296  $objectref = dol_sanitizeFileName($object->ref);
2297  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2298  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2299  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2300  $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
2301  } else {
2302  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2303  }
2304  if (empty($subdir)) {
2305  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2306  }
2307 
2308  $filepath = $dir_output.$subdir."/";
2309 
2310  $filepdf = $filepath.$objectref.".pdf";
2311  $relativepath = $subdir.'/'.$objectref.'.pdf';
2312 
2313  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2314  $fileimage = $filepdf.'_preview.png';
2315  $relativepathimage = $relativepath.'_preview.png';
2316 
2317  $pdfexists = file_exists($filepdf);
2318 
2319  // If PDF file exists
2320  if ($pdfexists) {
2321  // Conversion du PDF en image png si fichier png non existant
2322  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2323  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2324  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2325  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2326  if ($ret < 0) {
2327  $error++;
2328  }
2329  }
2330  }
2331  }
2332 
2333  if ($pdfexists && !$error) {
2334  $heightforphotref = 80;
2335  if (!empty($conf->dol_optimize_smallscreen)) {
2336  $heightforphotref = 60;
2337  }
2338  // If the preview file is found
2339  if (file_exists($fileimage)) {
2340  $phototoshow = '<div class="photoref">';
2341  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2342  $phototoshow .= '</div>';
2343  }
2344  }
2345  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2346  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2347  }
2348 
2349  if ($phototoshow) {
2350  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2351  $morehtmlleft .= $phototoshow;
2352  $morehtmlleft .= '</div>';
2353  }
2354  }
2355 
2356  if (empty($phototoshow)) { // Show No photo link (picto of object)
2357  if ($object->element == 'action') {
2358  $width = 80;
2359  $cssclass = 'photorefcenter';
2360  $nophoto = img_picto('No photo', 'title_agenda');
2361  } else {
2362  $width = 14;
2363  $cssclass = 'photorefcenter';
2364  $picto = $object->picto;
2365  $prefix = 'object_';
2366  if ($object->element == 'project' && !$object->public) {
2367  $picto = 'project'; // instead of projectpub
2368  }
2369  if (strpos($picto, 'fontawesome_') !== false) {
2370  $prefix = '';
2371  }
2372  $nophoto = img_picto('No photo', $prefix.$picto);
2373  }
2374  $morehtmlleft .= '<!-- No photo to show -->';
2375  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2376  $morehtmlleft .= $nophoto;
2377  $morehtmlleft .= '</div></div>';
2378  }
2379  }
2380  }
2381 
2382  // Show barcode
2383  if ($showbarcode) {
2384  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2385  }
2386 
2387  if ($object->element == 'societe') {
2388  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2389  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2390  } else {
2391  $morehtmlstatus .= $object->getLibStatut(6);
2392  }
2393  } elseif ($object->element == 'product') {
2394  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2395  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2396  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2397  } else {
2398  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2399  }
2400  $morehtmlstatus .= ' &nbsp; ';
2401  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2402  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2403  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2404  } else {
2405  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2406  }
2407  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2408  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2409  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2410  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2411  }
2412  $morehtmlstatus .= $tmptxt;
2413  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2414  if ($object->statut == 0) {
2415  $morehtmlstatus .= $object->getLibStatut(5);
2416  } else {
2417  $morehtmlstatus .= $object->getLibStatut(4);
2418  }
2419  } elseif ($object->element == 'facturerec') {
2420  if ($object->frequency == 0) {
2421  $morehtmlstatus .= $object->getLibStatut(2);
2422  } else {
2423  $morehtmlstatus .= $object->getLibStatut(5);
2424  }
2425  } elseif ($object->element == 'project_task') {
2426  $object->fk_statut = 1;
2427  if ($object->progress > 0) {
2428  $object->fk_statut = 2;
2429  }
2430  if ($object->progress >= 100) {
2431  $object->fk_statut = 3;
2432  }
2433  $tmptxt = $object->getLibStatut(5);
2434  $morehtmlstatus .= $tmptxt; // No status on task
2435  } elseif (method_exists($object, 'getLibStatut')) { // Generic case
2436  $tmptxt = $object->getLibStatut(6);
2437  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2438  $tmptxt = $object->getLibStatut(5);
2439  }
2440  $morehtmlstatus .= $tmptxt;
2441  }
2442 
2443  // Add if object was dispatched "into accountancy"
2444  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2445  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2446  if (method_exists($object, 'getVentilExportCompta')) {
2447  $accounted = $object->getVentilExportCompta();
2448  $langs->load("accountancy");
2449  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2450  }
2451  }
2452 
2453  // Add alias for thirdparty
2454  if (!empty($object->name_alias)) {
2455  $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
2456  }
2457 
2458  // Add label
2459  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2460  if (!empty($object->label)) {
2461  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2462  }
2463  }
2464 
2465  // Show address and email
2466  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2467  $moreaddress = $object->getBannerAddress('refaddress', $object);
2468  if ($moreaddress) {
2469  $morehtmlref .= '<div class="refidno refaddress">';
2470  $morehtmlref .= $moreaddress;
2471  $morehtmlref .= '</div>';
2472  }
2473  }
2474  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)) {
2475  $morehtmlref .= '<div style="clear: both;"></div>';
2476  $morehtmlref .= '<div class="refidno opacitymedium">';
2477  $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
2478  $morehtmlref .= '</div>';
2479  }
2480 
2481  $parameters=array('morehtmlref'=>$morehtmlref);
2482  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2483  if ($reshook < 0) {
2484  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2485  } elseif (empty($reshook)) {
2486  $morehtmlref .= $hookmanager->resPrint;
2487  } elseif ($reshook > 0) {
2488  $morehtmlref = $hookmanager->resPrint;
2489  }
2490 
2491 
2492  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2493  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2494  print '</div>';
2495  print '<div class="underrefbanner clearboth"></div>';
2496 }
2497 
2507 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2508 {
2509  global $langs;
2510  $ret = '';
2511  if ($fieldrequired) {
2512  $ret .= '<span class="fieldrequired">';
2513  }
2514  $ret .= '<label for="'.$fieldkey.'">';
2515  $ret .= $langs->trans($langkey);
2516  $ret .= '</label>';
2517  if ($fieldrequired) {
2518  $ret .= '</span>';
2519  }
2520  return $ret;
2521 }
2522 
2530 function dol_bc($var, $moreclass = '')
2531 {
2532  global $bc;
2533  $ret = ' '.$bc[$var];
2534  if ($moreclass) {
2535  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2536  }
2537  return $ret;
2538 }
2539 
2553 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2554 {
2555  global $conf, $langs, $hookmanager;
2556 
2557  $ret = '';
2558  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2559 
2560  // See format of addresses on https://en.wikipedia.org/wiki/Address
2561  // Address
2562  if (empty($mode)) {
2563  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/[\n\r]/', $sep, $object->address)));
2564  }
2565  // Zip/Town/State
2566  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2567  // US: title firstname name \n address lines \n town, state, zip \n country
2568  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2569  $ret .= (($ret && $town) ? $sep : '').$town;
2570 
2571  if (!empty($object->state)) {
2572  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2573  }
2574  if (!empty($object->zip)) {
2575  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2576  }
2577  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2578  // UK: title firstname name \n address lines \n town state \n zip \n country
2579  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2580  $ret .= ($ret ? $sep : '').$town;
2581  if (!empty($object->state)) {
2582  $ret .= ($ret ? ", " : '').$object->state;
2583  }
2584  if (!empty($object->zip)) {
2585  $ret .= ($ret ? $sep : '').$object->zip;
2586  }
2587  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2588  // ES: title firstname name \n address lines \n zip town \n state \n country
2589  $ret .= ($ret ? $sep : '').$object->zip;
2590  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2591  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2592  if (!empty($object->state)) {
2593  $ret .= $sep.$object->state;
2594  }
2595  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2596  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2597  // See https://www.sljfaq.org/afaq/addresses.html
2598  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2599  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2600  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2601  // IT: title firstname name\n address lines \n zip town state_code \n country
2602  $ret .= ($ret ? $sep : '').$object->zip;
2603  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2604  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2605  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2606  } else {
2607  // Other: title firstname name \n address lines \n zip town[, state] \n country
2608  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2609  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2610  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2611  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2612  $ret .= ($ret ? ", " : '').$object->state;
2613  }
2614  }
2615 
2616  if (!is_object($outputlangs)) {
2617  $outputlangs = $langs;
2618  }
2619  if ($withcountry) {
2620  $langs->load("dict");
2621  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2622  }
2623  if ($hookmanager) {
2624  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2625  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2626  if ($reshook > 0) {
2627  $ret = '';
2628  }
2629  $ret .= $hookmanager->resPrint;
2630  }
2631 
2632  return $ret;
2633 }
2634 
2635 
2636 
2645 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2646 {
2647  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2648  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2649  } else {
2650  return 'Error date into a not supported range';
2651  }
2652 }
2653 
2675 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2676 {
2677  global $conf, $langs;
2678 
2679  // If date undefined or "", we return ""
2680  if (dol_strlen($time) == 0) {
2681  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2682  }
2683 
2684  if ($tzoutput === 'auto') {
2685  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2686  }
2687 
2688  // Clean parameters
2689  $to_gmt = false;
2690  $offsettz = $offsetdst = 0;
2691  if ($tzoutput) {
2692  $to_gmt = true; // For backward compatibility
2693  if (is_string($tzoutput)) {
2694  if ($tzoutput == 'tzserver') {
2695  $to_gmt = false;
2696  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2697  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
2698  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
2699  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2700  $to_gmt = true;
2701  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2702 
2703  if (class_exists('DateTimeZone')) {
2704  $user_date_tz = new DateTimeZone($offsettzstring);
2705  $user_dt = new DateTime();
2706  $user_dt->setTimezone($user_date_tz);
2707  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2708  $offsettz = $user_dt->getOffset(); // should include dst ?
2709  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
2710  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2711  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2712  }
2713  }
2714  }
2715  }
2716  if (!is_object($outputlangs)) {
2717  $outputlangs = $langs;
2718  }
2719  if (!$format) {
2720  $format = 'daytextshort';
2721  }
2722 
2723  // Do we have to reduce the length of date (year on 2 chars) to save space.
2724  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2725  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2726  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2727  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2728  if ($formatwithoutreduce != $format) {
2729  $format = $formatwithoutreduce;
2730  $reduceformat = 1;
2731  } // so format 'dayreduceformat' is processed like day
2732 
2733  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2734  // TODO Add format daysmallyear and dayhoursmallyear
2735  if ($format == 'day') {
2736  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2737  } elseif ($format == 'hour') {
2738  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2739  } elseif ($format == 'hourduration') {
2740  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2741  } elseif ($format == 'daytext') {
2742  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2743  } elseif ($format == 'daytextshort') {
2744  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2745  } elseif ($format == 'dayhour') {
2746  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2747  } elseif ($format == 'dayhoursec') {
2748  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2749  } elseif ($format == 'dayhourtext') {
2750  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2751  } elseif ($format == 'dayhourtextshort') {
2752  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2753  } elseif ($format == 'dayhourlog') {
2754  // Format not sensitive to language
2755  $format = '%Y%m%d%H%M%S';
2756  } elseif ($format == 'dayhourlogsmall') {
2757  // Format not sensitive to language
2758  $format = '%y%m%d%H%M';
2759  } elseif ($format == 'dayhourldap') {
2760  $format = '%Y%m%d%H%M%SZ';
2761  } elseif ($format == 'dayhourxcard') {
2762  $format = '%Y%m%dT%H%M%SZ';
2763  } elseif ($format == 'dayxcard') {
2764  $format = '%Y%m%d';
2765  } elseif ($format == 'dayrfc') {
2766  $format = '%Y-%m-%d'; // DATE_RFC3339
2767  } elseif ($format == 'dayhourrfc') {
2768  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2769  } elseif ($format == 'standard') {
2770  $format = '%Y-%m-%d %H:%M:%S';
2771  }
2772 
2773  if ($reduceformat) {
2774  $format = str_replace('%Y', '%y', $format);
2775  $format = str_replace('yyyy', 'yy', $format);
2776  }
2777 
2778  // Clean format
2779  if (preg_match('/%b/i', $format)) { // There is some text to translate
2780  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2781  $format = str_replace('%b', '__b__', $format);
2782  $format = str_replace('%B', '__B__', $format);
2783  }
2784  if (preg_match('/%a/i', $format)) { // There is some text to translate
2785  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2786  $format = str_replace('%a', '__a__', $format);
2787  $format = str_replace('%A', '__A__', $format);
2788  }
2789 
2790  // Analyze date
2791  $reg = array();
2792  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
2793  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2794  return '';
2795  } 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
2796  // This part of code should not be used anymore.
2797  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2798  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2799  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2800  $syear = (!empty($reg[1]) ? $reg[1] : '');
2801  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2802  $sday = (!empty($reg[3]) ? $reg[3] : '');
2803  $shour = (!empty($reg[4]) ? $reg[4] : '');
2804  $smin = (!empty($reg[5]) ? $reg[5] : '');
2805  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2806 
2807  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2808 
2809  if ($to_gmt) {
2810  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2811  } else {
2812  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2813  }
2814  $dtts = new DateTime();
2815  $dtts->setTimestamp($time);
2816  $dtts->setTimezone($tzo);
2817  $newformat = str_replace(
2818  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2819  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2820  $format);
2821  $ret = $dtts->format($newformat);
2822  $ret = str_replace(
2823  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2824  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2825  $ret
2826  );
2827  } else {
2828  // Date is a timestamps
2829  if ($time < 100000000000) { // Protection against bad date values
2830  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2831 
2832  if ($to_gmt) {
2833  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2834  } else {
2835  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2836  }
2837  $dtts = new DateTime();
2838  $dtts->setTimestamp($timetouse);
2839  $dtts->setTimezone($tzo);
2840  $newformat = str_replace(
2841  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2842  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2843  $format);
2844  $ret = $dtts->format($newformat);
2845  $ret = str_replace(
2846  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2847  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2848  $ret
2849  );
2850  //var_dump($ret);exit;
2851  } else {
2852  $ret = 'Bad value '.$time.' for date';
2853  }
2854  }
2855 
2856  if (preg_match('/__b__/i', $format)) {
2857  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2858 
2859  if ($to_gmt) {
2860  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2861  } else {
2862  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2863  }
2864  $dtts = new DateTime();
2865  $dtts->setTimestamp($timetouse);
2866  $dtts->setTimezone($tzo);
2867  $month = $dtts->format("m");
2868  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2869  if ($encodetooutput) {
2870  $monthtext = $outputlangs->transnoentities('Month'.$month);
2871  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2872  } else {
2873  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2874  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2875  }
2876  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2877  $ret = str_replace('__b__', $monthtextshort, $ret);
2878  $ret = str_replace('__B__', $monthtext, $ret);
2879  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2880  //return $ret;
2881  }
2882  if (preg_match('/__a__/i', $format)) {
2883  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2884  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2885 
2886  if ($to_gmt) {
2887  $tzo = new DateTimeZone('UTC');
2888  } else {
2889  $tzo = new DateTimeZone(date_default_timezone_get());
2890  }
2891  $dtts = new DateTime();
2892  $dtts->setTimestamp($timetouse);
2893  $dtts->setTimezone($tzo);
2894  $w = $dtts->format("w");
2895  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2896 
2897  $ret = str_replace('__A__', $dayweek, $ret);
2898  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2899  }
2900 
2901  return $ret;
2902 }
2903 
2904 
2925 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2926 {
2927  $datetimeobj = new DateTime();
2928  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2929  if ($forcetimezone) {
2930  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2931  }
2932  $arrayinfo = array(
2933  'year'=>((int) date_format($datetimeobj, 'Y')),
2934  'mon'=>((int) date_format($datetimeobj, 'm')),
2935  'mday'=>((int) date_format($datetimeobj, 'd')),
2936  'wday'=>((int) date_format($datetimeobj, 'w')),
2937  'yday'=>((int) date_format($datetimeobj, 'z')),
2938  'hours'=>((int) date_format($datetimeobj, 'H')),
2939  'minutes'=>((int) date_format($datetimeobj, 'i')),
2940  'seconds'=>((int) date_format($datetimeobj, 's')),
2941  '0'=>$timestamp
2942  );
2943 
2944  return $arrayinfo;
2945 }
2946 
2968 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2969 {
2970  global $conf;
2971  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2972 
2973  if ($gm === 'auto') {
2974  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2975  }
2976  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2977 
2978  // Clean parameters
2979  if ($hour == -1 || empty($hour)) {
2980  $hour = 0;
2981  }
2982  if ($minute == -1 || empty($minute)) {
2983  $minute = 0;
2984  }
2985  if ($second == -1 || empty($second)) {
2986  $second = 0;
2987  }
2988 
2989  // Check parameters
2990  if ($check) {
2991  if (!$month || !$day) {
2992  return '';
2993  }
2994  if ($day > 31) {
2995  return '';
2996  }
2997  if ($month > 12) {
2998  return '';
2999  }
3000  if ($hour < 0 || $hour > 24) {
3001  return '';
3002  }
3003  if ($minute < 0 || $minute > 60) {
3004  return '';
3005  }
3006  if ($second < 0 || $second > 60) {
3007  return '';
3008  }
3009  }
3010 
3011  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3012  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3013  $localtz = new DateTimeZone($default_timezone);
3014  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3015  // We use dol_tz_string first because it is more reliable.
3016  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3017  try {
3018  $localtz = new DateTimeZone($default_timezone);
3019  } catch (Exception $e) {
3020  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
3021  $default_timezone = @date_default_timezone_get();
3022  }
3023  } elseif (strrpos($gm, "tz,") !== false) {
3024  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3025  try {
3026  $localtz = new DateTimeZone($timezone);
3027  } catch (Exception $e) {
3028  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3029  }
3030  }
3031 
3032  if (empty($localtz)) {
3033  $localtz = new DateTimeZone('UTC');
3034  }
3035  //var_dump($localtz);
3036  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3037  $dt = new DateTime('now', $localtz);
3038  $dt->setDate((int) $year, (int) $month, (int) $day);
3039  $dt->setTime((int) $hour, (int) $minute, (int) $second);
3040  $date = $dt->getTimestamp(); // should include daylight saving time
3041  //var_dump($date);
3042  return $date;
3043 }
3044 
3045 
3056 function dol_now($mode = 'auto')
3057 {
3058  $ret = 0;
3059 
3060  if ($mode === 'auto') {
3061  $mode = 'gmt';
3062  }
3063 
3064  if ($mode == 'gmt') {
3065  $ret = time(); // Time for now at greenwich.
3066  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3067  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3068  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3069  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3070  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3071  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3072  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3073  // $ret=dol_now('gmt')+($tzsecond*3600);
3074  //}
3075  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3076  // Time for now with user timezone added
3077  //print 'time: '.time();
3078  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3079  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3080  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3081  }
3082 
3083  return $ret;
3084 }
3085 
3086 
3095 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3096 {
3097  global $conf, $langs;
3098  $level = 1024;
3099 
3100  if (!empty($conf->dol_optimize_smallscreen)) {
3101  $shortunit = 1;
3102  }
3103 
3104  // Set value text
3105  if (empty($shortvalue) || $size < ($level * 10)) {
3106  $ret = $size;
3107  $textunitshort = $langs->trans("b");
3108  $textunitlong = $langs->trans("Bytes");
3109  } else {
3110  $ret = round($size / $level, 0);
3111  $textunitshort = $langs->trans("Kb");
3112  $textunitlong = $langs->trans("KiloBytes");
3113  }
3114  // Use long or short text unit
3115  if (empty($shortunit)) {
3116  $ret .= ' '.$textunitlong;
3117  } else {
3118  $ret .= ' '.$textunitshort;
3119  }
3120 
3121  return $ret;
3122 }
3123 
3134 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = 'float')
3135 {
3136  global $langs;
3137 
3138  if (empty($url)) {
3139  return '';
3140  }
3141 
3142  $link = '<a href="';
3143  if (!preg_match('/^http/i', $url)) {
3144  $link .= 'http://';
3145  }
3146  $link .= $url;
3147  $link .= '"';
3148  if ($target) {
3149  $link .= ' target="'.$target.'"';
3150  }
3151  $link .= '>';
3152  if (!preg_match('/^http/i', $url)) {
3153  $link .= 'http://';
3154  }
3155  $link .= dol_trunc($url, $max);
3156  $link .= '</a>';
3157 
3158  if ($morecss == 'float') {
3159  return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
3160  } else {
3161  return '<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</span>';
3162  }
3163 }
3164 
3177 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3178 {
3179  global $conf, $user, $langs, $hookmanager;
3180 
3181  $newemail = dol_escape_htmltag($email);
3182 
3183  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
3184  $withpicto = 0;
3185  }
3186 
3187  if (empty($email)) {
3188  return '&nbsp;';
3189  }
3190 
3191  if (!empty($addlink)) {
3192  $newemail = '<a style="text-overflow: ellipsis;" href="';
3193  if (!preg_match('/^mailto:/i', $email)) {
3194  $newemail .= 'mailto:';
3195  }
3196  $newemail .= $email;
3197  $newemail .= '">';
3198  $newemail .= dol_trunc($email, $max);
3199  $newemail .= '</a>';
3200  if ($showinvalid && !isValidEmail($email)) {
3201  $langs->load("errors");
3202  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3203  }
3204 
3205  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3206  $type = 'AC_EMAIL';
3207  $link = '';
3208  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
3209  $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>';
3210  }
3211  if ($link) {
3212  $newemail = '<div>'.$newemail.' '.$link.'</div>';
3213  }
3214  }
3215  } else {
3216  if ($showinvalid && !isValidEmail($email)) {
3217  $langs->load("errors");
3218  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3219  }
3220  }
3221 
3222  //$rep = '<div class="nospan" style="margin-right: 10px">';
3223  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3224  //$rep .= '</div>';
3225  if ($hookmanager) {
3226  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3227 
3228  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3229  if ($reshook > 0) {
3230  $rep = '';
3231  }
3232  $rep .= $hookmanager->resPrint;
3233  }
3234 
3235  return $rep;
3236 }
3237 
3244 {
3245  global $conf, $db;
3246 
3247  $socialnetworks = array();
3248  // Enable caching of array
3249  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3250  $cachekey = 'socialnetworks_' . $conf->entity;
3251  $dataretrieved = dol_getcache($cachekey);
3252  if (!is_null($dataretrieved)) {
3253  $socialnetworks = $dataretrieved;
3254  } else {
3255  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3256  $sql .= " WHERE entity=".$conf->entity;
3257  $resql = $db->query($sql);
3258  if ($resql) {
3259  while ($obj = $db->fetch_object($resql)) {
3260  $socialnetworks[$obj->code] = array(
3261  'rowid' => $obj->rowid,
3262  'label' => $obj->label,
3263  'url' => $obj->url,
3264  'icon' => $obj->icon,
3265  'active' => $obj->active,
3266  );
3267  }
3268  }
3269  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3270  }
3271 
3272  return $socialnetworks;
3273 }
3274 
3285 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3286 {
3287  global $conf, $user, $langs;
3288 
3289  $htmllink = $value;
3290 
3291  if (empty($value)) {
3292  return '&nbsp;';
3293  }
3294 
3295  if (!empty($type)) {
3296  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3297  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3298  $htmllink .= '<span class="fa pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3299  if ($type == 'skype') {
3300  $htmllink .= dol_escape_htmltag($value);
3301  $htmllink .= '&nbsp; <a href="skype:';
3302  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3303  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3304  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3305  $htmllink .= '</a><a href="skype:';
3306  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3307  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3308  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3309  $htmllink .= '</a>';
3310  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3311  $addlink = 'AC_SKYPE';
3312  $link = '';
3313  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3314  $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>';
3315  }
3316  $htmllink .= ($link ? ' '.$link : '');
3317  }
3318  } else {
3319  if (!empty($dictsocialnetworks[$type]['url'])) {
3320  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3321  if ($tmpvirginurl) {
3322  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3323  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3324 
3325  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3326  if ($tmpvirginurl3) {
3327  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3328  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3329  }
3330 
3331  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3332  if ($tmpvirginurl2) {
3333  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3334  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3335  }
3336  }
3337  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3338  if (preg_match('/^https?:\/\//i', $link)) {
3339  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3340  } else {
3341  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3342  }
3343  } else {
3344  $htmllink .= dol_escape_htmltag($value);
3345  }
3346  }
3347  $htmllink .= '</div>';
3348  } else {
3349  $langs->load("errors");
3350  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3351  }
3352  return $htmllink;
3353 }
3354 
3365 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3366 {
3367  global $mysoc;
3368 
3369  if (empty($profID) || empty($profIDtype)) {
3370  return '';
3371  }
3372  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3373  $newProfID = $profID;
3374  $id = substr($profIDtype, -1);
3375  $ret = '';
3376  if (strtoupper($countrycode) == 'FR') {
3377  // France
3378  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3379  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);
3380  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);
3381  }
3382  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3383  else $ret = $newProfID;
3384  return $ret;
3385 }
3386 
3401 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3402 {
3403  global $conf, $user, $langs, $mysoc, $hookmanager;
3404 
3405  // Clean phone parameter
3406  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3407  if (empty($phone)) {
3408  return '';
3409  }
3410  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3411  $separ = $conf->global->MAIN_PHONE_SEPAR;
3412  }
3413  if (empty($countrycode) && is_object($mysoc)) {
3414  $countrycode = $mysoc->country_code;
3415  }
3416 
3417  // Short format for small screens
3418  if ($conf->dol_optimize_smallscreen) {
3419  $separ = '';
3420  }
3421 
3422  $newphone = $phone;
3423  if (strtoupper($countrycode) == "FR") {
3424  // France
3425  if (dol_strlen($phone) == 10) {
3426  $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);
3427  } elseif (dol_strlen($phone) == 7) {
3428  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3429  } elseif (dol_strlen($phone) == 9) {
3430  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3431  } elseif (dol_strlen($phone) == 11) {
3432  $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);
3433  } elseif (dol_strlen($phone) == 12) {
3434  $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);
3435  } elseif (dol_strlen($phone) == 13) {
3436  $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);
3437  }
3438  } elseif (strtoupper($countrycode) == "CA") {
3439  if (dol_strlen($phone) == 10) {
3440  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3441  }
3442  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3443  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3444  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3445  }
3446  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3447  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3448  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3449  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3450  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3451  }
3452  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3453  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3454  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3455  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3456  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3457  }
3458  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3459  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3460  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3461  }
3462  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3463  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3464  $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);
3465  }
3466  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3467  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3468  $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);
3469  }
3470  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3471  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3472  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3473  }
3474  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3475  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3476  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3477  }
3478  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3479  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3480  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3481  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3482  $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);
3483  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3484  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3485  }
3486  } elseif (strtoupper($countrycode) == "ML") {//Mali
3487  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3488  $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);
3489  }
3490  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3491  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3492  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3493  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3494  $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);
3495  }
3496  } elseif (strtoupper($countrycode) == "MU") {
3497  //Maurice
3498  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3499  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3500  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3501  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3502  }
3503  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3504  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3505  $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);
3506  }
3507  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3508  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3509  $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);
3510  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3511  $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);
3512  }
3513  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3514  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3515  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3516  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3517  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3518  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3519  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3520  }
3521  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3522  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3523  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3524  }
3525  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3526  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3527  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3528  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3529  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3530  }
3531  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3532  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3533  $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);
3534  }
3535  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3536  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3537  $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);
3538  }
3539  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3540  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3541  $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);
3542  }
3543  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3544  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3545  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3546  }
3547  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3548  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3549  $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);
3550  }
3551  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3552  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3553  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3554  }
3555  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3556  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3557  $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);
3558  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3559  $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);
3560  }
3561  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3562  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3563  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3564  }
3565  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3566  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3567  $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);
3568  }
3569  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3570  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3571  $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);
3572  }
3573  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3574  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3575  $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);
3576  }
3577  } elseif (strtoupper($countrycode) == "IT") {//Italie
3578  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3579  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3580  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3581  $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);
3582  }
3583  } elseif (strtoupper($countrycode) == "AU") {
3584  //Australie
3585  if (dol_strlen($phone) == 12) {
3586  //ex: +61_A_BCDE_FGHI
3587  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3588  }
3589  } elseif (strtoupper($countrycode) == "LU") {
3590  // Luxembourg
3591  if (dol_strlen($phone) == 10) {// fixe 6 chiffres +352_AA_BB_CC
3592  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
3593  } elseif (dol_strlen($phone) == 11) {// fixe 7 chiffres +352_AA_BB_CC_D
3594  $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);
3595  } elseif (dol_strlen($phone) == 12) {// fixe 8 chiffres +352_AA_BB_CC_DD
3596  $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);
3597  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
3598  $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);
3599  }
3600  }
3601  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3602  if ($addlink == 'tel' || $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
3603  $newphoneform = $newphone;
3604  $newphone = '<a href="tel:'.$phone.'"';
3605  $newphone .= '>'.$newphoneform.'</a>';
3606  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3607  if (empty($user->clicktodial_loaded)) {
3608  $user->fetch_clicktodial();
3609  }
3610 
3611  // Define urlmask
3612  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3613  if (!empty($conf->global->CLICKTODIAL_URL)) {
3614  $urlmask = $conf->global->CLICKTODIAL_URL;
3615  }
3616  if (!empty($user->clicktodial_url)) {
3617  $urlmask = $user->clicktodial_url;
3618  }
3619 
3620  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3621  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3622  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3623  // This line is for backward compatibility
3624  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3625  // Thoose lines are for substitution
3626  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3627  '__PHONETO__'=>urlencode($phone),
3628  '__LOGIN__'=>$clicktodial_login,
3629  '__PASS__'=>$clicktodial_password);
3630  $url = make_substitutions($url, $substitarray);
3631  $newphonesav = $newphone;
3632  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3633  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3634  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3635  $newphone .= '>'.$newphonesav.'</a>';
3636  } else {
3637  // Old method
3638  $newphone = '<a href="'.$url.'"';
3639  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3640  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3641  }
3642  $newphone .= '>'.$newphonesav.'</a>';
3643  }
3644  }
3645 
3646  //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
3647  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3648  $type = 'AC_TEL';
3649  $link = '';
3650  if ($addlink == 'AC_FAX') {
3651  $type = 'AC_FAX';
3652  }
3653  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3654  $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>';
3655  }
3656  if ($link) {
3657  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3658  }
3659  }
3660  }
3661 
3662  if (empty($titlealt)) {
3663  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3664  }
3665  $rep = '';
3666 
3667  if ($hookmanager) {
3668  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3669  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3670  $rep .= $hookmanager->resPrint;
3671  }
3672  if (empty($reshook)) {
3673  $picto = '';
3674  if ($withpicto) {
3675  if ($withpicto == 'fax') {
3676  $picto = 'phoning_fax';
3677  } elseif ($withpicto == 'phone') {
3678  $picto = 'phoning';
3679  } elseif ($withpicto == 'mobile') {
3680  $picto = 'phoning_mobile';
3681  } else {
3682  $picto = '';
3683  }
3684  }
3685  if ($adddivfloat == 1) {
3686  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3687  } elseif (empty($adddivfloat)) {
3688  $rep .= '<span style="margin-right: 10px;">';
3689  }
3690  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3691  if ($adddivfloat == 1) {
3692  $rep .= '</div>';
3693  } elseif (empty($adddivfloat)) {
3694  $rep .= '</span>';
3695  }
3696  }
3697 
3698  return $rep;
3699 }
3700 
3708 function dol_print_ip($ip, $mode = 0)
3709 {
3710  global $conf, $langs;
3711 
3712  $ret = '';
3713 
3714  if (empty($mode)) {
3715  $ret .= $ip;
3716  }
3717 
3718  if ($mode != 2) {
3719  $countrycode = dolGetCountryCodeFromIp($ip);
3720  if ($countrycode) { // If success, countrycode is us, fr, ...
3721  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3722  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3723  } else {
3724  $ret .= ' ('.$countrycode.')';
3725  }
3726  } else {
3727  // Nothing
3728  }
3729  }
3730 
3731  return $ret;
3732 }
3733 
3743 {
3744  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3745  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3746  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3747  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3748  } else {
3749  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3750  }
3751  } else {
3752  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3753  }
3754  } else {
3755  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3756  }
3757  return $ip;
3758 }
3759 
3768 function isHTTPS()
3769 {
3770  $isSecure = false;
3771  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3772  $isSecure = true;
3773  } 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') {
3774  $isSecure = true;
3775  }
3776  return $isSecure;
3777 }
3778 
3786 {
3787  global $conf;
3788 
3789  $countrycode = '';
3790 
3791  if (!empty($conf->geoipmaxmind->enabled)) {
3792  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3793  //$ip='24.24.24.24';
3794  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3795  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3796  $geoip = new DolGeoIP('country', $datafile);
3797  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3798  $countrycode = $geoip->getCountryCodeFromIP($ip);
3799  }
3800 
3801  return $countrycode;
3802 }
3803 
3804 
3812 {
3813  global $conf, $langs, $user;
3814 
3815  //$ret=$user->xxx;
3816  $ret = '';
3817  if (!empty($conf->geoipmaxmind->enabled)) {
3818  $ip = getUserRemoteIP();
3819  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3820  //$ip='24.24.24.24';
3821  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3822  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3823  $geoip = new DolGeoIP('country', $datafile);
3824  $countrycode = $geoip->getCountryCodeFromIP($ip);
3825  $ret = $countrycode;
3826  }
3827  return $ret;
3828 }
3829 
3842 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3843 {
3844  global $conf, $user, $langs, $hookmanager;
3845 
3846  $out = '';
3847 
3848  if ($address) {
3849  if ($hookmanager) {
3850  $parameters = array('element' => $element, 'id' => $id);
3851  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3852  $out .= $hookmanager->resPrint;
3853  }
3854  if (empty($reshook)) {
3855  if (empty($charfornl)) {
3856  $out .= nl2br($address);
3857  } else {
3858  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3859  }
3860 
3861  // TODO Remove this block, we can add this using the hook now
3862  $showgmap = $showomap = 0;
3863  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3864  $showgmap = 1;
3865  }
3866  if ($element == 'contact' && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3867  $showgmap = 1;
3868  }
3869  if ($element == 'member' && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3870  $showgmap = 1;
3871  }
3872  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3873  $showomap = 1;
3874  }
3875  if ($element == 'contact' && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3876  $showomap = 1;
3877  }
3878  if ($element == 'member' && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3879  $showomap = 1;
3880  }
3881  if ($showgmap) {
3882  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3883  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3884  }
3885  if ($showomap) {
3886  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3887  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3888  }
3889  }
3890  }
3891  if ($noprint) {
3892  return $out;
3893  } else {
3894  print $out;
3895  }
3896 }
3897 
3898 
3908 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3909 {
3910  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3911  return true;
3912  }
3913  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3914  return true;
3915  }
3916  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3917  return true;
3918  }
3919 
3920  return false;
3921 }
3922 
3931 function isValidMXRecord($domain)
3932 {
3933  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3934  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3935  return 0;
3936  }
3937  if (function_exists('getmxrr')) {
3938  $mxhosts = array();
3939  $weight = array();
3940  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3941  if (count($mxhosts) > 1) {
3942  return 1;
3943  }
3944  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3945  return 1;
3946  }
3947 
3948  return 0;
3949  }
3950  }
3951 
3952  // function idn_to_ascii or checkdnsrr or getmxrr does not exists
3953  return -1;
3954 }
3955 
3963 function isValidPhone($phone)
3964 {
3965  return true;
3966 }
3967 
3968 
3978 function dolGetFirstLetters($s, $nbofchar = 1)
3979 {
3980  $ret = '';
3981  $tmparray = explode(' ', $s);
3982  foreach ($tmparray as $tmps) {
3983  $ret .= dol_substr($tmps, 0, $nbofchar);
3984  }
3985 
3986  return $ret;
3987 }
3988 
3989 
3997 function dol_strlen($string, $stringencoding = 'UTF-8')
3998 {
3999  if (is_null($string)) {
4000  return 0;
4001  }
4002 
4003  if (function_exists('mb_strlen')) {
4004  return mb_strlen($string, $stringencoding);
4005  } else {
4006  return strlen($string);
4007  }
4008 }
4009 
4020 function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4021 {
4022  global $langs;
4023 
4024  if (empty($stringencoding)) {
4025  $stringencoding = $langs->charset_output;
4026  }
4027 
4028  $ret = '';
4029  if (empty($trunconbytes)) {
4030  if (function_exists('mb_substr')) {
4031  $ret = mb_substr($string, $start, $length, $stringencoding);
4032  } else {
4033  $ret = substr($string, $start, $length);
4034  }
4035  } else {
4036  if (function_exists('mb_strcut')) {
4037  $ret = mb_strcut($string, $start, $length, $stringencoding);
4038  } else {
4039  $ret = substr($string, $start, $length);
4040  }
4041  }
4042  return $ret;
4043 }
4044 
4045 
4059 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4060 {
4061  global $conf;
4062 
4063  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
4064  return $string;
4065  }
4066 
4067  if (empty($stringencoding)) {
4068  $stringencoding = 'UTF-8';
4069  }
4070  // reduce for small screen
4071  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
4072  $size = round($size / 3);
4073  }
4074 
4075  // We go always here
4076  if ($trunc == 'right') {
4077  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4078  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4079  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4080  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4081  } else {
4082  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4083  return $string;
4084  }
4085  } elseif ($trunc == 'middle') {
4086  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4087  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4088  $size1 = round($size / 2);
4089  $size2 = round($size / 2);
4090  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4091  } else {
4092  return $string;
4093  }
4094  } elseif ($trunc == 'left') {
4095  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4096  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4097  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4098  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4099  } else {
4100  return $string;
4101  }
4102  } elseif ($trunc == 'wrap') {
4103  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4104  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4105  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4106  } else {
4107  return $string;
4108  }
4109  } else {
4110  return 'BadParam3CallingDolTrunc';
4111  }
4112 }
4113 
4135 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4136 {
4137  global $conf, $langs;
4138 
4139  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4140  $url = DOL_URL_ROOT;
4141  $theme = isset($conf->theme) ? $conf->theme : null;
4142  $path = 'theme/'.$theme;
4143  // Define fullpathpicto to use into src
4144  if ($pictoisfullpath) {
4145  // Clean parameters
4146  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4147  $picto .= '.png';
4148  }
4149  $fullpathpicto = $picto;
4150  $reg = array();
4151  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4152  $morecss .= ($morecss ? ' ' : '').$reg[1];
4153  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4154  }
4155  } else {
4156  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
4157  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4158  $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4159 
4160  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
4161  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
4162  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4163  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4164 
4165  $pictowithouttextarray = explode('_', $pictowithouttext);
4166  $marginleftonlyshort = 0;
4167 
4168  if (!empty($pictowithouttextarray[1])) {
4169  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4170  $fakey = 'fa-'.$pictowithouttextarray[0];
4171  $fa = empty($pictowithouttextarray[1]) ? 'fa' : $pictowithouttextarray[1];
4172  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4173  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4174  } else {
4175  $fakey = 'fa-'.$pictowithouttext;
4176  $fa = 'fa';
4177  $facolor = '';
4178  $fasize = '';
4179  }
4180 
4181  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4182  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4183  $morestyle = '';
4184  $reg = array();
4185  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4186  $morecss .= ($morecss ? ' ' : '').$reg[1];
4187  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4188  }
4189  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4190  $morestyle = $reg[1];
4191  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4192  }
4193  $moreatt = trim($moreatt);
4194 
4195  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4196  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4197  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4198  $enabledisablehtml .= $titlealt;
4199  }*/
4200  $enabledisablehtml .= '</span>';
4201 
4202  return $enabledisablehtml;
4203  }
4204 
4205  if (empty($srconly) && in_array($pictowithouttext, array(
4206  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4207  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
4208  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
4209  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4210  'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4211  'currency', 'multicurrency',
4212  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4213  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4214  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
4215  'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4216  'hands-helping', 'help', 'holiday',
4217  'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4218  'knowledgemanagement',
4219  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4220  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4221  'off', 'on', 'order',
4222  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4223  'stock', 'resize', 'service', 'stats', 'trip',
4224  'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_warning', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
4225  'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4226  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4227  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4228  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4229  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4230  'technic', 'ticket',
4231  'error', 'warning',
4232  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4233  'shapes', 'skill', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4234  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
4235  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4236  'conferenceorbooth', 'eventorganization',
4237  'stamp', 'signature'
4238  ))) {
4239  $fakey = $pictowithouttext;
4240  $facolor = '';
4241  $fasize = '';
4242  $fa = 'fas';
4243  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'))) {
4244  $fa = 'far';
4245  }
4246  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4247  $fa = 'fab';
4248  }
4249 
4250  $arrayconvpictotofa = array(
4251  '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',
4252  'bank_account'=>'university',
4253  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
4254  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
4255  'bom'=>'shapes',
4256  'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time', 'cross'=>'times',
4257  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
4258  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
4259  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4260  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4261  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4262  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4263  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4264  'generic'=>'file', 'holiday'=>'umbrella-beach',
4265  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'jobprofile'=>'cogs',
4266  'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4267  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4268  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4269  'sign-out'=>'sign-out-alt',
4270  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_warning'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4271  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4272  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4273  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4274  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4275  'other'=>'square',
4276  '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',
4277  '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',
4278  'recent' => 'check-square', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4279  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4280  'refresh'=>'redo', 'region'=>'map-marked', 'replacement'=>'exchange-alt', 'resource'=>'laptop-house', 'recurring'=>'history',
4281  'service'=>'concierge-bell',
4282  'skill'=>'shapes', 'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4283  'supplier'=>'building', 'technic'=>'cogs',
4284  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4285  'title_agenda'=>'calendar-alt',
4286  'uncheck'=>'times', 'uparrow'=>'share', 'url'=>'external-link-alt', 'vat'=>'money-check-alt', 'vcard'=>'arrow-alt-circle-down',
4287  'jabber'=>'comment-o',
4288  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4289  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4290  );
4291  if ($pictowithouttext == 'off') {
4292  $fakey = 'fa-square';
4293  $fasize = '1.3em';
4294  } elseif ($pictowithouttext == 'on') {
4295  $fakey = 'fa-check-square';
4296  $fasize = '1.3em';
4297  } elseif ($pictowithouttext == 'listlight') {
4298  $fakey = 'fa-download';
4299  $marginleftonlyshort = 1;
4300  } elseif ($pictowithouttext == 'printer') {
4301  $fakey = 'fa-print';
4302  $fasize = '1.2em';
4303  } elseif ($pictowithouttext == 'note') {
4304  $fakey = 'fa-sticky-note';
4305  $marginleftonlyshort = 1;
4306  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4307  $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');
4308  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4309  if (preg_match('/selected/', $pictowithouttext)) {
4310  $facolor = '#888';
4311  }
4312  $marginleftonlyshort = 1;
4313  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4314  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4315  } else {
4316  $fakey = 'fa-'.$pictowithouttext;
4317  }
4318 
4319  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4320  $morecss .= ' em092';
4321  }
4322  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4323  $morecss .= ' em088';
4324  }
4325  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4326  $morecss .= ' em080';
4327  }
4328 
4329  // Define $marginleftonlyshort
4330  $arrayconvpictotomarginleftonly = array(
4331  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4332  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4333  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4334  );
4335  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4336  $marginleftonlyshort = 0;
4337  }
4338 
4339  // Add CSS
4340  $arrayconvpictotomorcess = array(
4341  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4342  'bank_account'=>'infobox-bank_account',
4343  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4344  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4345  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4346  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4347  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4348  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4349  'incoterm'=>'infobox-supplier_proposal',
4350  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4351  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4352  'order'=>'infobox-commande',
4353  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4354  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_warning'=>'font-status4 warning', 'switch_on_red'=>'font-status8',
4355  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4356  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4357  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4358  'propal'=>'infobox-propal', 'proposal'=>'infobox-propal','private'=>'infobox-project',
4359  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4360  'resource'=>'infobox-action',
4361  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4362  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4363  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4364  'vat'=>'infobox-bank_account',
4365  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4366  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4367  );
4368  if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4369  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4370  }
4371 
4372  // Define $color
4373  $arrayconvpictotocolor = array(
4374  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4375  'clone'=>'#999', 'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4376  'dynamicprice'=>'#a69944',
4377  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4378  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4379  'lock'=>'#ddd', 'lot'=>'#a69944',
4380  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4381  'other'=>'#ddd', 'world'=>'#986c6a',
4382  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4383  //'shipment'=>'#a69944',
4384  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4385  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4386  'website'=>'#304', 'workstation'=>'#a69944'
4387  );
4388  if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4389  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4390  }
4391 
4392  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4393  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4394  $morestyle = '';
4395  $reg = array();
4396  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4397  $morecss .= ($morecss ? ' ' : '').$reg[1];
4398  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4399  }
4400  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4401  $morestyle = $reg[1];
4402  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4403  }
4404  $moreatt = trim($moreatt);
4405 
4406  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4407  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4408  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4409  $enabledisablehtml .= $titlealt;
4410  }*/
4411  $enabledisablehtml .= '</span>';
4412 
4413  return $enabledisablehtml;
4414  }
4415 
4416  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4417  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4418  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4419  $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
4420  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4421  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4422  }
4423 
4424  // If we ask an image into $url/$mymodule/img (instead of default path)
4425  $regs = array();
4426  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4427  $picto = $regs[1];
4428  $path = $regs[2]; // $path is $mymodule
4429  }
4430 
4431  // Clean parameters
4432  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4433  $picto .= '.png';
4434  }
4435  // If alt path are defined, define url where img file is, according to physical path
4436  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4437  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4438  if ($type == 'main') {
4439  continue;
4440  }
4441  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4442  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4443  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4444  break;
4445  }
4446  }
4447 
4448  // $url is '' or '/custom', $path is current theme or
4449  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4450  }
4451 
4452  if ($srconly) {
4453  return $fullpathpicto;
4454  }
4455 
4456  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4457  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
4458 }
4459 
4473 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4474 {
4475  if (strpos($picto, '^') === 0) {
4476  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4477  } else {
4478  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4479  }
4480 }
4481 
4493 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4494 {
4495  global $conf;
4496 
4497  if (is_numeric($picto)) {
4498  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4499  //$picto = $leveltopicto[$picto];
4500  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4501  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4502  $picto .= '.png';
4503  }
4504 
4505  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4506 
4507  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4508 }
4509 
4521 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4522 {
4523  global $conf;
4524 
4525  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4526  $picto .= '.png';
4527  }
4528 
4529  if ($pictoisfullpath) {
4530  $path = $picto;
4531  } else {
4532  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4533 
4534  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4535  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4536 
4537  if (file_exists($themepath)) {
4538  $path = $themepath;
4539  }
4540  }
4541  }
4542 
4543  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4544 }
4545 
4559 function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
4560 {
4561  global $langs;
4562 
4563  if (empty($titlealt) || $titlealt == 'default') {
4564  if ($numaction == '-1' || $numaction == 'ST_NO') {
4565  $numaction = -1;
4566  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4567  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4568  $numaction = 0;
4569  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4570  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4571  $numaction = 1;
4572  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4573  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4574  $numaction = 2;
4575  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4576  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4577  $numaction = 3;
4578  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4579  } else {
4580  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4581  $numaction = 0;
4582  }
4583  }
4584  if (!is_numeric($numaction)) {
4585  $numaction = 0;
4586  }
4587 
4588  return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
4589 }
4590 
4598 function img_pdf($titlealt = 'default', $size = 3)
4599 {
4600  global $langs;
4601 
4602  if ($titlealt == 'default') {
4603  $titlealt = $langs->trans('Show');
4604  }
4605 
4606  return img_picto($titlealt, 'pdf'.$size.'.png');
4607 }
4608 
4616 function img_edit_add($titlealt = 'default', $other = '')
4617 {
4618  global $langs;
4619 
4620  if ($titlealt == 'default') {
4621  $titlealt = $langs->trans('Add');
4622  }
4623 
4624  return img_picto($titlealt, 'edit_add.png', $other);
4625 }
4633 function img_edit_remove($titlealt = 'default', $other = '')
4634 {
4635  global $langs;
4636 
4637  if ($titlealt == 'default') {
4638  $titlealt = $langs->trans('Remove');
4639  }
4640 
4641  return img_picto($titlealt, 'edit_remove.png', $other);
4642 }
4643 
4652 function img_edit($titlealt = 'default', $float = 0, $other = '')
4653 {
4654  global $langs;
4655 
4656  if ($titlealt == 'default') {
4657  $titlealt = $langs->trans('Modify');
4658  }
4659 
4660  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4661 }
4662 
4671 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4672 {
4673  global $langs;
4674 
4675  if ($titlealt == 'default') {
4676  $titlealt = $langs->trans('View');
4677  }
4678 
4679  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4680 
4681  return img_picto($titlealt, 'eye', $moreatt);
4682 }
4683 
4692 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4693 {
4694  global $langs;
4695 
4696  if ($titlealt == 'default') {
4697  $titlealt = $langs->trans('Delete');
4698  }
4699 
4700  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4701 }
4702 
4710 function img_printer($titlealt = "default", $other = '')
4711 {
4712  global $langs;
4713  if ($titlealt == "default") {
4714  $titlealt = $langs->trans("Print");
4715  }
4716  return img_picto($titlealt, 'printer.png', $other);
4717 }
4718 
4726 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4727 {
4728  global $langs;
4729 
4730  if ($titlealt == 'default') {
4731  $titlealt = $langs->trans('Split');
4732  }
4733 
4734  return img_picto($titlealt, 'split.png', $other);
4735 }
4736 
4744 function img_help($usehelpcursor = 1, $usealttitle = 1)
4745 {
4746  global $langs;
4747 
4748  if ($usealttitle) {
4749  if (is_string($usealttitle)) {
4750  $usealttitle = dol_escape_htmltag($usealttitle);
4751  } else {
4752  $usealttitle = $langs->trans('Info');
4753  }
4754  }
4755 
4756  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4757 }
4758 
4765 function img_info($titlealt = 'default')
4766 {
4767  global $langs;
4768 
4769  if ($titlealt == 'default') {
4770  $titlealt = $langs->trans('Informations');
4771  }
4772 
4773  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4774 }
4775 
4784 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4785 {
4786  global $langs;
4787 
4788  if ($titlealt == 'default') {
4789  $titlealt = $langs->trans('Warning');
4790  }
4791 
4792  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4793  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4794 }
4795 
4802 function img_error($titlealt = 'default')
4803 {
4804  global $langs;
4805 
4806  if ($titlealt == 'default') {
4807  $titlealt = $langs->trans('Error');
4808  }
4809 
4810  return img_picto($titlealt, 'error.png');
4811 }
4812 
4820 function img_next($titlealt = 'default', $moreatt = '')
4821 {
4822  global $langs;
4823 
4824  if ($titlealt == 'default') {
4825  $titlealt = $langs->trans('Next');
4826  }
4827 
4828  //return img_picto($titlealt, 'next.png', $moreatt);
4829  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4830 }
4831 
4839 function img_previous($titlealt = 'default', $moreatt = '')
4840 {
4841  global $langs;
4842 
4843  if ($titlealt == 'default') {
4844  $titlealt = $langs->trans('Previous');
4845  }
4846 
4847  //return img_picto($titlealt, 'previous.png', $moreatt);
4848  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4849 }
4850 
4859 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4860 {
4861  global $langs;
4862 
4863  if ($titlealt == 'default') {
4864  $titlealt = $langs->trans('Down');
4865  }
4866 
4867  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4868 }
4869 
4878 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4879 {
4880  global $langs;
4881 
4882  if ($titlealt == 'default') {
4883  $titlealt = $langs->trans('Up');
4884  }
4885 
4886  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4887 }
4888 
4897 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4898 {
4899  global $langs;
4900 
4901  if ($titlealt == 'default') {
4902  $titlealt = $langs->trans('Left');
4903  }
4904 
4905  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4906 }
4907 
4916 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4917 {
4918  global $langs;
4919 
4920  if ($titlealt == 'default') {
4921  $titlealt = $langs->trans('Right');
4922  }
4923 
4924  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4925 }
4926 
4934 function img_allow($allow, $titlealt = 'default')
4935 {
4936  global $langs;
4937 
4938  if ($titlealt == 'default') {
4939  $titlealt = $langs->trans('Active');
4940  }
4941 
4942  if ($allow == 1) {
4943  return img_picto($titlealt, 'tick.png');
4944  }
4945 
4946  return '-';
4947 }
4948 
4956 function img_credit_card($brand, $morecss = null)
4957 {
4958  if (is_null($morecss)) {
4959  $morecss = 'fa-2x';
4960  }
4961 
4962  if ($brand == 'visa' || $brand == 'Visa') {
4963  $brand = 'cc-visa';
4964  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4965  $brand = 'cc-mastercard';
4966  } elseif ($brand == 'amex' || $brand == 'American Express') {
4967  $brand = 'cc-amex';
4968  } elseif ($brand == 'discover' || $brand == 'Discover') {
4969  $brand = 'cc-discover';
4970  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4971  $brand = 'cc-jcb';
4972  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4973  $brand = 'cc-diners-club';
4974  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4975  $brand = 'credit-card';
4976  }
4977 
4978  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4979 }
4980 
4989 function img_mime($file, $titlealt = '', $morecss = '')
4990 {
4991  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4992 
4993  $mimetype = dol_mimetype($file, '', 1);
4994  $mimeimg = dol_mimetype($file, '', 2);
4995  $mimefa = dol_mimetype($file, '', 4);
4996 
4997  if (empty($titlealt)) {
4998  $titlealt = 'Mime type: '.$mimetype;
4999  }
5000 
5001  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5002  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5003 }
5004 
5005 
5013 function img_search($titlealt = 'default', $other = '')
5014 {
5015  global $conf, $langs;
5016 
5017  if ($titlealt == 'default') {
5018  $titlealt = $langs->trans('Search');
5019  }
5020 
5021  $img = img_picto($titlealt, 'search.png', $other, false, 1);
5022 
5023  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5024  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5025 
5026  return $input;
5027 }
5028 
5036 function img_searchclear($titlealt = 'default', $other = '')
5037 {
5038  global $conf, $langs;
5039 
5040  if ($titlealt == 'default') {
5041  $titlealt = $langs->trans('Search');
5042  }
5043 
5044  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
5045 
5046  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5047  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5048 
5049  return $input;
5050 }
5051 
5063 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
5064 {
5065  global $conf, $langs;
5066 
5067  if ($infoonimgalt) {
5068  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5069  } else {
5070  if (empty($conf->use_javascript_ajax)) {
5071  $textfordropdown = '';
5072  }
5073 
5074  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5075  $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>');
5076 
5077  if ($textfordropdown) {
5078  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5079  $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5080  jQuery(document).ready(function() {
5081  jQuery(".'.$class.'text").click(function() {
5082  console.log("toggle text");
5083  jQuery(".'.$class.'").toggle();
5084  });
5085  });
5086  </script>';
5087 
5088  $result = $tmpresult.$result;
5089  }
5090  }
5091 
5092  return $result;
5093 }
5094 
5095 
5107 function dol_print_error($db = '', $error = '', $errors = null)
5108 {
5109  global $conf, $langs, $argv;
5110  global $dolibarr_main_prod;
5111 
5112  $out = '';
5113  $syslog = '';
5114 
5115  // If error occurs before the $lang object was loaded
5116  if (!$langs) {
5117  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5118  $langs = new Translate('', $conf);
5119  $langs->load("main");
5120  }
5121 
5122  // Load translation files required by the error messages
5123  $langs->loadLangs(array('main', 'errors'));
5124 
5125  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5126  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5127  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5128  $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";
5129  }
5130  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5131 
5132  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5133  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5134  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5135  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5136  }
5137  if (function_exists("phpversion")) {
5138  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5139  }
5140  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5141  if (function_exists("php_uname")) {
5142  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5143  }
5144  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5145  $out .= "<br>\n";
5146  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5147  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5148  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5149  $out .= "<br>\n";
5150  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5151  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5152  } else { // Mode CLI
5153  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5154  $syslog .= "pid=".dol_getmypid();
5155  }
5156 
5157  if (!empty($conf->modules)) {
5158  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
5159  }
5160 
5161  if (is_object($db)) {
5162  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5163  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5164  $lastqueryerror = $db->lastqueryerror();
5165  if (!utf8_check($lastqueryerror)) {
5166  $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5167  }
5168  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5169  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5170  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5171  $out .= "<br>\n";
5172  } else { // Mode CLI
5173  // No dol_escape_htmltag for output, we are in CLI mode
5174  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5175  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5176  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5177  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5178  }
5179  $syslog .= ", sql=".$db->lastquery();
5180  $syslog .= ", db_error=".$db->lasterror();
5181  }
5182 
5183  if ($error || $errors) {
5184  $langs->load("errors");
5185 
5186  // Merge all into $errors array
5187  if (is_array($error) && is_array($errors)) {
5188  $errors = array_merge($error, $errors);
5189  } elseif (is_array($error)) {
5190  $errors = $error;
5191  } elseif (is_array($errors)) {
5192  $errors = array_merge(array($error), $errors);
5193  } else {
5194  $errors = array_merge(array($error), array($errors));
5195  }
5196 
5197  foreach ($errors as $msg) {
5198  if (empty($msg)) {
5199  continue;
5200  }
5201  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5202  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5203  } else // Mode CLI
5204  {
5205  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5206  }
5207  $syslog .= ", msg=".$msg;
5208  }
5209  }
5210  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5211  xdebug_print_function_stack();
5212  $out .= '<b>XDebug informations:</b>'."<br>\n";
5213  $out .= 'File: '.xdebug_call_file()."<br>\n";
5214  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5215  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5216  $out .= "<br>\n";
5217  }
5218 
5219  // Return a http header with error code if possible
5220  if (!headers_sent()) {
5221  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5222  top_httphead();
5223  }
5224  //http_response_code(500); // If we use 500, message is not ouput with some command line tools
5225  http_response_code(202); // If we use 202, this is not really an error message, but this allow to ouput message on command line tools
5226  }
5227 
5228  if (empty($dolibarr_main_prod)) {
5229  print $out;
5230  } else {
5231  if (empty($langs->defaultlang)) {
5232  $langs->setDefaultLang();
5233  }
5234  $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.
5235  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5236  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";
5237  print $langs->trans("DolibarrHasDetectedError").'. ';
5238  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5239  define("MAIN_CORE_ERROR", 1);
5240  }
5241 
5242  dol_syslog("Error ".$syslog, LOG_ERR);
5243 }
5244 
5255 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5256 {
5257  global $langs, $conf;
5258 
5259  if (empty($email)) {
5260  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
5261  }
5262 
5263  $langs->load("errors");
5264  $now = dol_now();
5265 
5266  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5267  print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
5268  if ($errormessage) {
5269  print '<br><br>'.$errormessage;
5270  }
5271  if (is_array($errormessages) && count($errormessages)) {
5272  foreach ($errormessages as $mesgtoshow) {
5273  print '<br><br>'.$mesgtoshow;
5274  }
5275  }
5276  print '</div></div>';
5277 }
5278 
5295 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5296 {
5297  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5298 }
5299 
5318 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5319 {
5320  global $conf, $langs, $form;
5321  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5322 
5323  if ($moreattrib == 'class="right"') {
5324  $prefix .= 'right '; // For backward compatibility
5325  }
5326 
5327  $sortorder = strtoupper($sortorder);
5328  $out = '';
5329  $sortimg = '';
5330 
5331  $tag = 'th';
5332  if ($thead == 2) {
5333  $tag = 'div';
5334  }
5335 
5336  $tmpsortfield = explode(',', $sortfield);
5337  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5338  $tmpfield = explode(',', $field);
5339  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5340 
5341  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5342  $prefix = 'wrapcolumntitle '.$prefix;
5343  }
5344 
5345  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5346  // If field is used as sort criteria we use a specific css class liste_titre_sel
5347  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5348  $liste_titre = 'liste_titre';
5349  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5350  $liste_titre = 'liste_titre_sel';
5351  }
5352 
5353  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5354  //$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)).'"' : '');
5355  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5356  $tagstart .= '>';
5357 
5358  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5359  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5360  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5361  $options = preg_replace('/&+/i', '&', $options);
5362  if (!preg_match('/^&/', $options)) {
5363  $options = '&'.$options;
5364  }
5365 
5366  $sortordertouseinlink = '';
5367  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5368  if (preg_match('/^DESC/i', $sortorder)) {
5369  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5370  } else { // We reverse the var $sortordertouseinlink
5371  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5372  }
5373  } else { // We are on field that is the first current sorting criteria
5374  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5375  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5376  } else {
5377  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5378  }
5379  }
5380  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5381  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5382  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5383  $out .= '>';
5384  }
5385  if ($tooltip) {
5386  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
5387  if (preg_match('/:\w+$/', $tooltip)) {
5388  $tmptooltip = explode(':', $tooltip);
5389  } else {
5390  $tmptooltip = array($tooltip);
5391  }
5392  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5393  } else {
5394  $out .= $langs->trans($name);
5395  }
5396 
5397  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5398  $out .= '</a>';
5399  }
5400 
5401  if (empty($thead) && $field) { // If this is a sort field
5402  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5403  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5404  $options = preg_replace('/&+/i', '&', $options);
5405  if (!preg_match('/^&/', $options)) {
5406  $options = '&'.$options;
5407  }
5408 
5409  if (!$sortorder || ($field1 != $sortfield1)) {
5410  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5411  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5412  } else {
5413  if (preg_match('/^DESC/', $sortorder)) {
5414  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5415  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5416  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5417  }
5418  if (preg_match('/^ASC/', $sortorder)) {
5419  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5420  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5421  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5422  }
5423  }
5424  }
5425 
5426  $tagend = '</'.$tag.'>';
5427 
5428  $out = $tagstart.$sortimg.$out.$tagend;
5429 
5430  return $out;
5431 }
5432 
5441 function print_titre($title)
5442 {
5443  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5444 
5445  print '<div class="titre">'.$title.'</div>';
5446 }
5447 
5459 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5460 {
5461  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5462 }
5463 
5477 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5478 {
5479  global $conf;
5480 
5481  $return = '';
5482 
5483  if ($picto == 'setup') {
5484  $picto = 'generic';
5485  }
5486 
5487  $return .= "\n";
5488  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5489  $return .= '<tr class="titre">';
5490  if ($picto) {
5491  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5492  }
5493  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5494  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5495  $return .= '</td>';
5496  if (dol_strlen($morehtmlcenter)) {
5497  $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5498  }
5499  if (dol_strlen($morehtmlright)) {
5500  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
5501  }
5502  $return .= '</tr></table>'."\n";
5503 
5504  return $return;
5505 }
5506 
5530 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 = '')
5531 {
5532  global $conf, $langs;
5533 
5534  $savlimit = $limit;
5535  $savtotalnboflines = $totalnboflines;
5536  $totalnboflines = abs((int) $totalnboflines);
5537 
5538  $page = (int) $page;
5539 
5540  if ($picto == 'setup') {
5541  $picto = 'title_setup.png';
5542  }
5543  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5544  $picto = 'title.gif';
5545  }
5546  if ($limit < 0) {
5547  $limit = $conf->liste_limit;
5548  }
5549 
5550  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5551  $nextpage = 1;
5552  } else {
5553  $nextpage = 0;
5554  }
5555  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
5556 
5557  print "\n";
5558  print "<!-- Begin title -->\n";
5559  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5560 
5561  // Left
5562 
5563  if ($picto && $titre) {
5564  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5565  }
5566 
5567  print '<td class="nobordernopadding valignmiddle col-title">';
5568  print '<div class="titre inline-block">'.$titre;
5569  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5570  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5571  }
5572  print '</div></td>';
5573 
5574  // Center
5575  if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
5576  print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5577  }
5578 
5579  // Right
5580  print '<td class="nobordernopadding valignmiddle right col-right">';
5581  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5582  if ($sortfield) {
5583  $options .= "&sortfield=".urlencode($sortfield);
5584  }
5585  if ($sortorder) {
5586  $options .= "&sortorder=".urlencode($sortorder);
5587  }
5588  // Show navigation bar
5589  $pagelist = '';
5590  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5591  if ($totalnboflines) { // If we know total nb of lines
5592  // Define nb of extra page links before and after selected page + ... + first or last
5593  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5594 
5595  if ($limit > 0) {
5596  $nbpages = ceil($totalnboflines / $limit);
5597  } else {
5598  $nbpages = 1;
5599  }
5600  $cpt = ($page - $maxnbofpage);
5601  if ($cpt < 0) {
5602  $cpt = 0;
5603  }
5604 
5605  if ($cpt >= 1) {
5606  if (empty($pagenavastextinput)) {
5607  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5608  if ($cpt > 2) {
5609  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5610  } elseif ($cpt == 2) {
5611  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5612  }
5613  }
5614  }
5615 
5616  do {
5617  if ($pagenavastextinput) {
5618  if ($cpt == $page) {
5619  $pagelist .= '<li class="pagination"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5620  $pagelist .= '/';
5621  }
5622  } else {
5623  if ($cpt == $page) {
5624  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5625  } else {
5626  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5627  }
5628  }
5629  $cpt++;
5630  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5631 
5632  if (empty($pagenavastextinput)) {
5633  if ($cpt < $nbpages) {
5634  if ($cpt < $nbpages - 2) {
5635  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5636  } elseif ($cpt == $nbpages - 2) {
5637  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5638  }
5639  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5640  }
5641  } else {
5642  //var_dump($page.' '.$cpt.' '.$nbpages);
5643  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5644  }
5645  } else {
5646  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5647  }
5648  }
5649 
5650  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5651  print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit, $morehtmlrightbeforearrow, $hidenavigation); // output the div and ul for previous/last completed with page numbers into $pagelist
5652  }
5653 
5654  // js to autoselect page field on focus
5655  if ($pagenavastextinput) {
5656  print ajax_autoselect('.pageplusone');
5657  }
5658 
5659  print '</td>';
5660  print '</tr>';
5661 
5662  print '</table>'."\n";
5663 
5664  // Center
5665  if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
5666  print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
5667  }
5668 
5669  print "<!-- End title -->\n\n";
5670 }
5671 
5688 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
5689 {
5690  global $conf, $langs;
5691 
5692  print '<div class="pagination"><ul>';
5693  if ($beforearrows) {
5694  print '<li class="paginationbeforearrows">';
5695  print $beforearrows;
5696  print '</li>';
5697  }
5698 
5699  if (empty($hidenavigation)) {
5700  if ((int) $limit > 0 && empty($hideselectlimit)) {
5701  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5702  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5703  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5704  //$pagesizechoices.=',2:2';
5705  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5706  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5707  }
5708 
5709  print '<li class="pagination">';
5710  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5711  $tmpchoice = explode(',', $pagesizechoices);
5712  $tmpkey = $limit.':'.$limit;
5713  if (!in_array($tmpkey, $tmpchoice)) {
5714  $tmpchoice[] = $tmpkey;
5715  }
5716  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5717  if (!in_array($tmpkey, $tmpchoice)) {
5718  $tmpchoice[] = $tmpkey;
5719  }
5720  asort($tmpchoice, SORT_NUMERIC);
5721  foreach ($tmpchoice as $val) {
5722  $selected = '';
5723  $tmp = explode(':', $val);
5724  $key = $tmp[0];
5725  $val = $tmp[1];
5726  if ($key != '' && $val != '') {
5727  if ((int) $key == (int) $limit) {
5728  $selected = ' selected="selected"';
5729  }
5730  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5731  }
5732  }
5733  print '</select>';
5734  if ($conf->use_javascript_ajax) {
5735  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5736  <script>
5737  jQuery(document).ready(function () {
5738  jQuery(".selectlimit").change(function() {
5739  console.log("Change limit. Send submit");
5740  $(this).parents(\'form:first\').submit();
5741  });
5742  });
5743  </script>
5744  ';
5745  }
5746  print '</li>';
5747  }
5748  if ($page > 0) {
5749  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>';
5750  }
5751  if ($betweenarrows) {
5752  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5753  print $betweenarrows;
5754  print '<!--</div>-->';
5755  }
5756  if ($nextpage > 0) {
5757  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>';
5758  }
5759  if ($afterarrows) {
5760  print '<li class="paginationafterarrows">';
5761  print $afterarrows;
5762  print '</li>';
5763  }
5764  }
5765  print '</ul></div>'."\n";
5766 }
5767 
5768 
5780 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5781 {
5782  $morelabel = '';
5783 
5784  if (preg_match('/%/', $rate)) {
5785  $rate = str_replace('%', '', $rate);
5786  $addpercent = true;
5787  }
5788  $reg = array();
5789  if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
5790  $morelabel = ' ('.$reg[1].')';
5791  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5792  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5793  }
5794  if (preg_match('/\*/', $rate)) {
5795  $rate = str_replace('*', '', $rate);
5796  $info_bits |= 1;
5797  }
5798 
5799  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5800  if (!preg_match('/\//', $rate)) {
5801  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5802  } else {
5803  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5804  $ret = $rate.($addpercent ? '%' : '');
5805  }
5806  if (($info_bits & 1) && $usestarfornpr >= 0) {
5807  $ret .= ' *';
5808  }
5809  $ret .= $morelabel;
5810  return $ret;
5811 }
5812 
5813 
5829 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5830 {
5831  global $langs, $conf;
5832 
5833  // Clean parameters
5834  if (empty($amount)) {
5835  $amount = 0; // To have a numeric value if amount not defined or = ''
5836  }
5837  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5838  if ($rounding == -1) {
5839  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5840  }
5841  $nbdecimal = $rounding;
5842 
5843  if ($outlangs === 'none') {
5844  // Use international separators
5845  $dec = '.';
5846  $thousand = '';
5847  } else {
5848  // Output separators by default (french)
5849  $dec = ',';
5850  $thousand = ' ';
5851 
5852  // If $outlangs not forced, we use use language
5853  if (!is_object($outlangs)) {
5854  $outlangs = $langs;
5855  }
5856 
5857  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5858  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5859  }
5860  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5861  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5862  }
5863  if ($thousand == 'None') {
5864  $thousand = '';
5865  } elseif ($thousand == 'Space') {
5866  $thousand = ' ';
5867  }
5868  }
5869  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5870 
5871  //print "amount=".$amount."-";
5872  $amount = str_replace(',', '.', $amount); // should be useless
5873  //print $amount."-";
5874  $datas = explode('.', $amount);
5875  $decpart = isset($datas[1]) ? $datas[1] : '';
5876  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5877  //print "decpart=".$decpart."<br>";
5878  $end = '';
5879 
5880  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5881  if (dol_strlen($decpart) > $nbdecimal) {
5882  $nbdecimal = dol_strlen($decpart);
5883  }
5884  // Si on depasse max
5885  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5886  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5887  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5888  // Si un affichage est tronque, on montre des ...
5889  $end = '...';
5890  }
5891  }
5892 
5893  // If force rounding
5894  if ((string) $forcerounding != '-1') {
5895  if ($forcerounding === 'MU') {
5896  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5897  } elseif ($forcerounding === 'MT') {
5898  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_TOT;
5899  } elseif ($forcerounding >= 0) {
5900  $nbdecimal = $forcerounding;
5901  }
5902  }
5903 
5904  // Format number
5905  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5906  if ($form) {
5907  $output = preg_replace('/\s/', '&nbsp;', $output);
5908  $output = preg_replace('/\'/', '&#039;', $output);
5909  }
5910  // Add symbol of currency if requested
5911  $cursymbolbefore = $cursymbolafter = '';
5912  if ($currency_code && is_object($outlangs)) {
5913  if ($currency_code == 'auto') {
5914  $currency_code = $conf->currency;
5915  }
5916 
5917  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC');
5918  $listoflanguagesbefore = array('nl_NL');
5919  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5920  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5921  } else {
5922  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5923  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5924  }
5925  }
5926  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5927 
5928  return $output;
5929 }
5930 
5955 function price2num($amount, $rounding = '', $option = 0)
5956 {
5957  global $langs, $conf;
5958 
5959  // Clean parameters
5960  if (is_null($amount)) {
5961  $amount = '';
5962  }
5963 
5964  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5965  // Numbers must be '1234.56'
5966  // Decimal delimiter for PHP and database SQL requests must be '.'
5967  $dec = ',';
5968  $thousand = ' ';
5969  if (is_null($langs)) { // $langs is not defined, we use english values.
5970  $dec = '.';
5971  $thousand = ',';
5972  } else {
5973  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5974  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5975  }
5976  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5977  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5978  }
5979  }
5980  if ($thousand == 'None') {
5981  $thousand = '';
5982  } elseif ($thousand == 'Space') {
5983  $thousand = ' ';
5984  }
5985  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5986 
5987  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5988  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5989  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5990  if (!is_numeric($amount)) {
5991  $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
5992  }
5993 
5994  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
5995  $amount = str_replace($thousand, '', $amount);
5996  }
5997 
5998  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5999  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6000  // So if number was already a good number, it is converted into local Dolibarr setup.
6001  if (is_numeric($amount)) {
6002  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6003  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6004  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6005  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6006  $amount = number_format($amount, $nbofdec, $dec, $thousand);
6007  }
6008  //print "QQ".$amount."<br>\n";
6009 
6010  // Now make replace (the main goal of function)
6011  if ($thousand != ',' && $thousand != '.') {
6012  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6013  }
6014 
6015  $amount = str_replace(' ', '', $amount); // To avoid spaces
6016  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6017  $amount = str_replace($dec, '.', $amount);
6018 
6019  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6020  }
6021  //print ' XX'.$amount.' '.$rounding;
6022 
6023  // Now, $amount is a real PHP float number. We make a rounding if required.
6024  if ($rounding) {
6025  $nbofdectoround = '';
6026  if ($rounding == 'MU') {
6027  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
6028  } elseif ($rounding == 'MT') {
6029  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
6030  } elseif ($rounding == 'MS') {
6031  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6032  } elseif ($rounding == 'CU') {
6033  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
6034  } elseif ($rounding == 'CT') {
6035  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
6036  } elseif (is_numeric($rounding)) {
6037  $nbofdectoround = (int) $rounding;
6038  }
6039 
6040  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6041  if (dol_strlen($nbofdectoround)) {
6042  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6043  } else {
6044  return 'ErrorBadParameterProvidedToFunction';
6045  }
6046  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6047 
6048  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6049  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6050  if (is_numeric($amount)) {
6051  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6052  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6053  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6054  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6055  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6056  }
6057  //print "TT".$amount.'<br>';
6058 
6059  // Always make replace because each math function (like round) replace
6060  // with local values and we want a number that has a SQL string format x.y
6061  if ($thousand != ',' && $thousand != '.') {
6062  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6063  }
6064 
6065  $amount = str_replace(' ', '', $amount); // To avoid spaces
6066  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6067  $amount = str_replace($dec, '.', $amount);
6068 
6069  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6070  }
6071 
6072  return $amount;
6073 }
6074 
6087 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6088 {
6089  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6090 
6091  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6092  $dimension = $dimension * 1000000;
6093  $unit = $unit - 6;
6094  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6095  $dimension = $dimension * 1000;
6096  $unit = $unit - 3;
6097  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6098  $dimension = $dimension / 1000000;
6099  $unit = $unit + 6;
6100  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6101  $dimension = $dimension / 1000;
6102  $unit = $unit + 3;
6103  }
6104  // Special case when we want output unit into pound or ounce
6105  /* TODO
6106  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6107  {
6108  $dimension = // convert dimension from standard unit into ounce or pound
6109  $unit = $forceunitoutput;
6110  }
6111  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6112  {
6113  $dimension = // convert dimension from standard unit into ounce or pound
6114  $unit = $forceunitoutput;
6115  }*/
6116 
6117  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6118  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6119 
6120  return $ret;
6121 }
6122 
6123 
6136 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
6137 {
6138  global $db, $conf, $mysoc;
6139 
6140  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6141  $thirdparty_seller = $mysoc;
6142  }
6143 
6144  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);
6145 
6146  $vatratecleaned = $vatrate;
6147  $reg = array();
6148  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6149  $vatratecleaned = trim($reg[1]);
6150  $vatratecode = $reg[2];
6151  }
6152 
6153  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6154  {
6155  return 0;
6156  }*/
6157 
6158  // Some test to guess with no need to make database access
6159  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6160  if ($local == 1) {
6161  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6162  return 0;
6163  }
6164  if ($thirdparty_seller->id == $mysoc->id) {
6165  if (!$thirdparty_buyer->localtax1_assuj) {
6166  return 0;
6167  }
6168  } else {
6169  if (!$thirdparty_seller->localtax1_assuj) {
6170  return 0;
6171  }
6172  }
6173  }
6174 
6175  if ($local == 2) {
6176  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6177  if (!$mysoc->localtax2_assuj) {
6178  return 0; // If main vat is 0, IRPF may be different than 0.
6179  }
6180  if ($thirdparty_seller->id == $mysoc->id) {
6181  if (!$thirdparty_buyer->localtax2_assuj) {
6182  return 0;
6183  }
6184  } else {
6185  if (!$thirdparty_seller->localtax2_assuj) {
6186  return 0;
6187  }
6188  }
6189  }
6190  } else {
6191  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6192  return 0;
6193  }
6194  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6195  return 0;
6196  }
6197  }
6198 
6199  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6200  if (in_array($mysoc->country_code, array('ES'))) {
6201  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6202  }
6203 
6204  // Search local taxes
6205  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
6206  if ($local == 1) {
6207  if ($thirdparty_seller != $mysoc) {
6208  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6209  return $thirdparty_seller->localtax1_value;
6210  }
6211  } else { // i am the seller
6212  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6213  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6214  }
6215  }
6216  }
6217  if ($local == 2) {
6218  if ($thirdparty_seller != $mysoc) {
6219  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6220  // TODO We should also return value defined on thirdparty only if defined
6221  return $thirdparty_seller->localtax2_value;
6222  }
6223  } else { // i am the seller
6224  if (in_array($mysoc->country_code, array('ES'))) {
6225  return $thirdparty_buyer->localtax2_value;
6226  } else {
6227  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6228  }
6229  }
6230  }
6231  }
6232 
6233  // By default, search value of local tax on line of common tax
6234  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6235  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6236  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6237  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6238  if (!empty($vatratecode)) {
6239  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6240  } else {
6241  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
6242  }
6243 
6244  $resql = $db->query($sql);
6245 
6246  if ($resql) {
6247  $obj = $db->fetch_object($resql);
6248  if ($obj) {
6249  if ($local == 1) {
6250  return $obj->localtax1;
6251  } elseif ($local == 2) {
6252  return $obj->localtax2;
6253  }
6254  }
6255  }
6256 
6257  return 0;
6258 }
6259 
6260 
6269 function isOnlyOneLocalTax($local)
6270 {
6271  $tax = get_localtax_by_third($local);
6272 
6273  $valors = explode(":", $tax);
6274 
6275  if (count($valors) > 1) {
6276  return false;
6277  } else {
6278  return true;
6279  }
6280 }
6281 
6288 function get_localtax_by_third($local)
6289 {
6290  global $db, $mysoc;
6291 
6292  $sql = " SELECT t.localtax".$local." as localtax";
6293  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
6294  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = (";
6295  $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";
6296  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)";
6297  $sql .= " AND t.localtax".$local."_type <> '0'";
6298  $sql .= " ORDER BY t.rowid DESC";
6299 
6300  $resql = $db->query($sql);
6301  if ($resql) {
6302  $obj = $db->fetch_object($resql);
6303  if ($obj) {
6304  return $obj->localtax;
6305  } else {
6306  return '0';
6307  }
6308  }
6309 
6310  return 'Error';
6311 }
6312 
6313 
6325 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6326 {
6327  global $db, $mysoc;
6328 
6329  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6330 
6331  // Search local taxes
6332  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6333  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6334  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6335  if ($firstparamisid) {
6336  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6337  } else {
6338  $vatratecleaned = $vatrate;
6339  $vatratecode = '';
6340  $reg = array();
6341  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6342  $vatratecleaned = $reg[1];
6343  $vatratecode = $reg[2];
6344  }
6345 
6346  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6347  /*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 ??
6348  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6349  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6350  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6351  if ($vatratecode) {
6352  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6353  }
6354  }
6355 
6356  $resql = $db->query($sql);
6357  if ($resql) {
6358  $obj = $db->fetch_object($resql);
6359  if ($obj) {
6360  return array(
6361  'rowid'=>$obj->rowid,
6362  'code'=>$obj->code,
6363  'rate'=>$obj->rate,
6364  'localtax1'=>$obj->localtax1,
6365  'localtax1_type'=>$obj->localtax1_type,
6366  'localtax2'=>$obj->localtax2,
6367  'localtax2_type'=>$obj->localtax2_type,
6368  'npr'=>$obj->npr,
6369  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6370  'accountancy_code_buy'=>$obj->accountancy_code_buy
6371  );
6372  } else {
6373  return array();
6374  }
6375  } else {
6376  dol_print_error($db);
6377  }
6378 
6379  return array();
6380 }
6381 
6398 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6399 {
6400  global $db, $mysoc;
6401 
6402  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6403 
6404  // Search local taxes
6405  $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";
6406  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6407  if ($firstparamisid) {
6408  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6409  } else {
6410  $vatratecleaned = $vatrate;
6411  $vatratecode = '';
6412  $reg = array();
6413  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6414  $vatratecleaned = $reg[1];
6415  $vatratecode = $reg[2];
6416  }
6417 
6418  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6419  if (!empty($mysoc) && $mysoc->country_code == 'ES') {
6420  $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
6421  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
6422  } else {
6423  $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
6424  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
6425  }
6426  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6427  if ($vatratecode) {
6428  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6429  }
6430  }
6431 
6432  $resql = $db->query($sql);
6433  if ($resql) {
6434  $obj = $db->fetch_object($resql);
6435 
6436  if ($obj) {
6437  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6438 
6439  if ($local == 1) {
6440  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6441  } elseif ($local == 2) {
6442  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6443  } else {
6444  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);
6445  }
6446  }
6447  }
6448 
6449  return array();
6450 }
6451 
6462 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6463 {
6464  global $db, $conf, $mysoc;
6465 
6466  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6467 
6468  $ret = 0;
6469  $found = 0;
6470 
6471  if ($idprod > 0) {
6472  // Load product
6473  $product = new Product($db);
6474  $product->fetch($idprod);
6475 
6476  if ($mysoc->country_code == $thirdpartytouse->country_code) {
6477  // If country to consider is ours
6478  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6479  $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
6480  if ($result > 0) {
6481  $ret = $product->vatrate_supplier;
6482  if ($product->default_vat_code_supplier) {
6483  $ret .= ' ('.$product->default_vat_code_supplier.')';
6484  }
6485  $found = 1;
6486  }
6487  }
6488  if (!$found) {
6489  $ret = $product->tva_tx; // Default sales vat of product
6490  if ($product->default_vat_code) {
6491  $ret .= ' ('.$product->default_vat_code.')';
6492  }
6493  $found = 1;
6494  }
6495  } else {
6496  // TODO Read default product vat according to product and another countrycode.
6497  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6498  }
6499  }
6500 
6501  if (!$found) {
6502  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6503  // 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).
6504  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6505  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6506  $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
6507  $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
6508  $sql .= $db->plimit(1);
6509 
6510  $resql = $db->query($sql);
6511  if ($resql) {
6512  $obj = $db->fetch_object($resql);
6513  if ($obj) {
6514  $ret = $obj->vat_rate;
6515  if ($obj->default_vat_code) {
6516  $ret .= ' ('.$obj->default_vat_code.')';
6517  }
6518  }
6519  $db->free($resql);
6520  } else {
6521  dol_print_error($db);
6522  }
6523  } else {
6524  // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
6525  // '1.23'
6526  // or '1.23 (CODE)'
6527  $defaulttx = '';
6528  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6529  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6530  }
6531  /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6532  $defaultcode = $reg[1];
6533  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6534  }*/
6535 
6536  $ret = $defaulttx;
6537  }
6538  }
6539 
6540  dol_syslog("get_product_vat_for_country: ret=".$ret);
6541  return $ret;
6542 }
6543 
6553 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6554 {
6555  global $db, $mysoc;
6556 
6557  if (!class_exists('Product')) {
6558  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6559  }
6560 
6561  $ret = 0;
6562  $found = 0;
6563 
6564  if ($idprod > 0) {
6565  // Load product
6566  $product = new Product($db);
6567  $result = $product->fetch($idprod);
6568 
6569  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6570  /* Not defined yet, so we don't use this
6571  if ($local==1) $ret=$product->localtax1_tx;
6572  elseif ($local==2) $ret=$product->localtax2_tx;
6573  $found=1;
6574  */
6575  } else {
6576  // TODO Read default product vat according to product and another countrycode.
6577  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6578  }
6579  }
6580 
6581  if (!$found) {
6582  // If vat of product for the country not found or not defined, we return higher vat of country.
6583  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6584  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6585  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6586  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6587  $sql .= $db->plimit(1);
6588 
6589  $resql = $db->query($sql);
6590  if ($resql) {
6591  $obj = $db->fetch_object($resql);
6592  if ($obj) {
6593  if ($local == 1) {
6594  $ret = $obj->localtax1;
6595  } elseif ($local == 2) {
6596  $ret = $obj->localtax2;
6597  }
6598  }
6599  } else {
6600  dol_print_error($db);
6601  }
6602  }
6603 
6604  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6605  return $ret;
6606 }
6607 
6624 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6625 {
6626  global $conf;
6627 
6628  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6629 
6630  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6631  $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;
6632 
6633  $seller_country_code = $thirdparty_seller->country_code;
6634  $seller_in_cee = isInEEC($thirdparty_seller);
6635 
6636  $buyer_country_code = $thirdparty_buyer->country_code;
6637  $buyer_in_cee = isInEEC($thirdparty_buyer);
6638 
6639  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 : ''));
6640 
6641  // 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)
6642  // we use the buyer VAT.
6643  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6644  if ($seller_in_cee && $buyer_in_cee) {
6645  $isacompany = $thirdparty_buyer->isACompany();
6646  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6647  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6648  if (!isValidVATID($thirdparty_buyer)) {
6649  $isacompany = 0;
6650  }
6651  }
6652 
6653  if (!$isacompany) {
6654  //print 'VATRULE 0';
6655  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6656  }
6657  }
6658  }
6659 
6660  // If seller does not use VAT, default VAT is 0. End of rule.
6661  if (!$seller_use_vat) {
6662  //print 'VATRULE 1';
6663  return 0;
6664  }
6665 
6666  // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
6667  if (($seller_country_code == $buyer_country_code)
6668  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6669  //print 'VATRULE 2';
6670  $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6671 
6672  if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
6673  // Special case for india.
6674  //print 'VATRULE 2b';
6675  $reg = array();
6676  if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
6677  // we must revert the C+S into I
6678  $tmpvat = str_replace("C+S", "I", $tmpvat);
6679  } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
6680  // we must revert the I into C+S
6681  $tmpvat = str_replace("I", "C+S", $tmpvat);
6682  }
6683  }
6684 
6685  return $tmpvat;
6686  }
6687 
6688  // If (seller and buyer in the European Community) and (property sold = new means of transport such as car, boat, plane) then VAT by default = 0 (VAT must be paid by the buyer to the tax center of his country and not to the seller). End of rule.
6689  // 'VATRULE 3' - Not supported
6690 
6691  // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
6692  // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
6693  if (($seller_in_cee && $buyer_in_cee)) {
6694  $isacompany = $thirdparty_buyer->isACompany();
6695  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6696  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6697  if (!isValidVATID($thirdparty_buyer)) {
6698  $isacompany = 0;
6699  }
6700  }
6701 
6702  if (!$isacompany) {
6703  //print 'VATRULE 4';
6704  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6705  } else {
6706  //print 'VATRULE 5';
6707  return 0;
6708  }
6709  }
6710 
6711  // If (seller in the European Community and buyer outside the European Community and private buyer) then VAT by default = VAT of the product sold. End of rule
6712  // I don't see any use case that need this rule.
6713  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6714  $isacompany = $thirdparty_buyer->isACompany();
6715  if (!$isacompany) {
6716  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6717  //print 'VATRULE extra';
6718  }
6719  }
6720 
6721  // Otherwise the VAT proposed by default=0. End of rule.
6722  // Rem: This means that at least one of the 2 is outside the European Community and the country differs
6723  //print 'VATRULE 6';
6724  return 0;
6725 }
6726 
6727 
6738 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6739 {
6740  global $db;
6741 
6742  if ($idprodfournprice > 0) {
6743  if (!class_exists('ProductFournisseur')) {
6744  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6745  }
6746  $prodprice = new ProductFournisseur($db);
6747  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6748  return $prodprice->fourn_tva_npr;
6749  } elseif ($idprod > 0) {
6750  if (!class_exists('Product')) {
6751  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6752  }
6753  $prod = new Product($db);
6754  $prod->fetch($idprod);
6755  return $prod->tva_npr;
6756  }
6757 
6758  return 0;
6759 }
6760 
6774 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6775 {
6776  global $mysoc;
6777 
6778  if (!is_object($thirdparty_seller)) {
6779  return -1;
6780  }
6781  if (!is_object($thirdparty_buyer)) {
6782  return -1;
6783  }
6784 
6785  if ($local == 1) { // Localtax 1
6786  if ($mysoc->country_code == 'ES') {
6787  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6788  return 0;
6789  }
6790  } else {
6791  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6792  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6793  return 0;
6794  }
6795  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6796  return 0;
6797  }
6798  }
6799  } elseif ($local == 2) { //I Localtax 2
6800  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6801  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6802  return 0;
6803  }
6804  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6805  return 0;
6806  }
6807  }
6808 
6809  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6810  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6811  }
6812 
6813  return 0;
6814 }
6815 
6824 function yn($yesno, $case = 1, $color = 0)
6825 {
6826  global $langs;
6827 
6828  $result = 'unknown';
6829  $classname = '';
6830  if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // A mettre avant test sur no a cause du == 0
6831  $result = $langs->trans('yes');
6832  if ($case == 1 || $case == 3) {
6833  $result = $langs->trans("Yes");
6834  }
6835  if ($case == 2) {
6836  $result = '<input type="checkbox" value="1" checked disabled>';
6837  }
6838  if ($case == 3) {
6839  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6840  }
6841 
6842  $classname = 'ok';
6843  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6844  $result = $langs->trans("no");
6845  if ($case == 1 || $case == 3) {
6846  $result = $langs->trans("No");
6847  }
6848  if ($case == 2) {
6849  $result = '<input type="checkbox" value="0" disabled>';
6850  }
6851  if ($case == 3) {
6852  $result = '<input type="checkbox" value="0" disabled> '.$result;
6853  }
6854 
6855  if ($color == 2) {
6856  $classname = 'ok';
6857  } else {
6858  $classname = 'error';
6859  }
6860  }
6861  if ($color) {
6862  return '<span class="'.$classname.'">'.$result.'</span>';
6863  }
6864  return $result;
6865 }
6866 
6882 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6883 {
6884  global $conf;
6885 
6886  if (empty($modulepart) && !empty($object->module)) {
6887  $modulepart = $object->module;
6888  }
6889 
6890  $path = '';
6891 
6892  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6893  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
6894  $arrayforoldpath[] = 'product';
6895  }
6896  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6897  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6898  if (empty($alpha)) {
6899  $num = preg_replace('/([^0-9])/i', '', $num);
6900  } else {
6901  $num = preg_replace('/^.*\-/i', '', $num);
6902  }
6903  $num = substr("000".$num, -$level);
6904  if ($level == 1) {
6905  $path = substr($num, 0, 1);
6906  }
6907  if ($level == 2) {
6908  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6909  }
6910  if ($level == 3) {
6911  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6912  }
6913  } else {
6914  // We will enhance here a common way of forging path for document storage.
6915  // In a future, we may distribute directories on several levels depending on setup and object.
6916  // Here, $object->id, $object->ref and $modulepart are required.
6917  //var_dump($modulepart);
6918  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6919  }
6920 
6921  if (empty($withoutslash) && !empty($path)) {
6922  $path .= '/';
6923  }
6924 
6925  return $path;
6926 }
6927 
6936 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6937 {
6938  global $conf;
6939 
6940  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6941 
6942  $dir_osencoded = dol_osencode($dir);
6943  if (@is_dir($dir_osencoded)) {
6944  return 0;
6945  }
6946 
6947  $nberr = 0;
6948  $nbcreated = 0;
6949 
6950  $ccdir = '';
6951  if (!empty($dataroot)) {
6952  // Remove data root from loop
6953  $dir = str_replace($dataroot.'/', '', $dir);
6954  $ccdir = $dataroot.'/';
6955  }
6956 
6957  $cdir = explode("/", $dir);
6958  $num = count($cdir);
6959  for ($i = 0; $i < $num; $i++) {
6960  if ($i > 0) {
6961  $ccdir .= '/'.$cdir[$i];
6962  } else {
6963  $ccdir .= $cdir[$i];
6964  }
6965  if (preg_match("/^.:$/", $ccdir, $regs)) {
6966  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6967  }
6968 
6969  // Attention, le is_dir() peut echouer bien que le rep existe.
6970  // (ex selon config de open_basedir)
6971  if ($ccdir) {
6972  $ccdir_osencoded = dol_osencode($ccdir);
6973  if (!@is_dir($ccdir_osencoded)) {
6974  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6975 
6976  umask(0);
6977  $dirmaskdec = octdec((string) $newmask);
6978  if (empty($newmask)) {
6979  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6980  }
6981  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
6982  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
6983  // Si le is_dir a renvoye une fausse info, alors on passe ici.
6984  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
6985  $nberr++;
6986  } else {
6987  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
6988  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
6989  $nbcreated++;
6990  }
6991  } else {
6992  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
6993  }
6994  }
6995  }
6996  return ($nberr ? -$nberr : $nbcreated);
6997 }
6998 
6999 
7007 function dolChmod($filepath, $newmask = '')
7008 {
7009  global $conf;
7010 
7011  if (!empty($newmask)) {
7012  @chmod($filepath, octdec($newmask));
7013  } elseif (!empty($conf->global->MAIN_UMASK)) {
7014  @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7015  }
7016 }
7017 
7018 
7024 function picto_required()
7025 {
7026  return '<span class="fieldrequired">*</span>';
7027 }
7028 
7029 
7046 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
7047 {
7048  if (is_null($stringtoclean)) {
7049  return '';
7050  }
7051 
7052  if ($removelinefeed == 2) {
7053  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
7054  }
7055  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
7056 
7057  // 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)
7058  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7059 
7060  $temp = str_replace('< ', '__ltspace__', $temp);
7061 
7062  if ($strip_tags) {
7063  $temp = strip_tags($temp);
7064  } else {
7065  // 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).
7066  $pattern = "/<[^<>]+>/";
7067  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
7068  // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
7069  // pass 2 - $temp after pass 2: 0000-021
7070  $tempbis = $temp;
7071  do {
7072  $temp = $tempbis;
7073  $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
7074  $tempbis = preg_replace($pattern, '', $tempbis);
7075  //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
7076  } while ($tempbis != $temp);
7077 
7078  $temp = $tempbis;
7079 
7080  // 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).
7081  $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
7082  }
7083 
7084  $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
7085 
7086  // Remove also carriage returns
7087  if ($removelinefeed == 1) {
7088  $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
7089  }
7090 
7091  // And double quotes
7092  if ($removedoublespaces) {
7093  while (strpos($temp, " ")) {
7094  $temp = str_replace(" ", " ", $temp);
7095  }
7096  }
7097 
7098  $temp = str_replace('__ltspace__', '< ', $temp);
7099 
7100  return trim($temp);
7101 }
7102 
7118 function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0)
7119 {
7120  if (empty($allowed_tags)) {
7121  $allowed_tags = array(
7122  "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
7123  "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"
7124  );
7125  }
7126  $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
7127  if ($allowiframe) {
7128  if (!in_array('iframe', $allowed_tags)) {
7129  $allowed_tags[] = "iframe";
7130  }
7131  }
7132  if ($allowlink) {
7133  if (!in_array('link', $allowed_tags)) {
7134  $allowed_tags[] = "link";
7135  }
7136  }
7137 
7138  $allowed_tags_string = join("><", $allowed_tags);
7139  $allowed_tags_string = '<'.$allowed_tags_string.'>';
7140 
7141  $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
7142 
7143  $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
7144 
7145  //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
7146  $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
7147 
7148  $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
7149  $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
7150 
7151  $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
7152 
7153  if ($cleanalsosomestyles) { // Clean for remaining html tags
7154  $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
7155  }
7156  if ($removeclassattribute) { // Clean for remaining html tags
7157  $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
7158  }
7159 
7160  // Remove 'javascript:' that we should not find into a text with
7161  // 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)).
7162  if ($cleanalsojavascript) {
7163  $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);
7164  }
7165 
7166  $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
7167 
7168  $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
7169 
7170 
7171  return $temp;
7172 }
7173 
7174 
7186 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"))
7187 {
7188  if (class_exists('DOMDocument') && !empty($stringtoclean)) {
7189  $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
7190 
7191  $dom = new DOMDocument(null, 'UTF-8');
7192  $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
7193 
7194  if (is_object($dom)) {
7195  for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
7196  for ($attrs = $els->item($i)->attributes, $ii = $attrs->length - 1; $ii >= 0; $ii--) {
7197  //var_dump($attrs->item($ii));
7198  if (!empty($attrs->item($ii)->name)) {
7199  if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
7200  // Delete attribute if not into allowed_attributes
7201  $els->item($i)->removeAttribute($attrs->item($ii)->name);
7202  } elseif (in_array($attrs->item($ii)->name, array('style'))) {
7203  // If attribute is 'style'
7204  $valuetoclean = $attrs->item($ii)->value;
7205 
7206  if (isset($valuetoclean)) {
7207  do {
7208  $oldvaluetoclean = $valuetoclean;
7209  $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
7210  $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
7211  if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
7212  $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
7213  $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
7214  $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
7215  }
7216 
7217  // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
7218  $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
7219  $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
7220  } while ($oldvaluetoclean != $valuetoclean);
7221  }
7222 
7223  $attrs->item($ii)->value = $valuetoclean;
7224  }
7225  }
7226  }
7227  }
7228  }
7229 
7230  $return = $dom->saveHTML();
7231  //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
7232 
7233  $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
7234  $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
7235  $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
7236  return trim($return);
7237  } else {
7238  return $stringtoclean;
7239  }
7240 }
7241 
7253 function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
7254 {
7255  $temp = $stringtoclean;
7256  foreach ($disallowed_tags as $tagtoremove) {
7257  $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
7258  $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
7259  }
7260 
7261  if ($cleanalsosomestyles) {
7262  $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
7263  }
7264 
7265  return $temp;
7266 }
7267 
7268 
7278 function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
7279 {
7280  if ($nboflines == 1) {
7281  if (dol_textishtml($text)) {
7282  $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
7283  $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
7284  } else {
7285  if (isset($text)) {
7286  $firstline = preg_replace('/[\n\r].*/', '', $text);
7287  } else {
7288  $firstline = '';
7289  }
7290  }
7291  return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
7292  } else {
7293  $ishtml = 0;
7294  if (dol_textishtml($text)) {
7295  $text = preg_replace('/\n/', '', $text);
7296  $ishtml = 1;
7297  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7298  } else {
7299  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7300  }
7301 
7302  $text = strtr($text, $repTable);
7303  if ($charset == 'UTF-8') {
7304  $pattern = '/(<br[^>]*>)/Uu';
7305  } else {
7306  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7307  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7308  }
7309  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7310 
7311  $firstline = '';
7312  $i = 0;
7313  $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
7314  while (($i < $nba) && ($i < ($nboflines * 2))) {
7315  if ($i % 2 == 0) {
7316  $firstline .= $a[$i];
7317  } elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) {
7318  $firstline .= ($ishtml ? "<br>\n" : "\n");
7319  }
7320  $i++;
7321  }
7322  unset($a);
7323  return $firstline.(($i < $nba) ? '...' : '');
7324  }
7325 }
7326 
7327 
7339 function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
7340 {
7341  if (is_null($stringtoencode)) {
7342  return '';
7343  }
7344 
7345  if (!$nl2brmode) {
7346  return nl2br($stringtoencode, $forxml);
7347  } else {
7348  $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
7349  return $ret;
7350  }
7351 }
7352 
7361 function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
7362 {
7363  global $conf;
7364 
7365  if (empty($nouseofiframesandbox) && !empty($conf->global->MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS)) {
7366  // TODO using sandbox on inline html content is not possible yet with current browsers
7367  //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
7368  //$s .= $stringtoencode;
7369  //$s .= '</body></html></iframe>';
7370  return $stringtoencode;
7371  } else {
7372  $out = $stringtoencode;
7373 
7374  do {
7375  $oldstringtoclean = $out;
7376 
7377  libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
7378 
7379  if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') {
7380  try {
7381  $dom = new DOMDocument;
7382  // Add a trick to solve pb with text without parent tag
7383  // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
7384  // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
7385  $out = '<div class="tricktoremove">'.$out.'</div>';
7386  $dom->loadHTML($out, LIBXML_HTML_NODEFDTD|LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
7387  $out = trim($dom->saveHTML());
7388 
7389  // Remove the trick added to solve pb with text without parent tag
7390  $out = preg_replace('/^<div class="tricktoremove">/', '', $out);
7391  $out = preg_replace('/<\/div>$/', '', $out);
7392  } catch (Exception $e) {
7393  // If error, invalid HTML string with no way to clean it
7394  //print $e->getMessage();
7395  $out = 'InvalidHTMLString';
7396  }
7397  }
7398 
7399  // Clean some html entities that are useless so text is cleaner
7400  $out = preg_replace('/&(tab|newline);/i', ' ', $out);
7401 
7402  // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are
7403  // encoded using text entities) so we can then exclude all numeric entities.
7404  $out = preg_replace('/&#39;/i', '&apos;', $out);
7405 
7406  // 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).
7407  // 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
7408  // using a non coventionnel way to be encoded, to not have them sanitized just after)
7409  $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', function ($m) {
7410  return realCharForNumericEntities($m); }, $out);
7411 
7412 
7413  // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
7414  $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'.
7415 
7416  // Keep only some html tags and remove also some 'javascript:' strings
7417  $out = dol_string_onlythesehtmltags($out, 0, ($check == 'restricthtmlallowclass' ? 0 : 1), 1);
7418 
7419  // We should also exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
7420  if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) {
7421  // 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.
7423  }
7424 
7425  // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
7426  $out = preg_replace('/&apos;/i', "&#39;", $out);
7427  } while ($oldstringtoclean != $out);
7428 
7429  // Check the limit of external links that are automatically executed in a Rich text content. We count:
7430  // '<img' to avoid <img src="http...">
7431  // 'url(' to avoid inline style like background: url(http...
7432  // '<link' to avoid <link href="http...">
7433  $reg = array();
7434  preg_match_all('/(<img|url\‍(|<link)/i', $out, $reg);
7435  $nbextlink = count($reg[0]);
7436  if ($nbextlink > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
7437  $out = 'TooManyLinksIntoHTMLString';
7438  }
7439  //
7440  if (!empty($conf->global->MAIN_DISALLOW_EXT_URL_INTO_DESCRIPTIONS) || $check == 'restricthtmlnolink') {
7441  if ($nbextlink > 0) {
7442  $out = 'ExternalLinksNotAllowed';
7443  }
7444  }
7445 
7446  return $out;
7447  }
7448 }
7449 
7469 function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
7470 {
7471  if (is_null($stringtoencode)) {
7472  return '';
7473  }
7474 
7475  $newstring = $stringtoencode;
7476  if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
7477  $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.
7478  if ($removelasteolbr) {
7479  $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
7480  }
7481  $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__'));
7482  $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
7483  $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"'));
7484  } else {
7485  if ($removelasteolbr) {
7486  $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
7487  }
7488  $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
7489  }
7490  // Other substitutions that htmlentities does not do
7491  //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
7492  return $newstring;
7493 }
7494 
7502 function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
7503 {
7504  $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7505  $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
7506  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
7507  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
7508  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
7509  return $ret;
7510 }
7511 
7518 function dol_htmlcleanlastbr($stringtodecode)
7519 {
7520  $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
7521  $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
7522  return $ret;
7523 }
7524 
7534 function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
7535 {
7536  $newstring = $a;
7537  if ($keepsomeentities) {
7538  $newstring = strtr($newstring, array('&amp;'=>'__andamp__', '&lt;'=>'__andlt__', '&gt;'=>'__andgt__', '"'=>'__dquot__'));
7539  }
7540  $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
7541  if ($keepsomeentities) {
7542  $newstring = strtr($newstring, array('__andamp__'=>'&amp;', '__andlt__'=>'&lt;', '__andgt__'=>'&gt;', '__dquot__'=>'"'));
7543  }
7544  return $newstring;
7545 }
7546 
7557 function dol_htmlentities($string, $flags = ENT_QUOTES|ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
7558 {
7559  return htmlentities($string, $flags, $encoding, $double_encode);
7560 }
7561 
7571 function dol_string_is_good_iso($s, $clean = 0)
7572 {
7573  $len = dol_strlen($s);
7574  $out = '';
7575  $ok = 1;
7576  for ($scursor = 0; $scursor < $len; $scursor++) {
7577  $ordchar = ord($s[$scursor]);
7578  //print $scursor.'-'.$ordchar.'<br>';
7579  if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
7580  $ok = 0;
7581  break;
7582  } elseif ($ordchar > 126 && $ordchar < 160) {
7583  $ok = 0;
7584  break;
7585  } elseif ($clean) {
7586  $out .= $s[$scursor];
7587  }
7588  }
7589  if ($clean) {
7590  return $out;
7591  }
7592  return $ok;
7593 }
7594 
7603 function dol_nboflines($s, $maxchar = 0)
7604 {
7605  if ($s == '') {
7606  return 0;
7607  }
7608  $arraystring = explode("\n", $s);
7609  $nb = count($arraystring);
7610 
7611  return $nb;
7612 }
7613 
7614 
7624 function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
7625 {
7626  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7627  if (dol_textishtml($text)) {
7628  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7629  }
7630 
7631  $text = strtr($text, $repTable);
7632  if ($charset == 'UTF-8') {
7633  $pattern = '/(<br[^>]*>)/Uu';
7634  } else {
7635  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7636  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7637  }
7638  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7639 
7640  $nblines = (int) floor((count($a) + 1) / 2);
7641  // count possible auto line breaks
7642  if ($maxlinesize) {
7643  foreach ($a as $line) {
7644  if (dol_strlen($line) > $maxlinesize) {
7645  //$line_dec = html_entity_decode(strip_tags($line));
7646  $line_dec = html_entity_decode($line);
7647  if (dol_strlen($line_dec) > $maxlinesize) {
7648  $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
7649  $nblines += substr_count($line_dec, '\n');
7650  }
7651  }
7652  }
7653  }
7654 
7655  unset($a);
7656  return $nblines;
7657 }
7658 
7667 function dol_textishtml($msg, $option = 0)
7668 {
7669  if (is_null($msg)) {
7670  return false;
7671  }
7672 
7673  if ($option == 1) {
7674  if (preg_match('/<html/i', $msg)) {
7675  return true;
7676  } elseif (preg_match('/<body/i', $msg)) {
7677  return true;
7678  } elseif (preg_match('/<\/textarea/i', $msg)) {
7679  return true;
7680  } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
7681  return true;
7682  } elseif (preg_match('/<br/i', $msg)) {
7683  return true;
7684  }
7685  return false;
7686  } else {
7687  // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
7688  $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
7689  if (preg_match('/<html/i', $msg)) {
7690  return true;
7691  } elseif (preg_match('/<body/i', $msg)) {
7692  return true;
7693  } elseif (preg_match('/<\/textarea/i', $msg)) {
7694  return true;
7695  } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
7696  return true;
7697  } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
7698  return true;
7699  } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
7700  return true;
7701  } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
7702  return true;
7703  } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
7704  return true; // must accept <img src="http://example.com/aaa.png" />
7705  } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
7706  return true; // must accept <a href="http://example.com/aaa.png" />
7707  } elseif (preg_match('/<h[0-9]>/i', $msg)) {
7708  return true;
7709  } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
7710  // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
7711  return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
7712  } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
7713  return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
7714  }
7715 
7716  return false;
7717  }
7718 }
7719 
7734 function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
7735 {
7736  if (!empty($invert)) {
7737  $tmp = $text1;
7738  $text1 = $text2;
7739  $text2 = $tmp;
7740  }
7741 
7742  $ret = '';
7743  $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
7744  $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
7745  $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
7746  return $ret;
7747 }
7748 
7749 
7750 
7762 function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
7763 {
7764  global $db, $conf, $mysoc, $user, $extrafields;
7765 
7766  $substitutionarray = array();
7767 
7768  if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
7769  // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
7770  // this will include signature content first and then replace var found into content of signature
7771  //var_dump($onlykey);
7772  $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()
7773  $usersignature = $user->signature;
7774  $substitutionarray = array_merge($substitutionarray, array(
7775  '__SENDEREMAIL_SIGNATURE__' => (string) ((empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
7776  '__USER_SIGNATURE__' => (string) (($usersignature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
7777  ));
7778 
7779  if (is_object($user)) {
7780  $substitutionarray = array_merge($substitutionarray, array(
7781  '__USER_ID__' => (string) $user->id,
7782  '__USER_LOGIN__' => (string) $user->login,
7783  '__USER_EMAIL__' => (string) $user->email,
7784  '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
7785  '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
7786  '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
7787  '__USER_FAX__' => (string) $user->office_fax,
7788  '__USER_LASTNAME__' => (string) $user->lastname,
7789  '__USER_FIRSTNAME__' => (string) $user->firstname,
7790  '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
7791  '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
7792  '__USER_JOB__' => (string) $user->job,
7793  '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
7794  '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
7795  ));
7796  }
7797  }
7798  if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
7799  $substitutionarray = array_merge($substitutionarray, array(
7800  '__MYCOMPANY_NAME__' => $mysoc->name,
7801  '__MYCOMPANY_EMAIL__' => $mysoc->email,
7802  '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
7803  '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
7804  '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
7805  '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
7806  '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
7807  '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
7808  '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
7809  '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
7810  '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
7811  '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
7812  '__MYCOMPANY_ADDRESS__' => $mysoc->address,
7813  '__MYCOMPANY_ZIP__' => $mysoc->zip,
7814  '__MYCOMPANY_TOWN__' => $mysoc->town,
7815  '__MYCOMPANY_COUNTRY__' => $mysoc->country,
7816  '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
7817  '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
7818  '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
7819  ));
7820  }
7821 
7822  if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
7823  if ($onlykey) {
7824  $substitutionarray['__ID__'] = '__ID__';
7825  $substitutionarray['__REF__'] = '__REF__';
7826  $substitutionarray['__NEWREF__'] = '__NEWREF__';
7827  $substitutionarray['__LABEL__'] = '__LABEL__';
7828  $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
7829  $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
7830  $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
7831  $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
7832  $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
7833 
7834  if (isModEnabled("societe")) { // Most objects are concerned
7835  $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
7836  $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
7837  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
7838  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
7839  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
7840  $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
7841  $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
7842  $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
7843  $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
7844  $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
7845  $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
7846  $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
7847  $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
7848  $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
7849  $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
7850  $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
7851  $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
7852  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
7853  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
7854  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
7855  }
7856  if (isModEnabled('adherent') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
7857  $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
7858  $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
7859  $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
7860  $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
7861  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
7862  /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
7863  $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
7864  }
7865  // add variables subtitutions ticket
7866  if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
7867  $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
7868  $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
7869  $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
7870  $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
7871  $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
7872  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
7873  $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
7874  $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
7875  $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
7876  }
7877 
7878  if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
7879  $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
7880  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
7881  $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
7882  }
7883  if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
7884  $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
7885  $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
7886  $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
7887  /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
7888  $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
7889  }
7890  if (isModEnabled('contrat') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
7891  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
7892  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
7893  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
7894  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
7895  }
7896  if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
7897  $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
7898  }
7899  if (isModEnabled("ficheinter") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
7900  $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
7901  }
7902  $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
7903  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
7904  $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
7905  $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
7906  $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
7907  $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
7908  $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
7909 
7910  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
7911  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
7912  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
7913  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
7914  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
7915 
7916  if (isModEnabled("expedition") && (!is_object($object) || $object->element == 'shipping')) {
7917  $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
7918  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
7919  }
7920  if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
7921  $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment';
7922  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
7923  }
7924  } else {
7925  $substitutionarray['__ID__'] = $object->id;
7926  $substitutionarray['__REF__'] = $object->ref;
7927  $substitutionarray['__NEWREF__'] = $object->newref;
7928  $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
7929  $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7930  $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7931  $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
7932  $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
7933  if ($object->element == "shipping") {
7934  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', 0, $outputlangs) : '');
7935  } else {
7936  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7937  }
7938  $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%d") : '');
7939  $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%A") : '');
7940  $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%m") : '');
7941  $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%b") : '');
7942  $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%Y") : '');
7943  $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%H") : '');
7944  $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%M") : '');
7945  $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%S") : '');
7946 
7947  // For backward compatibility (deprecated)
7948  $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7949  $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7950  $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7951  $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 : '')) : '');
7952 
7953  if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
7954  $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
7955 
7956  $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
7957  if (method_exists($object, 'getCivilityLabel')) {
7958  $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
7959  }
7960  $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
7961  $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
7962  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
7963  if (method_exists($object, 'getFullName')) {
7964  $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
7965  }
7966  $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
7967  $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
7968  $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
7969  $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
7970  $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
7971  $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
7972  $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
7973  $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
7974  $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
7975  $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
7976  $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
7977  $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
7978  $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
7979  $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
7980  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
7981  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
7982  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
7983  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
7984  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
7985  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
7986  }
7987 
7988  if (is_object($object) && $object->element == 'societe') {
7989  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
7990  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
7991  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
7992  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
7993  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
7994  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
7995  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
7996  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
7997  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
7998  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
7999  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
8000  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
8001  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
8002  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
8003  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
8004  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
8005  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
8006  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
8007  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
8008  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
8009  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
8010  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
8011  } elseif (is_object($object->thirdparty)) {
8012  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
8013  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
8014  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
8015  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
8016  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
8017  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
8018  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
8019  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
8020  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
8021  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
8022  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
8023  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
8024  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
8025  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
8026  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
8027  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
8028  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
8029  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
8030  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
8031  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
8032  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
8033  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
8034  }
8035 
8036  if (is_object($object) && $object->element == 'recruitmentcandidature') {
8037  $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
8038  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
8039  $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
8040  }
8041  if (is_object($object) && $object->element == 'conferenceorboothattendee') {
8042  $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
8043  $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
8044  $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
8045  }
8046 
8047  if (is_object($object->project)) {
8048  $substitutionarray['__PROJECT_ID__'] = (is_object($object->project) ? $object->project->id : '');
8049  $substitutionarray['__PROJECT_REF__'] = (is_object($object->project) ? $object->project->ref : '');
8050  $substitutionarray['__PROJECT_NAME__'] = (is_object($object->project) ? $object->project->title : '');
8051  }
8052  if (is_object($object->projet)) { // Deprecated, for backward compatibility
8053  $substitutionarray['__PROJECT_ID__'] = (is_object($object->projet) ? $object->projet->id : '');
8054  $substitutionarray['__PROJECT_REF__'] = (is_object($object->projet) ? $object->projet->ref : '');
8055  $substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet) ? $object->projet->title : '');
8056  }
8057  if (is_object($object) && $object->element == 'project') {
8058  $substitutionarray['__PROJECT_NAME__'] = $object->title;
8059  }
8060 
8061  if (is_object($object) && $object->element == 'shipping') {
8062  $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
8063  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
8064  }
8065  if (is_object($object) && $object->element == 'reception') {
8066  $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
8067  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
8068  }
8069 
8070  if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
8071  $dateplannedstart = '';
8072  $datenextexpiration = '';
8073  foreach ($object->lines as $line) {
8074  if ($line->date_start > $dateplannedstart) {
8075  $dateplannedstart = $line->date_start;
8076  }
8077  if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
8078  $datenextexpiration = $line->date_end;
8079  }
8080  }
8081  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
8082  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
8083  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
8084  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
8085  }
8086  // add substition variable for ticket
8087  if (is_object($object) && $object->element == 'ticket') {
8088  $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
8089  $substitutionarray['__REF__'] = $object->ref;
8090  $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
8091  $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
8092  $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
8093  $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
8094  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
8095  $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
8096  $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
8097  $userstat = new User($db);
8098  if ($object->fk_user_assign > 0) {
8099  $userstat->fetch($object->fk_user_assign);
8100  $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
8101  }
8102 
8103  if ($object->fk_user_create > 0) {
8104  $userstat->fetch($object->fk_user_create);
8105  $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
8106  }
8107  }
8108 
8109  // Create dynamic tags for __EXTRAFIELD_FIELD__
8110  if ($object->table_element && $object->id > 0) {
8111  if (!is_object($extrafields)) {
8112  $extrafields = new ExtraFields($db);
8113  }
8114  $extrafields->fetch_name_optionals_label($object->table_element, true);
8115 
8116  if ($object->fetch_optionals() > 0) {
8117  if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
8118  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
8119  if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
8120  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
8121  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
8122  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
8123  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
8124  $datetime = $object->array_options['options_'.$key];
8125  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
8126  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
8127  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
8128  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
8129  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
8130  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
8131  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
8132  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
8133  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]);
8134  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
8135  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] :'';
8136  }
8137  }
8138  }
8139  }
8140  }
8141 
8142  // Complete substitution array with the url to make online payment
8143  $paymenturl = '';
8144  if (empty($substitutionarray['__REF__'])) {
8145  $paymenturl = '';
8146  } else {
8147  // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
8148  require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
8149  $outputlangs->loadLangs(array('paypal', 'other'));
8150 
8151  $amounttouse = 0;
8152  $typeforonlinepayment = 'free';
8153  if (is_object($object) && $object->element == 'commande') {
8154  $typeforonlinepayment = 'order';
8155  }
8156  if (is_object($object) && $object->element == 'facture') {
8157  $typeforonlinepayment = 'invoice';
8158  }
8159  if (is_object($object) && $object->element == 'member') {
8160  $typeforonlinepayment = 'member';
8161  if (!empty($object->last_subscription_amount)) {
8162  $amounttouse = $object->last_subscription_amount;
8163  }
8164  }
8165  if (is_object($object) && $object->element == 'contrat') {
8166  $typeforonlinepayment = 'contract';
8167  }
8168  if (is_object($object) && $object->element == 'fichinter') {
8169  $typeforonlinepayment = 'ficheinter';
8170  }
8171 
8172  $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
8173  $paymenturl = $url;
8174  }
8175 
8176  if ($object->id > 0) {
8177  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
8178  $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
8179 
8180  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
8181  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
8182  } else {
8183  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
8184  }
8185  if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') {
8186  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
8187  } else {
8188  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
8189  }
8190  if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') {
8191  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
8192  } else {
8193  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
8194  }
8195  if (!empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'contrat') {
8196  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
8197  } else {
8198  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
8199  }
8200  if (!empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'fichinter') {
8201  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
8202  } else {
8203  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
8204  }
8205  if (!empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'supplier_proposal') {
8206  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
8207  } else {
8208  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
8209  }
8210 
8211  if (is_object($object) && $object->element == 'propal') {
8212  $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
8213  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
8214  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
8215  }
8216  if (is_object($object) && $object->element == 'commande') {
8217  $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
8218  }
8219  if (is_object($object) && $object->element == 'facture') {
8220  $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
8221  }
8222  if (is_object($object) && $object->element == 'contrat') {
8223  $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
8224  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
8225  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref);
8226  }
8227  if (is_object($object) && $object->element == 'fichinter') {
8228  $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
8229  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
8230  $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref);
8231  }
8232  if (is_object($object) && $object->element == 'supplier_proposal') {
8233  $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
8234  }
8235  if (is_object($object) && $object->element == 'shipping') {
8236  $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
8237  }
8238  }
8239 
8240  if (is_object($object) && $object->element == 'action') {
8241  $substitutionarray['__EVENT_LABEL__'] = $object->label;
8242  $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y');
8243  $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S');
8244  }
8245  }
8246  }
8247  if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
8248  include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
8249 
8250  $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
8251  $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
8252 
8253  $already_payed_all = 0;
8254  if (is_object($object) && ($object instanceof Facture)) {
8255  $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
8256  }
8257 
8258  $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
8259 
8260  $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
8261  $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
8262  $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
8263 
8264  $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
8265 
8266  $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8267  $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)) : '';
8268  $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)) : '';
8269 
8270  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8271  $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
8272  }
8273  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8274  $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
8275  }
8276 
8277  // Amount keys formated in a currency
8278  $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8279  $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8280  $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) : '';
8281  $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)) : '';
8282  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8283  $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8284  }
8285  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8286  $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8287  }
8288 
8289  $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
8290  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
8291  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
8292  // TODO Add other keys for foreign multicurrency
8293 
8294  // For backward compatibility
8295  if ($onlykey != 2) {
8296  $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
8297  $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
8298  $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8299  }
8300  }
8301 
8302  //var_dump($substitutionarray['__AMOUNT_FORMATED__']);
8303  if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
8304  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8305 
8306  $now = dol_now();
8307 
8308  $tmp = dol_getdate($now, true);
8309  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8310  $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
8311  $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8312  $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
8313 
8314  $daytext = $outputlangs->trans('Day'.$tmp['wday']);
8315 
8316  $substitutionarray = array_merge($substitutionarray, array(
8317  '__NOW_TMS__' => (int) $now,
8318  '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 0, $outputlangs),
8319  '__DAY__' => (string) $tmp['mday'],
8320  '__DAY_TEXT__' => $daytext, // Monday
8321  '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
8322  '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
8323  '__MONTH__' => (string) $tmp['mon'],
8324  '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
8325  '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
8326  '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
8327  '__YEAR__' => (string) $tmp['year'],
8328  '__PREVIOUS_DAY__' => (string) $tmp2['day'],
8329  '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
8330  '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
8331  '__NEXT_DAY__' => (string) $tmp4['day'],
8332  '__NEXT_MONTH__' => (string) $tmp5['month'],
8333  '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
8334  ));
8335  }
8336 
8337  if (isModEnabled('multicompany')) {
8338  $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
8339  }
8340  if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
8341  $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
8342  $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
8343  $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
8344  $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
8345  }
8346 
8347  return $substitutionarray;
8348 }
8349 
8366 function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
8367 {
8368  global $conf, $langs;
8369 
8370  if (!is_array($substitutionarray)) {
8371  return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
8372  }
8373 
8374  if (empty($outputlangs)) {
8375  $outputlangs = $langs;
8376  }
8377 
8378  // Is initial text HTML or simple text ?
8379  $msgishtml = 0;
8380  if (dol_textishtml($text, 1)) {
8381  $msgishtml = 1;
8382  }
8383 
8384  // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
8385  if (is_object($outputlangs)) {
8386  $reg = array();
8387  while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
8388  // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
8389  $tmp = explode('|', $reg[1]);
8390  if (!empty($tmp[1])) {
8391  $outputlangs->load($tmp[1]);
8392  }
8393 
8394  $value = $outputlangs->transnoentitiesnoconv($reg[1]);
8395 
8396  if (empty($converttextinhtmlifnecessary)) {
8397  // convert $newval into HTML is necessary
8398  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8399  } else {
8400  if (! $msgishtml) {
8401  $valueishtml = dol_textishtml($value, 1);
8402  //var_dump("valueishtml=".$valueishtml);
8403 
8404  if ($valueishtml) {
8405  $text = dol_htmlentitiesbr($text);
8406  $msgishtml = 1;
8407  }
8408  } else {
8409  $value = dol_nl2br("$value");
8410  }
8411 
8412  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
8413  }
8414  }
8415  }
8416 
8417  // Make substitution for constant keys.
8418  // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
8419  $reg = array();
8420  while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
8421  $keyfound = $reg[1];
8422  if (isASecretKey($keyfound)) {
8423  $value = '*****forbidden*****';
8424  } else {
8425  $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
8426  }
8427 
8428  if (empty($converttextinhtmlifnecessary)) {
8429  // convert $newval into HTML is necessary
8430  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8431  } else {
8432  if (! $msgishtml) {
8433  $valueishtml = dol_textishtml($value, 1);
8434 
8435  if ($valueishtml) {
8436  $text = dol_htmlentitiesbr($text);
8437  $msgishtml = 1;
8438  }
8439  } else {
8440  $value = dol_nl2br("$value");
8441  }
8442 
8443  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
8444  }
8445  }
8446 
8447  // Make substitution for array $substitutionarray
8448  foreach ($substitutionarray as $key => $value) {
8449  if (!isset($value)) {
8450  continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
8451  }
8452 
8453  if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) {
8454  $value = ''; // Protection
8455  }
8456 
8457  if (empty($converttextinhtmlifnecessary)) {
8458  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8459  } else {
8460  if (! $msgishtml) {
8461  $valueishtml = dol_textishtml($value, 1);
8462 
8463  if ($valueishtml) {
8464  $text = dol_htmlentitiesbr($text);
8465  $msgishtml = 1;
8466  }
8467  } else {
8468  $value = dol_nl2br("$value");
8469  }
8470  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8471  }
8472  }
8473 
8474  return $text;
8475 }
8476 
8489 function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
8490 {
8491  global $conf, $user;
8492 
8493  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8494 
8495  // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
8496 
8497  // Check if there is external substitution to do, requested by plugins
8498  $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
8499 
8500  foreach ($dirsubstitutions as $reldir) {
8501  $dir = dol_buildpath($reldir, 0);
8502 
8503  // Check if directory exists
8504  if (!dol_is_dir($dir)) {
8505  continue;
8506  }
8507 
8508  $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
8509  foreach ($substitfiles as $substitfile) {
8510  $reg = array();
8511  if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
8512  $module = $reg[1];
8513 
8514  dol_syslog("Library ".$substitfile['name']." found into ".$dir);
8515  // Include the user's functions file
8516  require_once $dir.$substitfile['name'];
8517  // Call the user's function, and only if it is defined
8518  $function_name = $module."_".$callfunc;
8519  if (function_exists($function_name)) {
8520  $function_name($substitutionarray, $outputlangs, $object, $parameters);
8521  }
8522  }
8523  }
8524  }
8525  if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) {
8526  // to list all tags in odt template
8527  $tags = '';
8528  foreach ($substitutionarray as $key => $value) {
8529  $tags .= '{'.$key.'} => '.$value."\n";
8530  }
8531  $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
8532  }
8533 }
8534 
8544 function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
8545 {
8546  print get_date_range($date_start, $date_end, $format, $outputlangs);
8547 }
8548 
8559 function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
8560 {
8561  global $langs;
8562 
8563  $out = '';
8564 
8565  if (!is_object($outputlangs)) {
8566  $outputlangs = $langs;
8567  }
8568 
8569  if ($date_start && $date_end) {
8570  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8571  }
8572  if ($date_start && !$date_end) {
8573  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8574  }
8575  if (!$date_start && $date_end) {
8576  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8577  }
8578 
8579  return $out;
8580 }
8581 
8590 function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
8591 {
8592  global $conf;
8593 
8594  $ret = '';
8595  // If order not defined, we use the setup
8596  if ($nameorder < 0) {
8597  $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
8598  }
8599  if ($nameorder == 1) {
8600  $ret .= $firstname;
8601  if ($firstname && $lastname) {
8602  $ret .= ' ';
8603  }
8604  $ret .= $lastname;
8605  } elseif ($nameorder == 2 || $nameorder == 3) {
8606  $ret .= $firstname;
8607  if (empty($ret) && $nameorder == 3) {
8608  $ret .= $lastname;
8609  }
8610  } else { // 0, 4 or 5
8611  $ret .= $lastname;
8612  if (empty($ret) && $nameorder == 5) {
8613  $ret .= $firstname;
8614  }
8615  if ($nameorder == 0) {
8616  if ($firstname && $lastname) {
8617  $ret .= ' ';
8618  }
8619  $ret .= $firstname;
8620  }
8621  }
8622  return $ret;
8623 }
8624 
8625 
8637 function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0)
8638 {
8639  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
8640  if (!is_array($mesgs)) {
8641  // If mesgs is a string
8642  if ($mesgs) {
8643  if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
8644  return;
8645  }
8646  $_SESSION['dol_events'][$style][] = $mesgs;
8647  }
8648  } else {
8649  // If mesgs is an array
8650  foreach ($mesgs as $mesg) {
8651  if ($mesg) {
8652  if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
8653  return;
8654  }
8655  $_SESSION['dol_events'][$style][] = $mesg;
8656  }
8657  }
8658  }
8659 }
8660 
8673 function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0)
8674 {
8675  if (empty($mesg) && empty($mesgs)) {
8676  dol_syslog("Try to add a message in stack, but value to add is empty message", LOG_WARNING);
8677  } else {
8678  if ($messagekey) {
8679  // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
8680  // TODO
8681  $mesg .= '';
8682  }
8683  if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
8684  if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
8685  dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages');
8686  }
8687  if (empty($mesgs)) {
8688  setEventMessage($mesg, $style, $noduplicate);
8689  } else {
8690  if (!empty($mesg) && !in_array($mesg, $mesgs)) {
8691  setEventMessage($mesg, $style, $noduplicate); // Add message string if not already into array
8692  }
8693  setEventMessage($mesgs, $style, $noduplicate);
8694  }
8695  }
8696  }
8697 }
8698 
8708 function dol_htmloutput_events($disabledoutputofmessages = 0)
8709 {
8710  // Show mesgs
8711  if (isset($_SESSION['dol_events']['mesgs'])) {
8712  if (empty($disabledoutputofmessages)) {
8713  dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
8714  }
8715  unset($_SESSION['dol_events']['mesgs']);
8716  }
8717  // Show errors
8718  if (isset($_SESSION['dol_events']['errors'])) {
8719  if (empty($disabledoutputofmessages)) {
8720  dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
8721  }
8722  unset($_SESSION['dol_events']['errors']);
8723  }
8724 
8725  // Show warnings
8726  if (isset($_SESSION['dol_events']['warnings'])) {
8727  if (empty($disabledoutputofmessages)) {
8728  dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
8729  }
8730  unset($_SESSION['dol_events']['warnings']);
8731  }
8732 }
8733 
8748 function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
8749 {
8750  global $conf, $langs;
8751 
8752  $ret = 0;
8753  $return = '';
8754  $out = '';
8755  $divstart = $divend = '';
8756 
8757  // If inline message with no format, we add it.
8758  if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
8759  $divstart = '<div class="'.$style.' clearboth">';
8760  $divend = '</div>';
8761  }
8762 
8763  if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
8764  $langs->load("errors");
8765  $out .= $divstart;
8766  if (is_array($mesgarray) && count($mesgarray)) {
8767  foreach ($mesgarray as $message) {
8768  $ret++;
8769  $out .= $langs->trans($message);
8770  if ($ret < count($mesgarray)) {
8771  $out .= "<br>\n";
8772  }
8773  }
8774  }
8775  if ($mesgstring) {
8776  $ret++;
8777  $out .= $langs->trans($mesgstring);
8778  }
8779  $out .= $divend;
8780  }
8781 
8782  if ($out) {
8783  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) {
8784  $return = '<script nonce="'.getNonce().'">
8785  $(document).ready(function() {
8786  var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").'
8787  if (block) {
8788  $.dolEventValid("","'.dol_escape_js($out).'");
8789  } else {
8790  /* jnotify(message, preset of message type, keepmessage) */
8791  $.jnotify("'.dol_escape_js($out).'",
8792  "'.($style == "ok" ? 3000 : $style).'",
8793  '.($style == "ok" ? "false" : "true").',
8794  { remove: function (){} } );
8795  }
8796  });
8797  </script>';
8798  } else {
8799  $return = $out;
8800  }
8801  }
8802 
8803  return $return;
8804 }
8805 
8817 function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8818 {
8819  return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8820 }
8821 
8835 function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
8836 {
8837  if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
8838  return;
8839  }
8840 
8841  $iserror = 0;
8842  $iswarning = 0;
8843  if (is_array($mesgarray)) {
8844  foreach ($mesgarray as $val) {
8845  if ($val && preg_match('/class="error"/i', $val)) {
8846  $iserror++;
8847  break;
8848  }
8849  if ($val && preg_match('/class="warning"/i', $val)) {
8850  $iswarning++;
8851  break;
8852  }
8853  }
8854  } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
8855  $iserror++;
8856  } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
8857  $iswarning++;
8858  }
8859  if ($style == 'error') {
8860  $iserror++;
8861  }
8862  if ($style == 'warning') {
8863  $iswarning++;
8864  }
8865 
8866  if ($iserror || $iswarning) {
8867  // Remove div from texts
8868  $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
8869  $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
8870  $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
8871  // Remove div from texts array
8872  if (is_array($mesgarray)) {
8873  $newmesgarray = array();
8874  foreach ($mesgarray as $val) {
8875  if (is_string($val)) {
8876  $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
8877  $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
8878  $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
8879  $newmesgarray[] = $tmpmesgstring;
8880  } else {
8881  dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
8882  }
8883  }
8884  $mesgarray = $newmesgarray;
8885  }
8886  print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
8887  } else {
8888  print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
8889  }
8890 }
8891 
8903 function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8904 {
8905  dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8906 }
8907 
8922 function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
8923 {
8924  // Clean parameters
8925  $order = strtolower($order);
8926 
8927  if (is_array($array)) {
8928  $sizearray = count($array);
8929  if ($sizearray > 0) {
8930  $temp = array();
8931  foreach (array_keys($array) as $key) {
8932  if (is_object($array[$key])) {
8933  $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
8934  } else {
8935  $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
8936  }
8937  if ($natsort == -1) {
8938  $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
8939  }
8940  }
8941 
8942  if (empty($natsort) || $natsort == -1) {
8943  if ($order == 'asc') {
8944  asort($temp);
8945  } else {
8946  arsort($temp);
8947  }
8948  } else {
8949  if ($case_sensitive) {
8950  natsort($temp);
8951  } else {
8952  natcasesort($temp); // natecasesort is not sensible to case
8953  }
8954  if ($order != 'asc') {
8955  $temp = array_reverse($temp, true);
8956  }
8957  }
8958 
8959  $sorted = array();
8960 
8961  foreach (array_keys($temp) as $key) {
8962  (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
8963  }
8964 
8965  return $sorted;
8966  }
8967  }
8968  return $array;
8969 }
8970 
8971 
8978 function utf8_check($str)
8979 {
8980  $str = (string) $str; // Sometimes string is an int.
8981 
8982  // We must use here a binary strlen function (so not dol_strlen)
8983  $strLength = dol_strlen($str);
8984  for ($i = 0; $i < $strLength; $i++) {
8985  if (ord($str[$i]) < 0x80) {
8986  continue; // 0bbbbbbb
8987  } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
8988  $n = 1; // 110bbbbb
8989  } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
8990  $n = 2; // 1110bbbb
8991  } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
8992  $n = 3; // 11110bbb
8993  } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
8994  $n = 4; // 111110bb
8995  } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
8996  $n = 5; // 1111110b
8997  } else {
8998  return false; // Does not match any model
8999  }
9000  for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
9001  if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
9002  return false;
9003  }
9004  }
9005  }
9006  return true;
9007 }
9008 
9015 function utf8_valid($str)
9016 {
9017  /* 2 other methods to test if string is utf8
9018  $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
9019  $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
9020  */
9021  return preg_match('//u', $str) ? true : false;
9022 }
9023 
9024 
9031 function ascii_check($str)
9032 {
9033  if (function_exists('mb_check_encoding')) {
9034  //if (mb_detect_encoding($str, 'ASCII', true) return false;
9035  if (!mb_check_encoding($str, 'ASCII')) {
9036  return false;
9037  }
9038  } else {
9039  if (preg_match('/[^\x00-\x7f]/', $str)) {
9040  return false; // Contains a byte > 7f
9041  }
9042  }
9043 
9044  return true;
9045 }
9046 
9047 
9055 function dol_osencode($str)
9056 {
9057  global $conf;
9058 
9059  $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
9060  if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
9061  $tmp = 'iso-8859-1'; // By default for windows
9062  }
9063  if (empty($tmp)) {
9064  $tmp = 'utf-8'; // By default for other
9065  }
9066  if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) {
9067  $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING;
9068  }
9069 
9070  if ($tmp == 'iso-8859-1') {
9071  return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
9072  }
9073  return $str;
9074 }
9075 
9076 
9091 function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
9092 {
9093  global $cache_codes;
9094 
9095  // If key empty
9096  if ($key == '') {
9097  return '';
9098  }
9099 
9100  // Check in cache
9101  if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
9102  return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
9103  }
9104 
9105  dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
9106 
9107  $sql = "SELECT ".$fieldid." as valuetoget";
9108  $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
9109  $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
9110  if (!empty($entityfilter)) {
9111  $sql .= " AND entity IN (".getEntity($tablename).")";
9112  }
9113  if ($filters) {
9114  $sql .= $filters;
9115  }
9116 
9117  $resql = $db->query($sql);
9118  if ($resql) {
9119  $obj = $db->fetch_object($resql);
9120  if ($obj) {
9121  $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
9122  } else {
9123  $cache_codes[$tablename][$key][$fieldid] = '';
9124  }
9125  $db->free($resql);
9126  return $cache_codes[$tablename][$key][$fieldid];
9127  } else {
9128  return -1;
9129  }
9130 }
9131 
9138 function verifCond($strToEvaluate)
9139 {
9140  global $user, $conf, $langs;
9141  global $leftmenu;
9142  global $rights; // To export to dol_eval function
9143 
9144  //print $strToEvaluate."<br>\n";
9145  $rights = true;
9146  if (isset($strToEvaluate) && $strToEvaluate !== '') {
9147  //var_dump($strToEvaluate);
9148  $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
9149  $rights = $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
9150  //var_dump($rights);
9151  }
9152  return $rights;
9153 }
9154 
9165 function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
9166 {
9167  // Only global variables can be changed by eval function and returned to caller
9168  global $db, $langs, $user, $conf, $website, $websitepage;
9169  global $action, $mainmenu, $leftmenu;
9170  global $mysoc;
9171  global $objectoffield;
9172 
9173  // Old variables used
9174  global $rights;
9175  global $object;
9176  global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
9177  global $soc; // For backward compatibility
9178 
9179  try {
9180  // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
9181  if ($onlysimplestring == '1') {
9182  // We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
9183  // We must accept: '$conf->barcode->enabled || preg_match(\'/^AAA/\',$leftmenu)'
9184  // We must accept: '$user->rights->cabinetmed->read && !$object->canvas=="patient@cabinetmed"'
9185  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@', '/').']/i', $s)) {
9186  if ($returnvalue) {
9187  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
9188  } else {
9189  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
9190  return '';
9191  }
9192  // TODO
9193  // We can exclude all parenthesis ( that are not '($db' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match(' and 'isModEnabled('
9194  // ...
9195  }
9196  } elseif ($onlysimplestring == '2') {
9197  // 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"
9198  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@[]', '/').']/i', $s)) {
9199  if ($returnvalue) {
9200  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
9201  } else {
9202  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
9203  return '';
9204  }
9205  // TODO
9206  // We can exclude all parenthesis ( that are not '($db' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match(' and 'isModEnabled('
9207  // ...
9208  }
9209  }
9210  if (is_array($s) || $s === 'Array') {
9211  return 'Bad string syntax to evaluate (value is Array) '.var_export($s, true);
9212  }
9213  if (strpos($s, '::') !== false) {
9214  if ($returnvalue) {
9215  return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
9216  } else {
9217  dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s);
9218  return '';
9219  }
9220  }
9221  if (strpos($s, '`') !== false) {
9222  if ($returnvalue) {
9223  return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
9224  } else {
9225  dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s);
9226  return '';
9227  }
9228  }
9229  if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
9230  if ($returnvalue) {
9231  return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
9232  } else {
9233  dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s);
9234  return '';
9235  }
9236  }
9237 
9238  // We block use of php exec or php file functions
9239  $forbiddenphpstrings = array('$$');
9240  $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST'));
9241 
9242  $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen", "eval", "dol_eval", "executeCLI", "verifCond", "base64_decode");
9243  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
9244  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
9245 
9246  $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
9247 
9248  do {
9249  $oldstringtoclean = $s;
9250  $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
9251  $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
9252  //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
9253  } while ($oldstringtoclean != $s);
9254 
9255  if (strpos($s, '__forbiddenstring__') !== false) {
9256  dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
9257  if ($returnvalue) {
9258  return 'Bad string syntax to evaluate: '.$s;
9259  } else {
9260  dol_syslog('Bad string syntax to evaluate: '.$s);
9261  return '';
9262  }
9263  }
9264 
9265  //print $s."<br>\n";
9266  if ($returnvalue) {
9267  if ($hideerrors) {
9268  return @eval('return '.$s.';');
9269  } else {
9270  return eval('return '.$s.';');
9271  }
9272  } else {
9273  if ($hideerrors) {
9274  @eval($s);
9275  } else {
9276  eval($s);
9277  }
9278  }
9279  } catch (Error $e) {
9280  $error = 'dol_eval try/catch error : ';
9281  $error .= $e->getMessage();
9282  dol_syslog($error);
9283  }
9284 }
9285 
9292 function dol_validElement($element)
9293 {
9294  return (trim($element) != '');
9295 }
9296 
9305 function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
9306 {
9307  if (empty($codelang)) {
9308  return '';
9309  }
9310 
9311  if ($codelang == 'auto') {
9312  return '<span class="fa fa-language"></span>';
9313  }
9314 
9315  $langtocountryflag = array(
9316  'ar_AR' => '',
9317  'ca_ES' => 'catalonia',
9318  'da_DA' => 'dk',
9319  'fr_CA' => 'mq',
9320  'sv_SV' => 'se',
9321  'sw_SW' => 'unknown',
9322  'AQ' => 'unknown',
9323  'CW' => 'unknown',
9324  'IM' => 'unknown',
9325  'JE' => 'unknown',
9326  'MF' => 'unknown',
9327  'BL' => 'unknown',
9328  'SX' => 'unknown'
9329  );
9330 
9331  if (isset($langtocountryflag[$codelang])) {
9332  $flagImage = $langtocountryflag[$codelang];
9333  } else {
9334  $tmparray = explode('_', $codelang);
9335  $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
9336  }
9337 
9338  return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
9339 }
9340 
9348 function getLanguageCodeFromCountryCode($countrycode)
9349 {
9350  global $mysoc;
9351 
9352  if (empty($countrycode)) {
9353  return null;
9354  }
9355 
9356  if (strtoupper($countrycode) == 'MQ') {
9357  return 'fr_CA';
9358  }
9359  if (strtoupper($countrycode) == 'SE') {
9360  return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
9361  }
9362  if (strtoupper($countrycode) == 'CH') {
9363  if ($mysoc->country_code == 'FR') {
9364  return 'fr_CH';
9365  }
9366  if ($mysoc->country_code == 'DE') {
9367  return 'de_CH';
9368  }
9369  if ($mysoc->country_code == 'IT') {
9370  return 'it_CH';
9371  }
9372  }
9373 
9374  // Locale list taken from:
9375  // http://stackoverflow.com/questions/3191664/
9376  // list-of-all-locales-and-their-short-codes
9377  $locales = array(
9378  'af-ZA',
9379  'am-ET',
9380  'ar-AE',
9381  'ar-BH',
9382  'ar-DZ',
9383  'ar-EG',
9384  'ar-IQ',
9385  'ar-JO',
9386  'ar-KW',
9387  'ar-LB',
9388  'ar-LY',
9389  'ar-MA',
9390  'ar-OM',
9391  'ar-QA',
9392  'ar-SA',
9393  'ar-SY',
9394  'ar-TN',
9395  'ar-YE',
9396  //'as-IN', // Moved after en-IN
9397  'ba-RU',
9398  'be-BY',
9399  'bg-BG',
9400  'bn-BD',
9401  //'bn-IN', // Moved after en-IN
9402  'bo-CN',
9403  'br-FR',
9404  'ca-ES',
9405  'co-FR',
9406  'cs-CZ',
9407  'cy-GB',
9408  'da-DK',
9409  'de-AT',
9410  'de-CH',
9411  'de-DE',
9412  'de-LI',
9413  'de-LU',
9414  'dv-MV',
9415  'el-GR',
9416  'en-AU',
9417  'en-BZ',
9418  'en-CA',
9419  'en-GB',
9420  'en-IE',
9421  'en-IN',
9422  'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
9423  'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
9424  'en-JM',
9425  'en-MY',
9426  'en-NZ',
9427  'en-PH',
9428  'en-SG',
9429  'en-TT',
9430  'en-US',
9431  'en-ZA',
9432  'en-ZW',
9433  'es-AR',
9434  'es-BO',
9435  'es-CL',
9436  'es-CO',
9437  'es-CR',
9438  'es-DO',
9439  'es-EC',
9440  'es-ES',
9441  'es-GT',
9442  'es-HN',
9443  'es-MX',
9444  'es-NI',
9445  'es-PA',
9446  'es-PE',
9447  'es-PR',
9448  'es-PY',
9449  'es-SV',
9450  'es-US',
9451  'es-UY',
9452  'es-VE',
9453  'et-EE',
9454  'eu-ES',
9455  'fa-IR',
9456  'fi-FI',
9457  'fo-FO',
9458  'fr-BE',
9459  'fr-CA',
9460  'fr-CH',
9461  'fr-FR',
9462  'fr-LU',
9463  'fr-MC',
9464  'fy-NL',
9465  'ga-IE',
9466  'gd-GB',
9467  'gl-ES',
9468  'gu-IN',
9469  'he-IL',
9470  'hi-IN',
9471  'hr-BA',
9472  'hr-HR',
9473  'hu-HU',
9474  'hy-AM',
9475  'id-ID',
9476  'ig-NG',
9477  'ii-CN',
9478  'is-IS',
9479  'it-CH',
9480  'it-IT',
9481  'ja-JP',
9482  'ka-GE',
9483  'kk-KZ',
9484  'kl-GL',
9485  'km-KH',
9486  'kn-IN',
9487  'ko-KR',
9488  'ky-KG',
9489  'lb-LU',
9490  'lo-LA',
9491  'lt-LT',
9492  'lv-LV',
9493  'mi-NZ',
9494  'mk-MK',
9495  'ml-IN',
9496  'mn-MN',
9497  'mr-IN',
9498  'ms-BN',
9499  'ms-MY',
9500  'mt-MT',
9501  'nb-NO',
9502  'ne-NP',
9503  'nl-BE',
9504  'nl-NL',
9505  'nn-NO',
9506  'oc-FR',
9507  'or-IN',
9508  'pa-IN',
9509  'pl-PL',
9510  'ps-AF',
9511  'pt-BR',
9512  'pt-PT',
9513  'rm-CH',
9514  'ro-MD',
9515  'ro-RO',
9516  'ru-RU',
9517  'rw-RW',
9518  'sa-IN',
9519  'se-FI',
9520  'se-NO',
9521  'se-SE',
9522  'si-LK',
9523  'sk-SK',
9524  'sl-SI',
9525  'sq-AL',
9526  'sv-FI',
9527  'sv-SE',
9528  'sw-KE',
9529  'ta-IN',
9530  'te-IN',
9531  'th-TH',
9532  'tk-TM',
9533  'tn-ZA',
9534  'tr-TR',
9535  'tt-RU',
9536  'ug-CN',
9537  'uk-UA',
9538  'ur-PK',
9539  'vi-VN',
9540  'wo-SN',
9541  'xh-ZA',
9542  'yo-NG',
9543  'zh-CN',
9544  'zh-HK',
9545  'zh-MO',
9546  'zh-SG',
9547  'zh-TW',
9548  'zu-ZA',
9549  );
9550 
9551  $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
9552  if (in_array($buildprimarykeytotest, $locales)) {
9553  return strtolower($countrycode).'_'.strtoupper($countrycode);
9554  }
9555 
9556  if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
9557  foreach ($locales as $locale) {
9558  $locale_language = locale_get_primary_language($locale);
9559  $locale_region = locale_get_region($locale);
9560  if (strtoupper($countrycode) == $locale_region) {
9561  //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
9562  return strtolower($locale_language).'_'.strtoupper($locale_region);
9563  }
9564  }
9565  } else {
9566  dol_syslog("Warning Exention php-intl is not available", LOG_WARNING);
9567  }
9568 
9569  return null;
9570 }
9571 
9602 function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
9603 {
9604  global $hookmanager, $db;
9605 
9606  if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
9607  foreach ($conf->modules_parts['tabs'][$type] as $value) {
9608  $values = explode(':', $value);
9609 
9610  $reg = array();
9611  if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
9612  if (count($values) == 6) {
9613  // new declaration with permissions:
9614  // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9615  // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9616  if ($values[0] != $type) {
9617  continue;
9618  }
9619 
9620  if (verifCond($values[4])) {
9621  if ($values[3]) {
9622  if ($filterorigmodule) { // If a filter of module origin has been requested
9623  if (strpos($values[3], '@')) { // This is an external module
9624  if ($filterorigmodule != 'external') {
9625  continue;
9626  }
9627  } else { // This looks a core module
9628  if ($filterorigmodule != 'core') {
9629  continue;
9630  }
9631  }
9632  }
9633  $langs->load($values[3]);
9634  }
9635  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9636  // If label is "SUBSTITUION_..."
9637  $substitutionarray = array();
9638  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9639  $label = make_substitutions($reg[1], $substitutionarray);
9640  } else {
9641  // If label is "Label,Class,File,Method", we call the method to show content inside the badge
9642  $labeltemp = explode(',', $values[2]);
9643  $label = $langs->trans($labeltemp[0]);
9644 
9645  if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
9646  dol_include_once($labeltemp[2]);
9647  $classtoload = $labeltemp[1];
9648  if (class_exists($classtoload)) {
9649  $obj = new $classtoload($db);
9650  $function = $labeltemp[3];
9651  if ($obj && $function && method_exists($obj, $function)) {
9652  $nbrec = $obj->$function($object->id, $obj);
9653  if (!empty($nbrec)) {
9654  $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
9655  }
9656  }
9657  }
9658  }
9659  }
9660 
9661  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
9662  $head[$h][1] = $label;
9663  $head[$h][2] = str_replace('+', '', $values[1]);
9664  $h++;
9665  }
9666  } elseif (count($values) == 5) { // case deprecated
9667  dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
9668 
9669  if ($values[0] != $type) {
9670  continue;
9671  }
9672  if ($values[3]) {
9673  if ($filterorigmodule) { // If a filter of module origin has been requested
9674  if (strpos($values[3], '@')) { // This is an external module
9675  if ($filterorigmodule != 'external') {
9676  continue;
9677  }
9678  } else { // This looks a core module
9679  if ($filterorigmodule != 'core') {
9680  continue;
9681  }
9682  }
9683  }
9684  $langs->load($values[3]);
9685  }
9686  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9687  $substitutionarray = array();
9688  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9689  $label = make_substitutions($reg[1], $substitutionarray);
9690  } else {
9691  $label = $langs->trans($values[2]);
9692  }
9693 
9694  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
9695  $head[$h][1] = $label;
9696  $head[$h][2] = str_replace('+', '', $values[1]);
9697  $h++;
9698  }
9699  } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
9700  if ($values[0] != $type) {
9701  continue;
9702  }
9703  $tabname = str_replace('-', '', $values[1]);
9704  foreach ($head as $key => $val) {
9705  $condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
9706  //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
9707  if ($head[$key][2] == $tabname && $condition) {
9708  unset($head[$key]);
9709  break;
9710  }
9711  }
9712  }
9713  }
9714  }
9715 
9716  // No need to make a return $head. Var is modified as a reference
9717  if (!empty($hookmanager)) {
9718  $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
9719  $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
9720  if ($reshook > 0) { // Hook ask to replace completely the array
9721  $head = $hookmanager->resArray;
9722  } else { // Hook
9723  $head = array_merge($head, $hookmanager->resArray);
9724  }
9725  $h = count($head);
9726  }
9727 }
9728 
9740 function printCommonFooter($zone = 'private')
9741 {
9742  global $conf, $hookmanager, $user, $debugbar;
9743  global $action;
9744  global $micro_start_time;
9745 
9746  if ($zone == 'private') {
9747  print "\n".'<!-- Common footer for private page -->'."\n";
9748  } else {
9749  print "\n".'<!-- Common footer for public page -->'."\n";
9750  }
9751 
9752  // A div to store page_y POST parameter so we can read it using javascript
9753  print "\n<!-- A div to store page_y POST parameter -->\n";
9754  print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
9755 
9756  $parameters = array();
9757  $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
9758  if (empty($reshook)) {
9759  if (!empty($conf->global->MAIN_HTML_FOOTER)) {
9760  print $conf->global->MAIN_HTML_FOOTER."\n";
9761  }
9762 
9763  print "\n";
9764  if (!empty($conf->use_javascript_ajax)) {
9765  print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and madatory fields, tuning info, ... -->\n";
9766  print '<script>'."\n";
9767  print 'jQuery(document).ready(function() {'."\n";
9768 
9769  if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
9770  print "\n";
9771  print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
9772  print 'jQuery("li.menuhider").click(function(event) {';
9773  print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
9774  print ' console.log("We click on .menuhider");'."\n";
9775  print ' $("body").toggleClass("sidebar-collapse")'."\n";
9776  print '});'."\n";
9777  }
9778 
9779  // Management of focus and mandatory for fields
9780  if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))) || ((empty($action) || $action == 'addline') && (preg_match('/card\.php/', $_SERVER["PHP_SELF"])))) {
9781  print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
9782  $relativepathstring = $_SERVER["PHP_SELF"];
9783  // Clean $relativepathstring
9784  if (constant('DOL_URL_ROOT')) {
9785  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
9786  }
9787  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
9788  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
9789  //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
9790  if (!empty($user->default_values[$relativepathstring]['focus'])) {
9791  foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
9792  $qualified = 0;
9793  if ($defkey != '_noquery_') {
9794  $tmpqueryarraytohave = explode('&', $defkey);
9795  $foundintru = 0;
9796  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9797  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9798  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9799  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9800  $foundintru = 1;
9801  }
9802  }
9803  if (!$foundintru) {
9804  $qualified = 1;
9805  }
9806  //var_dump($defkey.'-'.$qualified);
9807  } else {
9808  $qualified = 1;
9809  }
9810 
9811  if ($qualified) {
9812  foreach ($defval as $paramkey => $paramval) {
9813  // Set focus on field
9814  print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
9815  print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
9816  print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
9817  }
9818  }
9819  }
9820  }
9821  if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
9822  foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
9823  $qualified = 0;
9824  if ($defkey != '_noquery_') {
9825  $tmpqueryarraytohave = explode('&', $defkey);
9826  $foundintru = 0;
9827  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9828  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9829  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9830  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9831  $foundintru = 1;
9832  }
9833  }
9834  if (!$foundintru) {
9835  $qualified = 1;
9836  }
9837  //var_dump($defkey.'-'.$qualified);
9838  } else {
9839  $qualified = 1;
9840  }
9841 
9842  if ($qualified) {
9843  foreach ($defval as $paramkey => $paramval) {
9844  // Add property 'required' on input
9845  print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9846  print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9847  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";
9848  print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9849  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
9850  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
9851 
9852  // Add 'field required' class on closest td for all input elements : input, textarea and select
9853  print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
9854  }
9855  }
9856  }
9857  }
9858  }
9859 
9860  print '});'."\n";
9861 
9862  // End of tuning
9863  if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) {
9864  print "\n";
9865  print "/* JS CODE TO ENABLE to add memory info */\n";
9866  print 'window.console && console.log("';
9867  if (!empty($conf->global->MEMCACHED_SERVER)) {
9868  print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
9869  }
9870  print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off');
9871  if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
9872  $micro_end_time = microtime(true);
9873  print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
9874  }
9875 
9876  if (function_exists("memory_get_usage")) {
9877  print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
9878  }
9879  if (function_exists("memory_get_peak_usage")) {
9880  print ' - Real mem peak: '.memory_get_peak_usage(true);
9881  }
9882  if (function_exists("zend_loader_file_encoded")) {
9883  print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
9884  }
9885  print '");'."\n";
9886  }
9887 
9888  print "\n".'</script>'."\n";
9889 
9890  // Google Analytics
9891  // TODO Add a hook here
9892  if (isModEnabled('google') && !empty($conf->global->MAIN_GOOGLE_AN_ID)) {
9893  $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID);
9894  foreach ($tmptagarray as $tmptag) {
9895  print "\n";
9896  print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
9897  print '
9898  <!-- Global site tag (gtag.js) - Google Analytics -->
9899  <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
9900  <script>
9901  window.dataLayer = window.dataLayer || [];
9902  function gtag(){dataLayer.push(arguments);}
9903  gtag(\'js\', new Date());
9904 
9905  gtag(\'config\', \''.trim($tmptag).'\');
9906  </script>';
9907  print "\n";
9908  }
9909  }
9910  }
9911 
9912  // Add Xdebug coverage of code
9913  if (defined('XDEBUGCOVERAGE')) {
9914  print_r(xdebug_get_code_coverage());
9915  }
9916 
9917  // Add DebugBar data
9918  if (!empty($user->rights->debugbar->read) && is_object($debugbar)) {
9919  $debugbar['time']->stopMeasure('pageaftermaster');
9920  print '<!-- Output debugbar data -->'."\n";
9921  $renderer = $debugbar->getRenderer();
9922  print $debugbar->getRenderer()->render();
9923  } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
9924  print "\n";
9925  print "<!-- Start of log output\n";
9926  //print '<div class="hidden">'."\n";
9927  foreach ($conf->logbuffer as $logline) {
9928  print $logline."<br>\n";
9929  }
9930  //print '</div>'."\n";
9931  print "End of log output -->\n";
9932  }
9933  }
9934 }
9935 
9945 function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
9946 {
9947  if (is_null($string)) {
9948  return array();
9949  }
9950 
9951  if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
9952  // This is a regex string
9953  $newdelimiter = $delimiter;
9954  } else {
9955  // This is a simple string
9956  $newdelimiter = preg_quote($delimiter, '/');
9957  }
9958 
9959  if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
9960  $ka = array();
9961  foreach ($a as $s) { // each part
9962  if ($s) {
9963  if ($pos = strpos($s, $kv)) { // key/value delimiter
9964  $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
9965  } else { // key delimiter not found
9966  $ka[] = trim($s);
9967  }
9968  }
9969  }
9970  return $ka;
9971  }
9972 
9973  return array();
9974 }
9975 
9976 
9983 function dol_set_focus($selector)
9984 {
9985  print "\n".'<!-- Set focus onto a specific field -->'."\n";
9986  print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
9987 }
9988 
9989 
9997 function dol_getmypid()
9998 {
9999  if (!function_exists('getmypid')) {
10000  return mt_rand(99900000, 99965535);
10001  } else {
10002  return getmypid(); // May be a number on 64 bits (depending on OS)
10003  }
10004 }
10005 
10006 
10024 function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
10025 {
10026  global $db, $langs;
10027 
10028  $value = trim($value);
10029 
10030  if ($mode == 0) {
10031  $value = preg_replace('/\*/', '%', $value); // Replace * with %
10032  }
10033  if ($mode == 1) {
10034  $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
10035  }
10036 
10037  $value = preg_replace('/\s*\|\s*/', '|', $value);
10038 
10039  $crits = explode(' ', $value);
10040  $res = '';
10041  if (!is_array($fields)) {
10042  $fields = array($fields);
10043  }
10044 
10045  $i1 = 0; // count the nb of and criteria added (all fields / criterias)
10046  foreach ($crits as $crit) { // Loop on each AND criteria
10047  $crit = trim($crit);
10048  $i2 = 0; // count the nb of valid criteria added for this this first criteria
10049  $newres = '';
10050  foreach ($fields as $field) {
10051  if ($mode == 1) {
10052  $tmpcrits = explode('|', $crit);
10053  $i3 = 0; // count the nb of valid criteria added for this current field
10054  foreach ($tmpcrits as $tmpcrit) {
10055  if ($tmpcrit !== '0' && empty($tmpcrit)) {
10056  continue;
10057  }
10058  $tmpcrit = trim($tmpcrit);
10059 
10060  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
10061 
10062  $operator = '=';
10063  $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
10064 
10065  $reg = array();
10066  preg_match('/([!<>=]+)/', $tmpcrit, $reg);
10067  if (!empty($reg[1])) {
10068  $operator = $reg[1];
10069  }
10070  if ($newcrit != '') {
10071  $numnewcrit = price2num($newcrit);
10072  if (is_numeric($numnewcrit)) {
10073  $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
10074  } else {
10075  $newres .= '1 = 2'; // force false, we received a corrupted data
10076  }
10077  $i3++; // a criteria was added to string
10078  }
10079  }
10080  $i2++; // a criteria for 1 more field was added to string
10081  } elseif ($mode == 2 || $mode == -2) {
10082  $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
10083  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
10084  $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
10085  if ($mode == -2) {
10086  $newres .= ' OR '.$field.' IS NULL';
10087  }
10088  $i2++; // a criteria for 1 more field was added to string
10089  } elseif ($mode == 3 || $mode == -3) {
10090  $tmparray = explode(',', $crit);
10091  if (count($tmparray)) {
10092  $listofcodes = '';
10093  foreach ($tmparray as $val) {
10094  $val = trim($val);
10095  if ($val) {
10096  $listofcodes .= ($listofcodes ? ',' : '');
10097  $listofcodes .= "'".$db->escape($val)."'";
10098  }
10099  }
10100  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
10101  $i2++; // a criteria for 1 more field was added to string
10102  }
10103  if ($mode == -3) {
10104  $newres .= ' OR '.$field.' IS NULL';
10105  }
10106  } elseif ($mode == 4) {
10107  $tmparray = explode(',', $crit);
10108  if (count($tmparray)) {
10109  $listofcodes = '';
10110  foreach ($tmparray as $val) {
10111  $val = trim($val);
10112  if ($val) {
10113  $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
10114  $newres .= ' OR '.$field." = '".$db->escape($val)."'";
10115  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
10116  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
10117  $newres .= ')';
10118  $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)
10119  }
10120  }
10121  }
10122  } else { // $mode=0
10123  $tmpcrits = explode('|', $crit);
10124  $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
10125  foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
10126  if ($tmpcrit !== '0' && empty($tmpcrit)) {
10127  continue;
10128  }
10129  $tmpcrit = trim($tmpcrit);
10130 
10131  if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
10132  $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
10133  } else {
10134  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
10135  }
10136 
10137  if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
10138  $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
10139  } else {
10140  $tmpcrit2 = $tmpcrit;
10141  $tmpbefore = '%';
10142  $tmpafter = '%';
10143  $tmps = '';
10144 
10145  if (preg_match('/^!/', $tmpcrit)) {
10146  $tmps .= $field." NOT LIKE "; // ! as exclude character
10147  $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
10148  } else {
10149  $tmps .= $field." LIKE ";
10150  }
10151  $tmps .= "'";
10152 
10153  if (preg_match('/^[\^\$]/', $tmpcrit)) {
10154  $tmpbefore = '';
10155  $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
10156  }
10157  if (preg_match('/[\^\$]$/', $tmpcrit)) {
10158  $tmpafter = '';
10159  $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
10160  }
10161 
10162  if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
10163  $tmps = "(".$tmps;
10164  }
10165  $newres .= $tmps;
10166  $newres .= $tmpbefore;
10167  $newres .= $db->escape($tmpcrit2);
10168  $newres .= $tmpafter;
10169  $newres .= "'";
10170  if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
10171  $newres .= " OR ".$field." IS NULL)";
10172  }
10173  }
10174 
10175  $i3++;
10176  }
10177 
10178  $i2++; // a criteria for 1 more field was added to string
10179  }
10180  }
10181 
10182  if ($newres) {
10183  $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
10184  }
10185  $i1++;
10186  }
10187  $res = ($nofirstand ? "" : " AND ")."(".$res.")";
10188 
10189  return $res;
10190 }
10191 
10198 function showDirectDownloadLink($object)
10199 {
10200  global $conf, $langs;
10201 
10202  $out = '';
10203  $url = $object->getLastMainDocLink($object->element);
10204 
10205  $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
10206  if ($url) {
10207  $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
10208  $out .= ajax_autoselect("directdownloadlink", 0);
10209  } else {
10210  $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
10211  }
10212 
10213  return $out;
10214 }
10215 
10224 function getImageFileNameForSize($file, $extName, $extImgTarget = '')
10225 {
10226  $dirName = dirname($file);
10227  if ($dirName == '.') {
10228  $dirName = '';
10229  }
10230 
10231  $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
10232  $fileName = basename($fileName);
10233 
10234  if (empty($extImgTarget)) {
10235  $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
10236  }
10237  if (empty($extImgTarget)) {
10238  $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
10239  }
10240  if (empty($extImgTarget)) {
10241  $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
10242  }
10243  if (empty($extImgTarget)) {
10244  $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
10245  }
10246  if (empty($extImgTarget)) {
10247  $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
10248  }
10249  if (empty($extImgTarget)) {
10250  $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
10251  }
10252 
10253  if (!$extImgTarget) {
10254  return $file;
10255  }
10256 
10257  $subdir = '';
10258  if ($extName) {
10259  $subdir = 'thumbs/';
10260  }
10261 
10262  return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
10263 }
10264 
10265 
10275 function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
10276 {
10277  global $conf, $langs;
10278 
10279  if (empty($conf->use_javascript_ajax)) {
10280  return '';
10281  }
10282 
10283  $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
10284 
10285  if ($alldata == 1) {
10286  if ($isAllowedForPreview) {
10287  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));
10288  } else {
10289  return array();
10290  }
10291  }
10292 
10293  // old behavior, return a string
10294  if ($isAllowedForPreview) {
10295  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')).'\')';
10296  } else {
10297  return '';
10298  }
10299 }
10300 
10301 
10310 function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
10311 {
10312  global $langs;
10313  $out = '<script nonce="'.getNonce().'">
10314  jQuery(document).ready(function () {
10315  jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
10316  });
10317  </script>';
10318  if ($addlink) {
10319  if ($textonlink === 'image') {
10320  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
10321  } else {
10322  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
10323  }
10324  }
10325  return $out;
10326 }
10327 
10335 function dolIsAllowedForPreview($file)
10336 {
10337  global $conf;
10338 
10339  // Check .noexe extension in filename
10340  if (preg_match('/\.noexe$/i', $file)) {
10341  return 0;
10342  }
10343 
10344  // Check mime types
10345  $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
10346  if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) {
10347  $mime_preview[] = 'svg+xml';
10348  }
10349  //$mime_preview[]='vnd.oasis.opendocument.presentation';
10350  //$mime_preview[]='archive';
10351  $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
10352  if ($num_mime !== false) {
10353  return 1;
10354  }
10355 
10356  // By default, not allowed for preview
10357  return 0;
10358 }
10359 
10360 
10370 function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
10371 {
10372  $mime = $default;
10373  $imgmime = 'other.png';
10374  $famime = 'file-o';
10375  $srclang = '';
10376 
10377  $tmpfile = preg_replace('/\.noexe$/', '', $file);
10378 
10379  // Plain text files
10380  if (preg_match('/\.txt$/i', $tmpfile)) {
10381  $mime = 'text/plain';
10382  $imgmime = 'text.png';
10383  $famime = 'file-text-o';
10384  }
10385  if (preg_match('/\.rtx$/i', $tmpfile)) {
10386  $mime = 'text/richtext';
10387  $imgmime = 'text.png';
10388  $famime = 'file-text-o';
10389  }
10390  if (preg_match('/\.csv$/i', $tmpfile)) {
10391  $mime = 'text/csv';
10392  $imgmime = 'text.png';
10393  $famime = 'file-text-o';
10394  }
10395  if (preg_match('/\.tsv$/i', $tmpfile)) {
10396  $mime = 'text/tab-separated-values';
10397  $imgmime = 'text.png';
10398  $famime = 'file-text-o';
10399  }
10400  if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
10401  $mime = 'text/plain';
10402  $imgmime = 'text.png';
10403  $famime = 'file-text-o';
10404  }
10405  if (preg_match('/\.ini$/i', $tmpfile)) {
10406  $mime = 'text/plain';
10407  $imgmime = 'text.png';
10408  $srclang = 'ini';
10409  $famime = 'file-text-o';
10410  }
10411  if (preg_match('/\.md$/i', $tmpfile)) {
10412  $mime = 'text/plain';
10413  $imgmime = 'text.png';
10414  $srclang = 'md';
10415  $famime = 'file-text-o';
10416  }
10417  if (preg_match('/\.css$/i', $tmpfile)) {
10418  $mime = 'text/css';
10419  $imgmime = 'css.png';
10420  $srclang = 'css';
10421  $famime = 'file-text-o';
10422  }
10423  if (preg_match('/\.lang$/i', $tmpfile)) {
10424  $mime = 'text/plain';
10425  $imgmime = 'text.png';
10426  $srclang = 'lang';
10427  $famime = 'file-text-o';
10428  }
10429  // Certificate files
10430  if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) {
10431  $mime = 'text/plain';
10432  $imgmime = 'text.png';
10433  $famime = 'file-text-o';
10434  }
10435  // XML based (HTML/XML/XAML)
10436  if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) {
10437  $mime = 'text/html';
10438  $imgmime = 'html.png';
10439  $srclang = 'html';
10440  $famime = 'file-text-o';
10441  }
10442  if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
10443  $mime = 'text/xml';
10444  $imgmime = 'other.png';
10445  $srclang = 'xml';
10446  $famime = 'file-text-o';
10447  }
10448  if (preg_match('/\.xaml$/i', $tmpfile)) {
10449  $mime = 'text/xml';
10450  $imgmime = 'other.png';
10451  $srclang = 'xaml';
10452  $famime = 'file-text-o';
10453  }
10454  // Languages
10455  if (preg_match('/\.bas$/i', $tmpfile)) {
10456  $mime = 'text/plain';
10457  $imgmime = 'text.png';
10458  $srclang = 'bas';
10459  $famime = 'file-code-o';
10460  }
10461  if (preg_match('/\.(c)$/i', $tmpfile)) {
10462  $mime = 'text/plain';
10463  $imgmime = 'text.png';
10464  $srclang = 'c';
10465  $famime = 'file-code-o';
10466  }
10467  if (preg_match('/\.(cpp)$/i', $tmpfile)) {
10468  $mime = 'text/plain';
10469  $imgmime = 'text.png';
10470  $srclang = 'cpp';
10471  $famime = 'file-code-o';
10472  }
10473  if (preg_match('/\.cs$/i', $tmpfile)) {
10474  $mime = 'text/plain';
10475  $imgmime = 'text.png';
10476  $srclang = 'cs';
10477  $famime = 'file-code-o';
10478  }
10479  if (preg_match('/\.(h)$/i', $tmpfile)) {
10480  $mime = 'text/plain';
10481  $imgmime = 'text.png';
10482  $srclang = 'h';
10483  $famime = 'file-code-o';
10484  }
10485  if (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
10486  $mime = 'text/plain';
10487  $imgmime = 'text.png';
10488  $srclang = 'java';
10489  $famime = 'file-code-o';
10490  }
10491  if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
10492  $mime = 'text/plain';
10493  $imgmime = 'php.png';
10494  $srclang = 'php';
10495  $famime = 'file-code-o';
10496  }
10497  if (preg_match('/\.phtml$/i', $tmpfile)) {
10498  $mime = 'text/plain';
10499  $imgmime = 'php.png';
10500  $srclang = 'php';
10501  $famime = 'file-code-o';
10502  }
10503  if (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
10504  $mime = 'text/plain';
10505  $imgmime = 'pl.png';
10506  $srclang = 'perl';
10507  $famime = 'file-code-o';
10508  }
10509  if (preg_match('/\.sql$/i', $tmpfile)) {
10510  $mime = 'text/plain';
10511  $imgmime = 'text.png';
10512  $srclang = 'sql';
10513  $famime = 'file-code-o';
10514  }
10515  if (preg_match('/\.js$/i', $tmpfile)) {
10516  $mime = 'text/x-javascript';
10517  $imgmime = 'jscript.png';
10518  $srclang = 'js';
10519  $famime = 'file-code-o';
10520  }
10521  // Open office
10522  if (preg_match('/\.odp$/i', $tmpfile)) {
10523  $mime = 'application/vnd.oasis.opendocument.presentation';
10524  $imgmime = 'ooffice.png';
10525  $famime = 'file-powerpoint-o';
10526  }
10527  if (preg_match('/\.ods$/i', $tmpfile)) {
10528  $mime = 'application/vnd.oasis.opendocument.spreadsheet';
10529  $imgmime = 'ooffice.png';
10530  $famime = 'file-excel-o';
10531  }
10532  if (preg_match('/\.odt$/i', $tmpfile)) {
10533  $mime = 'application/vnd.oasis.opendocument.text';
10534  $imgmime = 'ooffice.png';
10535  $famime = 'file-word-o';
10536  }
10537  // MS Office
10538  if (preg_match('/\.mdb$/i', $tmpfile)) {
10539  $mime = 'application/msaccess';
10540  $imgmime = 'mdb.png';
10541  $famime = 'file-o';
10542  }
10543  if (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
10544  $mime = 'application/msword';
10545  $imgmime = 'doc.png';
10546  $famime = 'file-word-o';
10547  }
10548  if (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
10549  $mime = 'application/msword';
10550  $imgmime = 'doc.png';
10551  $famime = 'file-word-o';
10552  }
10553  if (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
10554  $mime = 'application/vnd.ms-excel';
10555  $imgmime = 'xls.png';
10556  $famime = 'file-excel-o';
10557  }
10558  if (preg_match('/\.xla(m)?$/i', $tmpfile)) {
10559  $mime = 'application/vnd.ms-excel';
10560  $imgmime = 'xls.png';
10561  $famime = 'file-excel-o';
10562  }
10563  if (preg_match('/\.xls$/i', $tmpfile)) {
10564  $mime = 'application/vnd.ms-excel';
10565  $imgmime = 'xls.png';
10566  $famime = 'file-excel-o';
10567  }
10568  if (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
10569  $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
10570  $imgmime = 'xls.png';
10571  $famime = 'file-excel-o';
10572  }
10573  if (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
10574  $mime = 'application/vnd.ms-powerpoint';
10575  $imgmime = 'ppt.png';
10576  $famime = 'file-powerpoint-o';
10577  }
10578  if (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
10579  $mime = 'application/x-mspowerpoint';
10580  $imgmime = 'ppt.png';
10581  $famime = 'file-powerpoint-o';
10582  }
10583  // Other
10584  if (preg_match('/\.pdf$/i', $tmpfile)) {
10585  $mime = 'application/pdf';
10586  $imgmime = 'pdf.png';
10587  $famime = 'file-pdf-o';
10588  }
10589  // Scripts
10590  if (preg_match('/\.bat$/i', $tmpfile)) {
10591  $mime = 'text/x-bat';
10592  $imgmime = 'script.png';
10593  $srclang = 'dos';
10594  $famime = 'file-code-o';
10595  }
10596  if (preg_match('/\.sh$/i', $tmpfile)) {
10597  $mime = 'text/x-sh';
10598  $imgmime = 'script.png';
10599  $srclang = 'bash';
10600  $famime = 'file-code-o';
10601  }
10602  if (preg_match('/\.ksh$/i', $tmpfile)) {
10603  $mime = 'text/x-ksh';
10604  $imgmime = 'script.png';
10605  $srclang = 'bash';
10606  $famime = 'file-code-o';
10607  }
10608  if (preg_match('/\.bash$/i', $tmpfile)) {
10609  $mime = 'text/x-bash';
10610  $imgmime = 'script.png';
10611  $srclang = 'bash';
10612  $famime = 'file-code-o';
10613  }
10614  // Images
10615  if (preg_match('/\.ico$/i', $tmpfile)) {
10616  $mime = 'image/x-icon';
10617  $imgmime = 'image.png';
10618  $famime = 'file-image-o';
10619  }
10620  if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
10621  $mime = 'image/jpeg';
10622  $imgmime = 'image.png';
10623  $famime = 'file-image-o';
10624  }
10625  if (preg_match('/\.png$/i', $tmpfile)) {
10626  $mime = 'image/png';
10627  $imgmime = 'image.png';
10628  $famime = 'file-image-o';
10629  }
10630  if (preg_match('/\.gif$/i', $tmpfile)) {
10631  $mime = 'image/gif';
10632  $imgmime = 'image.png';
10633  $famime = 'file-image-o';
10634  }
10635  if (preg_match('/\.bmp$/i', $tmpfile)) {
10636  $mime = 'image/bmp';
10637  $imgmime = 'image.png';
10638  $famime = 'file-image-o';
10639  }
10640  if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
10641  $mime = 'image/tiff';
10642  $imgmime = 'image.png';
10643  $famime = 'file-image-o';
10644  }
10645  if (preg_match('/\.svg$/i', $tmpfile)) {
10646  $mime = 'image/svg+xml';
10647  $imgmime = 'image.png';
10648  $famime = 'file-image-o';
10649  }
10650  if (preg_match('/\.webp$/i', $tmpfile)) {
10651  $mime = 'image/webp';
10652  $imgmime = 'image.png';
10653  $famime = 'file-image-o';
10654  }
10655  // Calendar
10656  if (preg_match('/\.vcs$/i', $tmpfile)) {
10657  $mime = 'text/calendar';
10658  $imgmime = 'other.png';
10659  $famime = 'file-text-o';
10660  }
10661  if (preg_match('/\.ics$/i', $tmpfile)) {
10662  $mime = 'text/calendar';
10663  $imgmime = 'other.png';
10664  $famime = 'file-text-o';
10665  }
10666  // Other
10667  if (preg_match('/\.torrent$/i', $tmpfile)) {
10668  $mime = 'application/x-bittorrent';
10669  $imgmime = 'other.png';
10670  $famime = 'file-o';
10671  }
10672  // Audio
10673  if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) {
10674  $mime = 'audio';
10675  $imgmime = 'audio.png';
10676  $famime = 'file-audio-o';
10677  }
10678  // Video
10679  if (preg_match('/\.mp4$/i', $tmpfile)) {
10680  $mime = 'video/mp4';
10681  $imgmime = 'video.png';
10682  $famime = 'file-video-o';
10683  }
10684  if (preg_match('/\.ogv$/i', $tmpfile)) {
10685  $mime = 'video/ogg';
10686  $imgmime = 'video.png';
10687  $famime = 'file-video-o';
10688  }
10689  if (preg_match('/\.webm$/i', $tmpfile)) {
10690  $mime = 'video/webm';
10691  $imgmime = 'video.png';
10692  $famime = 'file-video-o';
10693  }
10694  if (preg_match('/\.avi$/i', $tmpfile)) {
10695  $mime = 'video/x-msvideo';
10696  $imgmime = 'video.png';
10697  $famime = 'file-video-o';
10698  }
10699  if (preg_match('/\.divx$/i', $tmpfile)) {
10700  $mime = 'video/divx';
10701  $imgmime = 'video.png';
10702  $famime = 'file-video-o';
10703  }
10704  if (preg_match('/\.xvid$/i', $tmpfile)) {
10705  $mime = 'video/xvid';
10706  $imgmime = 'video.png';
10707  $famime = 'file-video-o';
10708  }
10709  if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
10710  $mime = 'video';
10711  $imgmime = 'video.png';
10712  $famime = 'file-video-o';
10713  }
10714  // Archive
10715  if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) {
10716  $mime = 'archive';
10717  $imgmime = 'archive.png';
10718  $famime = 'file-archive-o';
10719  } // application/xxx where zzz is zip, ...
10720  // Exe
10721  if (preg_match('/\.(exe|com)$/i', $tmpfile)) {
10722  $mime = 'application/octet-stream';
10723  $imgmime = 'other.png';
10724  $famime = 'file-o';
10725  }
10726  // Lib
10727  if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) {
10728  $mime = 'library';
10729  $imgmime = 'library.png';
10730  $famime = 'file-o';
10731  }
10732  // Err
10733  if (preg_match('/\.err$/i', $tmpfile)) {
10734  $mime = 'error';
10735  $imgmime = 'error.png';
10736  $famime = 'file-text-o';
10737  }
10738 
10739  // Return string
10740  if ($mode == 1) {
10741  $tmp = explode('/', $mime);
10742  return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
10743  }
10744  if ($mode == 2) {
10745  return $imgmime;
10746  }
10747  if ($mode == 3) {
10748  return $srclang;
10749  }
10750  if ($mode == 4) {
10751  return $famime;
10752  }
10753  return $mime;
10754 }
10755 
10767 function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
10768 {
10769  global $conf, $db;
10770 
10771  $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
10772 
10773  $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
10774 
10775  if (is_null($dictvalues)) {
10776  $dictvalues = array();
10777 
10778  $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
10779  if ($checkentity) {
10780  $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
10781  }
10782 
10783  $resql = $db->query($sql);
10784  if ($resql) {
10785  while ($obj = $db->fetch_object($resql)) {
10786  $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
10787  }
10788  } else {
10789  dol_print_error($db);
10790  }
10791 
10792  $conf->cache['dictvalues_'.$tablename] = $dictvalues;
10793  }
10794 
10795  if (!empty($dictvalues[$id])) {
10796  // Found
10797  $tmp = $dictvalues[$id];
10798  return (property_exists($tmp, $field) ? $tmp->$field : '');
10799  } else {
10800  // Not found
10801  return '';
10802  }
10803 }
10804 
10811 function colorIsLight($stringcolor)
10812 {
10813  $stringcolor = str_replace('#', '', $stringcolor);
10814  $res = -1;
10815  if (!empty($stringcolor)) {
10816  $res = 0;
10817  $tmp = explode(',', $stringcolor);
10818  if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
10819  $r = $tmp[0];
10820  $g = $tmp[1];
10821  $b = $tmp[2];
10822  } else {
10823  $hexr = $stringcolor[0].$stringcolor[1];
10824  $hexg = $stringcolor[2].$stringcolor[3];
10825  $hexb = $stringcolor[4].$stringcolor[5];
10826  $r = hexdec($hexr);
10827  $g = hexdec($hexg);
10828  $b = hexdec($hexb);
10829  }
10830  $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
10831  if ($bright > 0.6) {
10832  $res = 1;
10833  }
10834  }
10835  return $res;
10836 }
10837 
10846 function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
10847 {
10848  global $conf;
10849 
10850  //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
10851  //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
10852  if (empty($menuentry['enabled'])) {
10853  return 0; // Entry disabled by condition
10854  }
10855  if ($type_user && $menuentry['module']) {
10856  $tmploops = explode('|', $menuentry['module']);
10857  $found = 0;
10858  foreach ($tmploops as $tmploop) {
10859  if (in_array($tmploop, $listofmodulesforexternal)) {
10860  $found++;
10861  break;
10862  }
10863  }
10864  if (!$found) {
10865  return 0; // Entry is for menus all excluded to external users
10866  }
10867  }
10868  if (!$menuentry['perms'] && $type_user) {
10869  return 0; // No permissions and user is external
10870  }
10871  if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) {
10872  return 0; // No permissions and option to hide when not allowed, even for internal user, is on
10873  }
10874  if (!$menuentry['perms']) {
10875  return 2; // No permissions and user is external
10876  }
10877  return 1;
10878 }
10879 
10887 function roundUpToNextMultiple($n, $x = 5)
10888 {
10889  return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
10890 }
10891 
10903 function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
10904 {
10905  $attr = array(
10906  'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css'])
10907  );
10908 
10909  if (empty($html)) {
10910  $html = $label;
10911  }
10912 
10913  if (!empty($url)) {
10914  $attr['href'] = $url;
10915  }
10916 
10917  if ($mode === 'dot') {
10918  $attr['class'] .= ' classfortooltip';
10919  $attr['title'] = $html;
10920  $attr['aria-label'] = $label;
10921  $html = '';
10922  }
10923 
10924  // Override attr
10925  if (!empty($params['attr']) && is_array($params['attr'])) {
10926  foreach ($params['attr'] as $key => $value) {
10927  if ($key == 'class') {
10928  $attr['class'] .= ' '.$value;
10929  } elseif ($key == 'classOverride') {
10930  $attr['class'] = $value;
10931  } else {
10932  $attr[$key] = $value;
10933  }
10934  }
10935  }
10936 
10937  // TODO: add hook
10938 
10939  // escape all attribute
10940  $attr = array_map('dol_escape_htmltag', $attr);
10941 
10942  $TCompiledAttr = array();
10943  foreach ($attr as $key => $value) {
10944  $TCompiledAttr[] = $key.'="'.$value.'"';
10945  }
10946 
10947  $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
10948 
10949  $tag = !empty($url) ? 'a' : 'span';
10950 
10951  return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
10952 }
10953 
10954 
10967 function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
10968 {
10969  global $conf;
10970 
10971  $return = '';
10972  $dolGetBadgeParams = array();
10973 
10974  if (!empty($params['badgeParams'])) {
10975  $dolGetBadgeParams = $params['badgeParams'];
10976  }
10977 
10978  // TODO : add a hook
10979  if ($displayMode == 0) {
10980  $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
10981  } elseif ($displayMode == 1) {
10982  $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10983  } elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) {
10984  // Use status with images (for backward compatibility)
10985  $return = '';
10986  $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
10987  $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>' : '');
10988 
10989  // For small screen, we always use the short label instead of long label.
10990  if (!empty($conf->dol_optimize_smallscreen)) {
10991  if ($displayMode == 0) {
10992  $displayMode = 1;
10993  } elseif ($displayMode == 4) {
10994  $displayMode = 2;
10995  } elseif ($displayMode == 6) {
10996  $displayMode = 5;
10997  }
10998  }
10999 
11000  // For backward compatibility. Image's filename are still in French, so we use this array to convert
11001  $statusImg = array(
11002  'status0' => 'statut0',
11003  'status1' => 'statut1',
11004  'status2' => 'statut2',
11005  'status3' => 'statut3',
11006  'status4' => 'statut4',
11007  'status5' => 'statut5',
11008  'status6' => 'statut6',
11009  'status7' => 'statut7',
11010  'status8' => 'statut8',
11011  'status9' => 'statut9'
11012  );
11013 
11014  if (!empty($statusImg[$statusType])) {
11015  $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
11016  } else {
11017  $htmlImg = img_picto($statusLabel, $statusType);
11018  }
11019 
11020  if ($displayMode === 2) {
11021  $return = $htmlImg.' '.$htmlLabelShort;
11022  } elseif ($displayMode === 3) {
11023  $return = $htmlImg;
11024  } elseif ($displayMode === 4) {
11025  $return = $htmlImg.' '.$htmlLabel;
11026  } elseif ($displayMode === 5) {
11027  $return = $htmlLabelShort.' '.$htmlImg;
11028  } else { // $displayMode >= 6
11029  $return = $htmlLabel.' '.$htmlImg;
11030  }
11031  } elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) {
11032  // Use new badge
11033  $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
11034 
11035  $dolGetBadgeParams['attr']['class'] = 'badge-status';
11036  $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
11037 
11038  if ($displayMode == 3) {
11039  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
11040  } elseif ($displayMode === 5) {
11041  $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
11042  } else {
11043  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
11044  }
11045  }
11046 
11047  return $return;
11048 }
11049 
11050 
11080 function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
11081 {
11082  global $hookmanager, $action, $object, $langs;
11083 
11084  // If $url is an array, we must build a dropdown button
11085  if (is_array($url)) {
11086  $out = '<div class="dropdown inline-block dropdown-holder">';
11087  $out .= '<a style="margin-right: auto;" class="dropdown-toggle butAction" data-toggle="dropdown">'.$label.'</a>';
11088  $out .= '<div class="dropdown-content">';
11089  foreach ($url as $subbutton) {
11090  if ($subbutton['enabled'] && $subbutton['perm']) {
11091  if (!empty($subbutton['lang'])) {
11092  $langs->load($subbutton['lang']);
11093  }
11094  $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', DOL_URL_ROOT.$subbutton['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage'])), '', 1, array('isDropDown' => true));
11095  }
11096  }
11097  $out .= "</div>";
11098  $out .= "</div>";
11099 
11100  return $out;
11101  }
11102 
11103  // If $url is a simple link
11104  if (!empty($params['isDropdown']))
11105  $class = "dropdown-item";
11106  else {
11107  $class = 'butAction';
11108  if ($actionType == 'danger' || $actionType == 'delete') {
11109  $class = 'butActionDelete';
11110  if (!empty($url) && strpos($url, 'token=') === false) $url .= '&token='.newToken();
11111  }
11112  }
11113  $attr = array(
11114  'class' => $class,
11115  'href' => empty($url) ? '' : $url,
11116  'title' => $label
11117  );
11118 
11119  if (empty($text)) {
11120  $text = $label;
11121  $attr['title'] = ''; // if html not set, leave label on title is redundant
11122  } else {
11123  $attr['title'] = $label;
11124  $attr['aria-label'] = $label;
11125  }
11126 
11127  if (empty($userRight)) {
11128  $attr['class'] = 'butActionRefused';
11129  $attr['href'] = '';
11130  $attr['title'] = (($label && $text && $label != $text) ? $label : $langs->trans('NotEnoughPermissions'));
11131  }
11132 
11133  if (!empty($id)) {
11134  $attr['id'] = $id;
11135  }
11136 
11137  // Override attr
11138  if (!empty($params['attr']) && is_array($params['attr'])) {
11139  foreach ($params['attr'] as $key => $value) {
11140  if ($key == 'class') {
11141  $attr['class'] .= ' '.$value;
11142  } elseif ($key == 'classOverride') {
11143  $attr['class'] = $value;
11144  } else {
11145  $attr[$key] = $value;
11146  }
11147  }
11148  }
11149 
11150  // automatic add tooltip when title is detected
11151  if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
11152  $attr['class'].= ' classfortooltip';
11153  }
11154 
11155  // Js Confirm button
11156  if ($userRight && !empty($params['confirm'])) {
11157  if (!is_array($params['confirm'])) {
11158  $params['confirm'] = array();
11159  }
11160 
11161  if (empty($params['confirm']['url'])) {
11162  $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
11163  }
11164 
11165  // for js desabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
11166  $attr['data-confirm-url'] = $params['confirm']['url'];
11167  $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
11168  $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
11169  $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
11170  $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
11171  $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
11172  $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
11173 
11174  $attr['class'].= ' butActionConfirm';
11175  }
11176 
11177  if (isset($attr['href']) && empty($attr['href'])) {
11178  unset($attr['href']);
11179  }
11180 
11181  // escape all attribute
11182  $attr = array_map('dol_escape_htmltag', $attr);
11183 
11184  $TCompiledAttr = array();
11185  foreach ($attr as $key => $value) {
11186  $TCompiledAttr[] = $key.'= "'.$value.'"';
11187  }
11188 
11189  $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
11190 
11191  $tag = !empty($attr['href']) ? 'a' : 'span';
11192 
11193 
11194  $parameters = array(
11195  'TCompiledAttr' => $TCompiledAttr, // array
11196  'compiledAttributes' => $compiledAttributes, // string
11197  'attr' => $attr,
11198  'tag' => $tag,
11199  'label' => $label,
11200  'html' => $text,
11201  'actionType' => $actionType,
11202  'url' => $url,
11203  'id' => $id,
11204  'userRight' => $userRight,
11205  'params' => $params
11206  );
11207 
11208  $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
11209  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
11210 
11211  if (empty($reshook)) {
11212  if (dol_textishtml($text)) { // If content already HTML encoded
11213  return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
11214  } else {
11215  return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
11216  }
11217  } else {
11218  return $hookmanager->resPrint;
11219  }
11220 }
11221 
11228 function dolGetButtonTitleSeparator($moreClass = "")
11229 {
11230  return '<span class="button-title-separator '.$moreClass.'" ></span>';
11231 }
11232 
11239 function getFieldErrorIcon($fieldValidationErrorMsg)
11240 {
11241  $out = '';
11242  if (!empty($fieldValidationErrorMsg)) {
11243  $out.= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
11244  $out.= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
11245  $out.= '</span>';
11246  }
11247 
11248  return $out;
11249 }
11250 
11263 function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
11264 {
11265  global $langs, $conf, $user;
11266 
11267  // Actually this conf is used in css too for external module compatibility and smooth transition to this function
11268  if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) {
11269  return '';
11270  }
11271 
11272  $class = 'btnTitle';
11273  if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
11274  $class .= ' btnTitlePlus';
11275  }
11276  $useclassfortooltip = 1;
11277 
11278  if (!empty($params['morecss'])) {
11279  $class .= ' '.$params['morecss'];
11280  }
11281 
11282  $attr = array(
11283  'class' => $class,
11284  'href' => empty($url) ? '' : $url
11285  );
11286 
11287  if (!empty($helpText)) {
11288  $attr['title'] = dol_escape_htmltag($helpText);
11289  } elseif (empty($attr['title']) && $label) {
11290  $attr['title'] = $label;
11291  $useclassfortooltip = 0;
11292  }
11293 
11294  if ($status == 2) {
11295  $attr['class'] .= ' btnTitleSelected';
11296  } elseif ($status <= 0) {
11297  $attr['class'] .= ' refused';
11298 
11299  $attr['href'] = '';
11300 
11301  if ($status == -1) { // disable
11302  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
11303  } elseif ($status == 0) { // Not enough permissions
11304  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
11305  }
11306  }
11307 
11308  if (!empty($attr['title']) && $useclassfortooltip) {
11309  $attr['class'] .= ' classfortooltip';
11310  }
11311 
11312  if (!empty($id)) {
11313  $attr['id'] = $id;
11314  }
11315 
11316  // Override attr
11317  if (!empty($params['attr']) && is_array($params['attr'])) {
11318  foreach ($params['attr'] as $key => $value) {
11319  if ($key == 'class') {
11320  $attr['class'] .= ' '.$value;
11321  } elseif ($key == 'classOverride') {
11322  $attr['class'] = $value;
11323  } else {
11324  $attr[$key] = $value;
11325  }
11326  }
11327  }
11328 
11329  if (isset($attr['href']) && empty($attr['href'])) {
11330  unset($attr['href']);
11331  }
11332 
11333  // TODO : add a hook
11334 
11335  // escape all attribute
11336  $attr = array_map('dol_escape_htmltag', $attr);
11337 
11338  $TCompiledAttr = array();
11339  foreach ($attr as $key => $value) {
11340  $TCompiledAttr[] = $key.'="'.$value.'"';
11341  }
11342 
11343  $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
11344 
11345  $tag = (empty($attr['href']) ? 'span' : 'a');
11346 
11347  $button = '<'.$tag.' '.$compiledAttributes.'>';
11348  $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
11349  if (!empty($params['forcenohideoftext'])) {
11350  $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
11351  }
11352  $button .= '</'.$tag.'>';
11353 
11354  return $button;
11355 }
11356 
11367 function getElementProperties($element_type)
11368 {
11369  global $conf;
11370 
11371  $regs = array();
11372 
11373  $classfile = $classname = $classpath = $subdir = $dir_output = '';
11374 
11375  // Parse element/subelement
11376  $module = $element_type;
11377  $element = $element_type;
11378  $subelement = $element_type;
11379 
11380  // If we ask a resource form external module (instead of default path)
11381  if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) { // 'myobject@mymodule'
11382  $element = $subelement = $regs[1];
11383  $module = $regs[2];
11384  }
11385 
11386  // If we ask a resource for a string with an element and a subelement
11387  // Example 'project_task'
11388  if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
11389  $module = $element = $regs[1];
11390  $subelement = $regs[2];
11391  }
11392 
11393  // For compat and To work with non standard path
11394  if ($element_type == "action") {
11395  $classpath = 'comm/action/class';
11396  $subelement = 'Actioncomm';
11397  $module = 'agenda';
11398  } elseif ($element_type == 'cronjob') {
11399  $classpath = 'cron/class';
11400  $module = 'cron';
11401  } elseif ($element_type == 'adherent_type') {
11402  $classpath = 'adherents/class';
11403  $classfile = 'adherent_type';
11404  $module = 'adherent';
11405  $subelement = 'adherent_type';
11406  $classname = 'AdherentType';
11407  } elseif ($element_type == 'bank_account') {
11408  $classpath = 'compta/bank/class';
11409  $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
11410  $classfile = 'account';
11411  $classname = 'Account';
11412  } elseif ($element_type == 'category') {
11413  $classpath = 'categories/class';
11414  $module = 'categorie';
11415  $subelement = 'categorie';
11416  } elseif ($element_type == 'contact') {
11417  $classpath = 'contact/class';
11418  $classfile = 'contact';
11419  $module = 'societe';
11420  $subelement = 'contact';
11421  } elseif ($element_type == 'stock') {
11422  $classpath = 'product/stock/class';
11423  $classfile = 'entrepot';
11424  $classname = 'Entrepot';
11425  } elseif ($element_type == 'project') {
11426  $classpath = 'projet/class';
11427  $module = 'projet';
11428  } elseif ($element_type == 'project_task') {
11429  $classpath = 'projet/class';
11430  $module = 'projet';
11431  $subelement = 'task';
11432  } elseif ($element_type == 'facture' || $element_type == 'invoice') {
11433  $classpath = 'compta/facture/class';
11434  $module = 'facture';
11435  $subelement = 'facture';
11436  } elseif ($element_type == 'commande' || $element_type == 'order') {
11437  $classpath = 'commande/class';
11438  $module = 'commande';
11439  $subelement = 'commande';
11440  } elseif ($element_type == 'propal') {
11441  $classpath = 'comm/propal/class';
11442  } elseif ($element_type == 'shipping') {
11443  $classpath = 'expedition/class';
11444  $classfile = 'expedition';
11445  $classname = 'Expedition';
11446  $module = 'expedition';
11447  } elseif ($element_type == 'supplier_proposal') {
11448  $classpath = 'supplier_proposal/class';
11449  $module = 'supplier_proposal';
11450  $element = 'supplierproposal';
11451  $classfile = 'supplier_proposal';
11452  $subelement = 'supplierproposal';
11453  } elseif ($element_type == 'shipping') {
11454  $classpath = 'expedition/class';
11455  $subelement = 'expedition';
11456  $module = 'expedition_bon';
11457  } elseif ($element_type == 'delivery') {
11458  $classpath = 'delivery/class';
11459  $subelement = 'delivery';
11460  $module = 'delivery_note';
11461  } elseif ($element_type == 'contract') {
11462  $classpath = 'contrat/class';
11463  $module = 'contrat';
11464  $subelement = 'contrat';
11465  } elseif ($element_type == 'mailing') {
11466  $classpath = 'comm/mailing/class';
11467  $module = 'mailing';
11468  $classfile = 'mailing';
11469  $classname = 'Mailing';
11470  $subelement = '';
11471  } elseif ($element_type == 'member') {
11472  $classpath = 'adherents/class';
11473  $module = 'adherent';
11474  $subelement = 'adherent';
11475  } elseif ($element_type == 'usergroup') {
11476  $classpath = 'user/class';
11477  $module = 'user';
11478  } elseif ($element_type == 'mo') {
11479  $classpath = 'mrp/class';
11480  $classfile = 'mo';
11481  $classname = 'Mo';
11482  $module = 'mrp';
11483  $subelement = '';
11484  } elseif ($element_type == 'cabinetmed_cons') {
11485  $classpath = 'cabinetmed/class';
11486  $module = 'cabinetmed';
11487  $subelement = 'cabinetmedcons';
11488  } elseif ($element_type == 'fichinter') {
11489  $classpath = 'fichinter/class';
11490  $module = 'ficheinter';
11491  $subelement = 'fichinter';
11492  } elseif ($element_type == 'dolresource' || $element_type == 'resource') {
11493  $classpath = 'resource/class';
11494  $module = 'resource';
11495  $subelement = 'dolresource';
11496  } elseif ($element_type == 'propaldet') {
11497  $classpath = 'comm/propal/class';
11498  $module = 'propal';
11499  $subelement = 'propaleligne';
11500  } elseif ($element_type == 'opensurvey_sondage') {
11501  $classpath = 'opensurvey/class';
11502  $module = 'opensurvey';
11503  $subelement = 'opensurveysondage';
11504  } elseif ($element_type == 'order_supplier') {
11505  $classpath = 'fourn/class';
11506  $module = 'fournisseur';
11507  $classfile = 'fournisseur.commande';
11508  $element = 'order_supplier';
11509  $subelement = '';
11510  $classname = 'CommandeFournisseur';
11511  } elseif ($element_type == 'invoice_supplier') {
11512  $classpath = 'fourn/class';
11513  $module = 'fournisseur';
11514  $classfile = 'fournisseur.facture';
11515  $element = 'invoice_supplier';
11516  $subelement = '';
11517  $classname = 'FactureFournisseur';
11518  } elseif ($element_type == "service") {
11519  $classpath = 'product/class';
11520  $subelement = 'product';
11521  } elseif ($element_type == 'salary') {
11522  $classpath = 'salaries/class';
11523  $module = 'salaries';
11524  } elseif ($element_type == 'productlot') {
11525  $module = 'productbatch';
11526  $classpath = 'product/stock/class';
11527  $classfile = 'productlot';
11528  $classname = 'Productlot';
11529  $element = 'productlot';
11530  $subelement = '';
11531  } elseif ($element_type == 'websitepage') {
11532  $classpath = 'website/class';
11533  $classfile = 'websitepage';
11534  $classname = 'Websitepage';
11535  $module = 'website';
11536  $subelement = 'websitepage';
11537  } elseif ($element_type == 'fiscalyear') {
11538  $classpath = 'core/class';
11539  $module = 'accounting';
11540  $subelement = 'fiscalyear';
11541  } elseif ($element_type == 'chargesociales') {
11542  $classpath = 'compta/sociales/class';
11543  $module = 'tax';
11544  } elseif ($element_type == 'tva') {
11545  $classpath = 'compta/tva/class';
11546  $module = 'tax';
11547  $subdir = '/vat';
11548  }
11549 
11550  if (empty($classfile)) {
11551  $classfile = strtolower($subelement);
11552  }
11553  if (empty($classname)) {
11554  $classname = ucfirst($subelement);
11555  }
11556  if (empty($classpath)) {
11557  $classpath = $module.'/class';
11558  }
11559 
11560  //print 'getElementProperties subdir='.$subdir;
11561 
11562  // Set dir_output
11563  if ($module && isset($conf->$module)) { // The generic case
11564  if (!empty($conf->$module->multidir_output[$conf->entity])) {
11565  $dir_output = $conf->$module->multidir_output[$conf->entity];
11566  } elseif (!empty($conf->$module->output[$conf->entity])) {
11567  $dir_output = $conf->$module->output[$conf->entity];
11568  } elseif (!empty($conf->$module->dir_output)) {
11569  $dir_output = $conf->$module->dir_output;
11570  }
11571  }
11572 
11573  // Overwrite value for special cases
11574  if ($element == 'order_supplier') {
11575  $dir_output = $conf->fournisseur->commande->dir_output;
11576  } elseif ($element == 'invoice_supplier') {
11577  $dir_output = $conf->fournisseur->facture->dir_output;
11578  }
11579  $dir_output .= $subdir;
11580 
11581  $element_properties = array(
11582  'module' => $module,
11583  'element' => $element,
11584  'subelement' => $subelement,
11585  'classpath' => $classpath,
11586  'classfile' => $classfile,
11587  'classname' => $classname,
11588  'dir_output' => $dir_output
11589  );
11590  return $element_properties;
11591 }
11592 
11602 function fetchObjectByElement($element_id, $element_type, $element_ref = '')
11603 {
11604  global $db;
11605 
11606  $ret = 0;
11607 
11608  $element_prop = getElementProperties($element_type);
11609 
11610  if (is_array($element_prop) && isModEnabled($element_prop['module'])) {
11611  dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
11612 
11613  if (class_exists($element_prop['classname'])) {
11614  $classname = $element_prop['classname'];
11615  $objecttmp = new $classname($db);
11616  $ret = $objecttmp->fetch($element_id, $element_ref);
11617  if ($ret >= 0) {
11618  if (empty($objecttmp->module)) {
11619  $objecttmp->module = $element_prop['module'];
11620  }
11621 
11622  return $objecttmp;
11623  }
11624  } else {
11625  return -1;
11626  }
11627  }
11628 
11629  return $ret;
11630 }
11631 
11638 function isAFileWithExecutableContent($filename)
11639 {
11640  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)) {
11641  return true;
11642  }
11643 
11644  return false;
11645 }
11646 
11654 function newToken()
11655 {
11656  return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
11657 }
11658 
11666 function currentToken()
11667 {
11668  return isset($_SESSION['token']) ? $_SESSION['token'] : '';
11669 }
11670 
11676 function getNonce()
11677 {
11678  global $conf;
11679 
11680  if (empty($conf->cache['nonce'])) {
11681  $conf->cache['nonce'] = dolGetRandomBytes(8);
11682  }
11683 
11684  return $conf->cache['nonce'];
11685 }
11686 
11687 
11700 function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1)
11701 {
11702  global $langs;
11703 
11704  print '<div class="div-table-responsive-no-min">';
11705  print '<table class="noborder centpercent">';
11706  print '<tr class="liste_titre">';
11707 
11708  print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">';
11709 
11710  print $langs->trans($header);
11711 
11712  // extra space between the first header and the number
11713  if ($number > -1) {
11714  print ' ';
11715  }
11716 
11717  if (!empty($link)) {
11718  if (!empty($arguments)) {
11719  print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11720  } else {
11721  print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
11722  }
11723  }
11724 
11725  if ($number > -1) {
11726  print '<span class="badge">'.$number.'</span>';
11727  }
11728 
11729  if (!empty($link)) {
11730  print '</a>';
11731  }
11732 
11733  print '</th>';
11734 
11735  if ($number < 0 && !empty($link)) {
11736  print '<th class="right">';
11737 
11738  if (!empty($arguments)) {
11739  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11740  } else {
11741  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">';
11742  }
11743 
11744  print $langs->trans("FullList");
11745  print '</a>';
11746  print '</th>';
11747  }
11748 
11749  print '</tr>';
11750 }
11751 
11760 function finishSimpleTable($addLineBreak = false)
11761 {
11762  print '</table>';
11763  print '</div>';
11764 
11765  if ($addLineBreak) {
11766  print '<br>';
11767  }
11768 }
11769 
11781 function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
11782 {
11783  global $langs;
11784 
11785  if ($num === 0) {
11786  print '<tr class="oddeven">';
11787  print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>';
11788  print '</tr>';
11789  return;
11790  }
11791 
11792  if ($nbofloop === 0) {
11793  // don't show a summary line
11794  return;
11795  }
11796 
11797  if ($num === 0) {
11798  $colspan = $tableColumnCount;
11799  } elseif ($num > $nbofloop) {
11800  $colspan = $tableColumnCount;
11801  } else {
11802  $colspan = $tableColumnCount - 1;
11803  }
11804 
11805  if ($extraRightColumn) {
11806  $colspan--;
11807  }
11808 
11809  print '<tr class="liste_total">';
11810 
11811  if ($nbofloop > 0 && $num > $nbofloop) {
11812  print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
11813  } else {
11814  print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
11815  print '<td class="right" width="100">'.price($total).'</td>';
11816  }
11817 
11818  if ($extraRightColumn) {
11819  print '<td></td>';
11820  }
11821 
11822  print '</tr>';
11823 }
11824 
11833 function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
11834 {
11835  global $conf;
11836 
11837  if ($method == -1) {
11838  $method = 0;
11839  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) {
11840  $method = 1;
11841  }
11842  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) {
11843  $method = 2;
11844  }
11845  }
11846 
11847  // Be sure we don't have output buffering enabled to have readfile working correctly
11848  while (ob_get_level()) {
11849  ob_end_flush();
11850  }
11851 
11852  // Solution 0
11853  if ($method == 0) {
11854  readfile($fullpath_original_file_osencoded);
11855  } elseif ($method == 1) {
11856  // Solution 1
11857  $handle = fopen($fullpath_original_file_osencoded, "rb");
11858  while (!feof($handle)) {
11859  print fread($handle, 8192);
11860  }
11861  fclose($handle);
11862  } elseif ($method == 2) {
11863  // Solution 2
11864  $handle1 = fopen($fullpath_original_file_osencoded, "rb");
11865  $handle2 = fopen("php://output", "wb");
11866  stream_copy_to_stream($handle1, $handle2);
11867  fclose($handle1);
11868  fclose($handle2);
11869  }
11870 }
11871 
11881 function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
11882 {
11883  /*
11884  global $conf;
11885 
11886  if (!empty($conf->dol_no_mouse_hover)) {
11887  $showonlyonhover = 0;
11888  }*/
11889 
11890  $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
11891  if ($texttoshow === 'none') {
11892  $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>';
11893  } elseif ($texttoshow) {
11894  $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>';
11895  } else {
11896  $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>';
11897  }
11898 
11899  return $result;
11900 }
11901 
11902 
11909 function jsonOrUnserialize($stringtodecode)
11910 {
11911  $result = json_decode($stringtodecode);
11912  if ($result === null) {
11913  $result = unserialize($stringtodecode);
11914  }
11915 
11916  return $result;
11917 }
11918 
11919 
11933 function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
11934 {
11935  if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
11936  $filter = '(' . $filter . ')';
11937  }
11938 
11939  $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'
11940 
11941  if (!dolCheckFilters($filter, $errorstr)) {
11942  if ($noerror) {
11943  return '1 = 2';
11944  } else {
11945  return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
11946  }
11947  }
11948 
11949  // Test the filter syntax
11950  $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
11951  $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
11952  // If the string result contains something else than '()', the syntax was wrong
11953  if (preg_match('/[^\‍(\‍)]/', $t)) {
11954  $errorstr = 'Bad syntax of the search string';
11955  if ($noerror) {
11956  return '1 = 2';
11957  } else {
11958  return 'Filter syntax error - '.$errorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
11959  }
11960  }
11961 
11962  return ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
11963 }
11964 
11972 function dolCheckFilters($sqlfilters, &$error = '')
11973 {
11974  //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
11975  //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
11976  $tmp = $sqlfilters;
11977  $i = 0; $nb = strlen($tmp);
11978  $counter = 0;
11979  while ($i < $nb) {
11980  if ($tmp[$i] == '(') {
11981  $counter++;
11982  }
11983  if ($tmp[$i] == ')') {
11984  $counter--;
11985  }
11986  if ($counter < 0) {
11987  $error = "Wrond balance of parenthesis in sqlfilters=".$sqlfilters;
11988  dol_syslog($error, LOG_WARNING);
11989  return false;
11990  }
11991  $i++;
11992  }
11993  return true;
11994 }
11995 
12004 {
12005  //dol_syslog("Convert matches ".$matches[1]);
12006  if (empty($matches[1])) {
12007  return '';
12008  }
12009  $tmp = explode(':', $matches[1]);
12010  if (count($tmp) < 3) {
12011  return '';
12012  }
12013 
12014  return '()'; // An empty criteria
12015 }
12016 
12025 function dolForgeCriteriaCallback($matches)
12026 {
12027  global $db;
12028 
12029  //dol_syslog("Convert matches ".$matches[1]);
12030  if (empty($matches[1])) {
12031  return '';
12032  }
12033  $tmp = explode(':', $matches[1]);
12034  if (count($tmp) < 3) {
12035  return '';
12036  }
12037 
12038  $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
12039 
12040  $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
12041 
12042  if ($operator == 'NOTLIKE') {
12043  $operator = 'NOT LIKE';
12044  }
12045  if ($operator == 'ISNOT') {
12046  $operator = 'IS NOT';
12047  }
12048  if ($operator == '!=') {
12049  $operator = '<>';
12050  }
12051 
12052  $tmpescaped = $tmp[2];
12053  $regbis = array();
12054 
12055  if ($operator == 'IN') { // IN is allowed for list of ID or code only
12056  //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
12057  $tmpescaped = '('.$db->escape($db->sanitize($tmpescaped, 1, 0)).')';
12058  //} else {
12059  // $tmpescaped = $db->escape($db->sanitize($tmpescaped, 1));
12060  //}
12061  } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
12062  if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
12063  $tmpescaped = $regbis[1];
12064  }
12065  //$tmpescaped = "'".$db->escapeforlike($db->escape($regbis[1]))."'";
12066  $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the like will works
12067  } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
12068  $tmpescaped = "'".$db->escape($regbis[1])."'";
12069  } else {
12070  if (strtoupper($tmpescaped) == 'NULL') {
12071  $tmpescaped = 'NULL';
12072  } elseif (is_int($tmpescaped)) {
12073  $tmpescaped = (int) $tmpescaped;
12074  } else {
12075  $tmpescaped = (float) $tmpescaped;
12076  }
12077  }
12078 
12079  return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
12080 }
12081 
12082 
12091 function getTimelineIcon($actionstatic, &$histo, $key)
12092 {
12093  global $conf, $langs;
12094  $out = '<!-- timeline icon -->'."\n";
12095  $iconClass = 'fa fa-comments';
12096  $img_picto = '';
12097  $colorClass = '';
12098  $pictoTitle = '';
12099 
12100  if ($histo[$key]['percent'] == -1) {
12101  $colorClass = 'timeline-icon-not-applicble';
12102  $pictoTitle = $langs->trans('StatusNotApplicable');
12103  } elseif ($histo[$key]['percent'] == 0) {
12104  $colorClass = 'timeline-icon-todo';
12105  $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
12106  } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
12107  $colorClass = 'timeline-icon-in-progress';
12108  $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
12109  } elseif ($histo[$key]['percent'] >= 100) {
12110  $colorClass = 'timeline-icon-done';
12111  $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
12112  }
12113 
12114  if ($actionstatic->code == 'AC_TICKET_CREATE') {
12115  $iconClass = 'fa fa-ticket';
12116  } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
12117  $iconClass = 'fa fa-pencilxxx';
12118  } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
12119  $iconClass = 'fa fa-comments';
12120  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
12121  $iconClass = 'fa fa-mask';
12122  } elseif (!empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
12123  if ($actionstatic->type_picto) {
12124  $img_picto = img_picto('', $actionstatic->type_picto);
12125  } else {
12126  if ($actionstatic->type_code == 'AC_RDV') {
12127  $iconClass = 'fa fa-handshake';
12128  } elseif ($actionstatic->type_code == 'AC_TEL') {
12129  $iconClass = 'fa fa-phone';
12130  } elseif ($actionstatic->type_code == 'AC_FAX') {
12131  $iconClass = 'fa fa-fax';
12132  } elseif ($actionstatic->type_code == 'AC_EMAIL') {
12133  $iconClass = 'fa fa-envelope';
12134  } elseif ($actionstatic->type_code == 'AC_INT') {
12135  $iconClass = 'fa fa-shipping-fast';
12136  } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
12137  $iconClass = 'fa fa-robot';
12138  } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
12139  $iconClass = 'fa fa-robot';
12140  }
12141  }
12142  }
12143 
12144  $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
12145  return $out;
12146 }
12147 
12154 function getActionCommEcmList($object)
12155 {
12156  global $conf, $db;
12157 
12158  $documents = array();
12159 
12160  $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
12161  $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
12162  $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
12163  //$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
12164  $sql .= ' ORDER BY ecm.position ASC';
12165 
12166  $resql = $db->query($sql);
12167  if ($resql) {
12168  if ($db->num_rows($resql)) {
12169  while ($obj = $db->fetch_object($resql)) {
12170  $documents[$obj->id] = $obj;
12171  }
12172  }
12173  }
12174 
12175  return $documents;
12176 }
12177 
12178 
12179 
12197 function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = '', $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
12198 {
12199  global $user, $conf;
12200  global $form;
12201 
12202  global $param, $massactionbutton;
12203 
12204  dol_include_once('/comm/action/class/actioncomm.class.php');
12205 
12206  // Check parameters
12207  if (!is_object($filterobj) && !is_object($objcon)) {
12208  dol_print_error('', 'BadParameter');
12209  }
12210 
12211  $histo = array();
12212  $numaction = 0;
12213  $now = dol_now();
12214 
12215  $sortfield_list = explode(',', $sortfield);
12216  $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
12217  $sortfield_new_list = array();
12218  foreach ($sortfield_list as $sortfield_value) {
12219  $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
12220  }
12221  $sortfield_new = implode(',', $sortfield_new_list);
12222 
12223  if (isModEnabled('agenda')) {
12224  // Search histo on actioncomm
12225  if (is_object($objcon) && $objcon->id > 0) {
12226  $sql = "SELECT DISTINCT a.id, a.label as label,";
12227  } else {
12228  $sql = "SELECT a.id, a.label as label,";
12229  }
12230  $sql .= " a.datep as dp,";
12231  $sql .= " a.note as message,";
12232  $sql .= " a.datep2 as dp2,";
12233  $sql .= " a.percent as percent, 'action' as type,";
12234  $sql .= " a.fk_element, a.elementtype,";
12235  $sql .= " a.fk_contact,";
12236  $sql .= " a.email_from as msg_from,";
12237  $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
12238  $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";
12239  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
12240  $sql .= ", sp.lastname, sp.firstname";
12241  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12242  $sql .= ", m.lastname, m.firstname";
12243  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12244  $sql .= ", o.ref";
12245  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12246  $sql .= ", o.ref";
12247  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12248  $sql .= ", o.ref";
12249  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
12250  $sql .= ", o.ref";
12251  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
12252  $sql .= ", o.ref";
12253  }
12254  $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
12255  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
12256  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
12257 
12258  $force_filter_contact = false;
12259  if (is_object($objcon) && $objcon->id > 0) {
12260  $force_filter_contact = true;
12261  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
12262  $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
12263  }
12264 
12265  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
12266  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
12267  } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
12268  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
12269  $sql .= " ON er.resource_type = 'dolresource'";
12270  $sql .= " AND er.element_id = a.id";
12271  $sql .= " AND er.resource_id = ".((int) $filterobj->id);
12272  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12273  $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
12274  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12275  $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
12276  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12277  $sql .= ", ".MAIN_DB_PREFIX."product as o";
12278  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12279  $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
12280  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
12281  $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
12282  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
12283  $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
12284  }
12285 
12286  $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
12287  if ($force_filter_contact === false) {
12288  if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
12289  $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
12290  } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
12291  $sql .= " AND a.fk_project = ".((int) $filterobj->id);
12292  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12293  $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
12294  if ($filterobj->id) {
12295  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12296  }
12297  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12298  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
12299  if ($filterobj->id) {
12300  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12301  }
12302  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12303  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
12304  if ($filterobj->id) {
12305  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12306  }
12307  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12308  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
12309  if ($filterobj->id) {
12310  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12311  }
12312  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
12313  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
12314  if ($filterobj->id) {
12315  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12316  }
12317  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
12318  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
12319  if ($filterobj->id) {
12320  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12321  }
12322  }
12323  }
12324 
12325  // Condition on actioncode
12326  if (!empty($actioncode)) {
12327  if (empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
12328  if ($actioncode == 'AC_NON_AUTO') {
12329  $sql .= " AND c.type != 'systemauto'";
12330  } elseif ($actioncode == 'AC_ALL_AUTO') {
12331  $sql .= " AND c.type = 'systemauto'";
12332  } else {
12333  if ($actioncode == 'AC_OTH') {
12334  $sql .= " AND c.type != 'systemauto'";
12335  } elseif ($actioncode == 'AC_OTH_AUTO') {
12336  $sql .= " AND c.type = 'systemauto'";
12337  }
12338  }
12339  } else {
12340  if ($actioncode == 'AC_NON_AUTO') {
12341  $sql .= " AND c.type != 'systemauto'";
12342  } elseif ($actioncode == 'AC_ALL_AUTO') {
12343  $sql .= " AND c.type = 'systemauto'";
12344  } else {
12345  $sql .= " AND c.code = '".$db->escape($actioncode)."'";
12346  }
12347  }
12348  }
12349  if ($donetodo == 'todo') {
12350  $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
12351  } elseif ($donetodo == 'done') {
12352  $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
12353  }
12354  if (is_array($filters) && $filters['search_agenda_label']) {
12355  $sql .= natural_search('a.label', $filters['search_agenda_label']);
12356  }
12357  }
12358 
12359  // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
12360  if (isModEnabled('mailing') && !empty($objcon->email)
12361  && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
12362  $langs->load("mails");
12363 
12364  $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";
12365  $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
12366  $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
12367  $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
12368  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
12369  $sql2 .= ", '' as lastname, '' as firstname";
12370  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12371  $sql2 .= ", '' as lastname, '' as firstname";
12372  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12373  $sql2 .= ", '' as ref";
12374  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12375  $sql2 .= ", '' as ref";
12376  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12377  $sql2 .= ", '' as ref";
12378  }
12379  $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
12380  $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
12381  $sql2 .= " AND mc.statut = 1";
12382  $sql2 .= " AND u.rowid = m.fk_user_valid";
12383  $sql2 .= " AND mc.fk_mailing=m.rowid";
12384  }
12385 
12386  if (!empty($sql) && !empty($sql2)) {
12387  $sql = $sql." UNION ".$sql2;
12388  } elseif (empty($sql) && !empty($sql2)) {
12389  $sql = $sql2;
12390  }
12391 
12392  // TODO Add limit in nb of results
12393  if ($sql) { // May not be defined if module Agenda is not enabled and mailing module disabled too
12394  $sql .= $db->order($sortfield_new, $sortorder);
12395 
12396  dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
12397  $resql = $db->query($sql);
12398  if ($resql) {
12399  $i = 0;
12400  $num = $db->num_rows($resql);
12401 
12402  while ($i < $num) {
12403  $obj = $db->fetch_object($resql);
12404 
12405  if ($obj->type == 'action') {
12406  $contactaction = new ActionComm($db);
12407  $contactaction->id = $obj->id;
12408  $result = $contactaction->fetchResources();
12409  if ($result < 0) {
12410  dol_print_error($db);
12411  setEventMessage("actions.lib::show_actions_messaging Error fetch ressource", 'errors');
12412  }
12413 
12414  //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
12415  //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
12416  $tododone = '';
12417  if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
12418  $tododone = 'todo';
12419  }
12420 
12421  $histo[$numaction] = array(
12422  'type'=>$obj->type,
12423  'tododone'=>$tododone,
12424  'id'=>$obj->id,
12425  'datestart'=>$db->jdate($obj->dp),
12426  'dateend'=>$db->jdate($obj->dp2),
12427  'note'=>$obj->label,
12428  'message'=>$obj->message,
12429  'percent'=>$obj->percent,
12430 
12431  'userid'=>$obj->user_id,
12432  'login'=>$obj->user_login,
12433  'userfirstname'=>$obj->user_firstname,
12434  'userlastname'=>$obj->user_lastname,
12435  'userphoto'=>$obj->user_photo,
12436  'msg_from'=>$obj->msg_from,
12437 
12438  'contact_id'=>$obj->fk_contact,
12439  'socpeopleassigned' => $contactaction->socpeopleassigned,
12440  'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
12441  'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
12442  'fk_element'=>$obj->fk_element,
12443  'elementtype'=>$obj->elementtype,
12444  // Type of event
12445  'acode'=>$obj->acode,
12446  'alabel'=>$obj->alabel,
12447  'libelle'=>$obj->alabel, // deprecated
12448  'apicto'=>$obj->apicto
12449  );
12450  } else {
12451  $histo[$numaction] = array(
12452  'type'=>$obj->type,
12453  'tododone'=>'done',
12454  'id'=>$obj->id,
12455  'datestart'=>$db->jdate($obj->dp),
12456  'dateend'=>$db->jdate($obj->dp2),
12457  'note'=>$obj->label,
12458  'message'=>$obj->message,
12459  'percent'=>$obj->percent,
12460  'acode'=>$obj->acode,
12461 
12462  'userid'=>$obj->user_id,
12463  'login'=>$obj->user_login,
12464  'userfirstname'=>$obj->user_firstname,
12465  'userlastname'=>$obj->user_lastname,
12466  'userphoto'=>$obj->user_photo
12467  );
12468  }
12469 
12470  $numaction++;
12471  $i++;
12472  }
12473  } else {
12474  dol_print_error($db);
12475  }
12476  }
12477 
12478  // Set $out to show events
12479  $out = '';
12480 
12481  if (!isModEnabled('agenda')) {
12482  $langs->loadLangs(array("admin", "errors"));
12483  $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
12484  }
12485 
12486  if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
12487  $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
12488 
12489  require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
12490  include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12491  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
12492  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
12493 
12494  $formactions = new FormActions($db);
12495 
12496  $actionstatic = new ActionComm($db);
12497  $userstatic = new User($db);
12498  $contactstatic = new Contact($db);
12499  $userGetNomUrlCache = array();
12500  $contactGetNomUrlCache = array();
12501 
12502  $out .= '<div class="filters-container" >';
12503  $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
12504  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
12505 
12506  if ($objcon && get_class($objcon) == 'Contact' &&
12507  (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
12508  $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
12509  } else {
12510  $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
12511  }
12512  if ($filterobj && get_class($filterobj) == 'Societe') {
12513  $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
12514  }
12515 
12516  $out .= "\n";
12517 
12518  $out .= '<div class="div-table-responsive-no-min">';
12519  $out .= '<table class="noborder borderbottom centpercent">';
12520 
12521  $out .= '<tr class="liste_titre">';
12522 
12523  // Action column
12524  if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
12525  $out .= '<th class="liste_titre width50 middle">';
12526  $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
12527  $out .= $searchpicto;
12528  $out .= '</th>';
12529  }
12530 
12531  $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
12532 
12533  $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
12534  if ($donetodo) {
12535  $out .= '<th class="liste_titre"></th>';
12536  }
12537  $out .= '<th class="liste_titre">';
12538  $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
12539  //$out .= img_picto($langs->trans("Type"), 'type');
12540  $out .= $formactions->select_type_actions($actioncode, "actioncode", '', empty($conf->global->AGENDA_USE_EVENT_TYPE) ? 1 : -1, 0, 0, 1, 'minwidth200imp');
12541  $out .= '</th>';
12542  $out .= '<th class="liste_titre maxwidth100onsmartphone">';
12543  $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
12544  $out .= '</th>';
12545 
12546  // Action column
12547  if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
12548  $out .= '<th class="liste_titre width50 middle">';
12549  $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
12550  $out .= $searchpicto;
12551  $out .= '</th>';
12552  }
12553 
12554  $out .= '</tr>';
12555 
12556 
12557  $out .= '</table>';
12558 
12559  $out .= '</form>';
12560  $out .= '</div>';
12561 
12562  $out .= "\n";
12563 
12564  $out .= '<ul class="timeline">';
12565 
12566  if ($donetodo) {
12567  $tmp = '';
12568  if (get_class($filterobj) == 'Societe') {
12569  $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
12570  }
12571  $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
12572  $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
12573  $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
12574  //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
12575  if (get_class($filterobj) == 'Societe') {
12576  $tmp .= '</a>';
12577  }
12578  $out .= getTitleFieldOfList($tmp);
12579  }
12580 
12581 
12582  //require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
12583  //$caction=new CActionComm($db);
12584  //$arraylist=$caction->liste_array(1, 'code', '', (empty($conf->global->AGENDA_USE_EVENT_TYPE)?1:0), '', 1);
12585 
12586  $actualCycleDate = false;
12587 
12588  // Loop on each event to show it
12589  foreach ($histo as $key => $value) {
12590  $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
12591 
12592  $actionstatic->type_picto = $histo[$key]['apicto'];
12593  $actionstatic->type_code = $histo[$key]['acode'];
12594 
12595  $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
12596 
12597  $tmpa = dol_getdate($histo[$key]['datestart'], false);
12598  if ($actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
12599  $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
12600  $out .= '<!-- timeline time label -->';
12601  $out .= '<li class="time-label">';
12602  $out .= '<span class="timeline-badge-date">';
12603  $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
12604  $out .= '</span>';
12605  $out .= '</li>';
12606  $out .= '<!-- /.timeline-label -->';
12607  }
12608 
12609 
12610  $out .= '<!-- timeline item -->'."\n";
12611  $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
12612 
12613  $out .= getTimelineIcon($actionstatic, $histo, $key);
12614 
12615  $out .= '<div class="timeline-item">'."\n";
12616 
12617  $out .= '<span class="timeline-header-action">';
12618 
12619  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
12620  $out .= '<a class="timeline-btn" href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12621  $out .= $histo[$key]['id'];
12622  $out .= '</a> ';
12623  } else {
12624  $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
12625  }
12626 
12627  if ($user->hasRight('agenda', 'allactions', 'create') ||
12628  (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
12629  $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>';
12630  }
12631 
12632  $out .= '</span>';
12633  // Date
12634  $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
12635  $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
12636  if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
12637  $tmpa = dol_getdate($histo[$key]['datestart'], true);
12638  $tmpb = dol_getdate($histo[$key]['dateend'], true);
12639  if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
12640  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
12641  } else {
12642  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
12643  }
12644  }
12645  $late = 0;
12646  if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12647  $late = 1;
12648  }
12649  if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12650  $late = 1;
12651  }
12652  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
12653  $late = 1;
12654  }
12655  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12656  $late = 1;
12657  }
12658  if ($late) {
12659  $out .= img_warning($langs->trans("Late")).' ';
12660  }
12661  $out .= "</span></span>\n";
12662 
12663  // Ref
12664  $out .= '<h3 class="timeline-header">';
12665 
12666  // Author of event
12667  $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
12668  if ($histo[$key]['userid'] > 0) {
12669  if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
12670  $userstatic->fetch($histo[$key]['userid']);
12671  $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
12672  }
12673  $out .= $userGetNomUrlCache[$histo[$key]['userid']];
12674  } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
12675  if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
12676  if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
12677  $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
12678  } else {
12679  $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
12680  }
12681  }
12682  $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
12683  }
12684  $out .= '</div>';
12685 
12686  // Title
12687  $libelle = '';
12688  $out .= ' <div class="messaging-title inline-block">';
12689 
12690  if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
12691  $out .= $langs->trans('TicketNewMessage');
12692  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
12693  $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
12694  } elseif (isset($histo[$key]['type'])) {
12695  if ($histo[$key]['type'] == 'action') {
12696  $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
12697  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
12698  $libelle = $histo[$key]['note'];
12699  $actionstatic->id = $histo[$key]['id'];
12700  $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
12701  } elseif ($histo[$key]['type'] == 'mailing') {
12702  $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12703  $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
12704  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
12705  $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
12706  } else {
12707  $libelle .= $histo[$key]['note'];
12708  $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
12709  }
12710  }
12711 
12712  $out .= '</div>';
12713 
12714  $out .= '</h3>';
12715 
12716  if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
12717  && $actionstatic->code != 'AC_TICKET_CREATE'
12718  && $actionstatic->code != 'AC_TICKET_MODIFY'
12719  ) {
12720  $out .= '<div class="timeline-body">';
12721  $out .= $histo[$key]['message'];
12722  $out .= '</div>';
12723  }
12724 
12725  // Timeline footer
12726  $footer = '';
12727 
12728  // Contact for this action
12729  if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
12730  $contactList = '';
12731  foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
12732  $contact = new Contact($db);
12733  $result = $contact->fetch($cid);
12734 
12735  if ($result < 0) {
12736  dol_print_error($db, $contact->error);
12737  }
12738 
12739  if ($result > 0) {
12740  $contactList .= !empty($contactList) ? ', ' : '';
12741  $contactList .= $contact->getNomUrl(1);
12742  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12743  if (!empty($contact->phone_pro)) {
12744  $contactList .= '('.dol_print_phone($contact->phone_pro).')';
12745  }
12746  }
12747  }
12748  }
12749 
12750  $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
12751  } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
12752  $contact = new Contact($db);
12753  $result = $contact->fetch($histo[$key]['contact_id']);
12754 
12755  if ($result < 0) {
12756  dol_print_error($db, $contact->error);
12757  }
12758 
12759  if ($result > 0) {
12760  $footer .= $contact->getNomUrl(1);
12761  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12762  if (!empty($contact->phone_pro)) {
12763  $footer .= '('.dol_print_phone($contact->phone_pro).')';
12764  }
12765  }
12766  }
12767  }
12768 
12769  $documents = getActionCommEcmList($actionstatic);
12770  if (!empty($documents)) {
12771  $footer .= '<div class="timeline-documents-container">';
12772  foreach ($documents as $doc) {
12773  $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
12774  $footer .= ' data-id="'.$doc->id.'" ';
12775  $footer .= ' data-path="'.$doc->filepath.'"';
12776  $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
12777  $footer .= '>';
12778 
12779  $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
12780  $mime = dol_mimetype($filePath);
12781  $file = $actionstatic->id.'/'.$doc->filename;
12782  $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
12783  $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
12784  $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
12785 
12786  $mimeAttr = ' mime="'.$mime.'" ';
12787  $class = '';
12788  if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
12789  $class .= ' documentpreview';
12790  }
12791 
12792  $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
12793  $footer .= img_mime($filePath).' '.$doc->filename;
12794  $footer .= '</a>';
12795 
12796  $footer .= '</span>';
12797  }
12798  $footer .= '</div>';
12799  }
12800 
12801  if (!empty($footer)) {
12802  $out .= '<div class="timeline-footer">'.$footer.'</div>';
12803  }
12804 
12805  $out .= '</div>'."\n"; // end timeline-item
12806 
12807  $out .= '</li>';
12808  $out .= '<!-- END timeline item -->';
12809 
12810  $i++;
12811  }
12812 
12813  $out .= "</ul>\n";
12814 
12815  if (empty($histo)) {
12816  $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
12817  }
12818  }
12819 
12820  if ($noprint) {
12821  return $out;
12822  } else {
12823  print $out;
12824  }
12825 }
12826 
12837 function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
12838 {
12839  if ($hourTime === 'getpost') {
12840  $hour = GETPOSTINT($prefix . 'hour');
12841  $minute = GETPOSTINT($prefix . 'minute');
12842  $second = GETPOSTINT($prefix . 'second');
12843  } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
12844  $hour = intval($m[1]);
12845  $minute = intval($m[2]);
12846  $second = intval($m[3]);
12847  } else {
12848  $hour = $minute = $second = 0;
12849  }
12850  // normalize out of range values
12851  $hour = min($hour, 23);
12852  $minute = min($minute, 59);
12853  $second = min($second, 59);
12854  return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
12855 }
12856 
12868 function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
12869 {
12870  if ($timestamp === null) $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
12871  $TParam = array(
12872  $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
12873  $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
12874  $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
12875  );
12876  if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
12877  $TParam = array_merge($TParam, array(
12878  $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
12879  $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
12880  $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
12881  ));
12882  }
12883 
12884  return '&' . http_build_query($TParam);
12885 }
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='', $forcenojs=0)
On/off button to change a property status of an object This uses the ajax service objectonoff....
Definition: ajax.lib.php:711
Class to manage agenda events (actions)
Class to manage contact/addresses.
Class to manage GeoIP conversion Usage: $geoip=new GeoIP('country',$datfile); $geoip->getCountryCodeF...
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:48
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
if(isModEnabled('facture') && $user->hasRight('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') && $user->hasRight('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)) $sql
Social contributions to pay.
Definition: index.php:746
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:495
dol_get_next_day($day, $month, $year)
Return next day.
Definition: date.lib.php:480
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:464
dol_get_next_month($month, $year)
Return next month.
Definition: date.lib.php:514
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:2092
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
Definition: files.lib.php:3425
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:62
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:453
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()" ...
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.
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.
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)
if(!function_exists('utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists('str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists('str_contains')) getMultidirOutput($object, $module='')
Return the full path of the directory where a module (or an object of a module) stores its files.
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.
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.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
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.
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
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.
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='classlink button bordertransp', $jsonopen='', $backtopagejsfields='', $accesskey='')
Return HTML code to output a button to open a dialog popup box.
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_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
img_action($titlealt, $numaction, $picto='', $moreatt='')
Show logo action.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0)
Set event message in dol_events session object.
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...
GETPOSTDATE($prefix, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form::selectDate) for year,...
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
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).
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
get_htmloutput_mesg($mesgstring='', $mesgarray='', $style='ok', $keepembedded=0)
Get formated messages to output (Used to show messages on html output).
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dolChmod($filepath, $newmask='')
Change mod of a file.
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.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0)
Clean a string to keep only desirable HTML tags.
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...
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_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
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)
Function that returns whether VAT must be recoverable collected VAT (e.g.
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.
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.
dol_sanitizeUrl($stringtoclean, $type=1)
Clean a string to use it as an URL (into a href or src attribute)
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.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
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.
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.
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
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.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='float')
Show Url link.
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,...
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).
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
buildParamDate($prefix, $timestamp=null, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form::selectDate) for year,...
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...
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
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.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
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_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
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:400
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
document_preview(file, type, title)
Function show document preview.
if(!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition: main.inc.php:63
if(!defined('NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
Definition: main.inc.php:1494
div float
Buy price without taxes.
Definition: style.css.php:921
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
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:120
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:123
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,...