dolibarr  18.0.6
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  'bank' => 'banque',
214  'category' => 'categorie',
215  'contract' => 'contrat',
216  'project' => 'projet',
217  'delivery_note' => 'expedition'
218  );
219  if (empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) {
220  $arrayconv['supplier_order'] = 'fournisseur';
221  $arrayconv['supplier_invoice'] = 'fournisseur';
222  }
223  if (!empty($arrayconv[$module])) {
224  $module = $arrayconv[$module];
225  }
226 
227  return !empty($conf->modules[$module]);
228  //return !empty($conf->$module->enabled);
229 }
230 
242 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
243 {
244  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
245 
246  $class = 'DoliDB'.ucfirst($type);
247  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
248  return $dolidb;
249 }
250 
268 function getEntity($element, $shared = 1, $currentobject = null)
269 {
270  global $conf, $mc, $hookmanager, $object, $action, $db;
271 
272  if (!is_object($hookmanager)) {
273  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
274  $hookmanager = new HookManager($db);
275  }
276 
277  // fix different element names (France to English)
278  switch ($element) {
279  case 'projet':
280  $element = 'project';
281  break;
282  case 'contrat':
283  $element = 'contract';
284  break; // "/contrat/class/contrat.class.php"
285  case 'order_supplier':
286  $element = 'supplier_order';
287  break; // "/fourn/class/fournisseur.commande.class.php"
288  case 'invoice_supplier':
289  $element = 'supplier_invoice';
290  break; // "/fourn/class/fournisseur.facture.class.php"
291  }
292 
293  if (is_object($mc)) {
294  $out = $mc->getEntity($element, $shared, $currentobject);
295  } else {
296  $out = '';
297  $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
298  if (in_array($element, $addzero)) {
299  $out .= '0,';
300  }
301  $out .= ((int) $conf->entity);
302  }
303 
304  // Manipulate entities to query on the fly
305  $parameters = array(
306  'element' => $element,
307  'shared' => $shared,
308  'object' => $object,
309  'currentobject' => $currentobject,
310  'out' => $out
311  );
312  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
313 
314  if (is_numeric($reshook)) {
315  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
316  $out .= ','.$hookmanager->resPrint; // add
317  } elseif ($reshook == 1) {
318  $out = $hookmanager->resPrint; // replace
319  }
320  }
321 
322  return $out;
323 }
324 
331 function setEntity($currentobject)
332 {
333  global $conf, $mc;
334 
335  if (is_object($mc) && method_exists($mc, 'setEntity')) {
336  return $mc->setEntity($currentobject);
337  } else {
338  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
339  }
340 }
341 
348 function isASecretKey($keyname)
349 {
350  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
351 }
352 
353 
360 function num2Alpha($n)
361 {
362  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
363  $r = chr($n % 26 + 0x41) . $r;
364  return $r;
365 }
366 
367 
384 function getBrowserInfo($user_agent)
385 {
386  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
387 
388  $name = 'unknown';
389  $version = '';
390  $os = 'unknown';
391  $phone = '';
392 
393  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
394 
395  $detectmobile = new Mobile_Detect(null, $user_agent);
396  $tablet = $detectmobile->isTablet();
397 
398  if ($detectmobile->isMobile()) {
399  $phone = 'unknown';
400 
401  // If phone/smartphone, we set phone os name.
402  if ($detectmobile->is('AndroidOS')) {
403  $os = $phone = 'android';
404  } elseif ($detectmobile->is('BlackBerryOS')) {
405  $os = $phone = 'blackberry';
406  } elseif ($detectmobile->is('iOS')) {
407  $os = 'ios';
408  $phone = 'iphone';
409  } elseif ($detectmobile->is('PalmOS')) {
410  $os = $phone = 'palm';
411  } elseif ($detectmobile->is('SymbianOS')) {
412  $os = 'symbian';
413  } elseif ($detectmobile->is('webOS')) {
414  $os = 'webos';
415  } elseif ($detectmobile->is('MaemoOS')) {
416  $os = 'maemo';
417  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
418  $os = 'windows';
419  }
420  }
421 
422  // OS
423  if (preg_match('/linux/i', $user_agent)) {
424  $os = 'linux';
425  } elseif (preg_match('/macintosh/i', $user_agent)) {
426  $os = 'macintosh';
427  } elseif (preg_match('/windows/i', $user_agent)) {
428  $os = 'windows';
429  }
430 
431  // Name
432  $reg = array();
433  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
434  $name = 'firefox';
435  $version = empty($reg[2]) ? '' : $reg[2];
436  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
437  $name = 'edge';
438  $version = empty($reg[2]) ? '' : $reg[2];
439  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
440  $name = 'chrome';
441  $version = empty($reg[2]) ? '' : $reg[2];
442  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
443  // we can have 'chrome (Mozilla...) chrome x.y' in one string
444  $name = 'chrome';
445  } elseif (preg_match('/iceweasel/i', $user_agent)) {
446  $name = 'iceweasel';
447  } elseif (preg_match('/epiphany/i', $user_agent)) {
448  $name = 'epiphany';
449  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
450  $name = 'safari';
451  $version = empty($reg[2]) ? '' : $reg[2];
452  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
453  // Safari is often present in string for mobile but its not.
454  $name = 'opera';
455  $version = empty($reg[2]) ? '' : $reg[2];
456  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
457  $name = 'ie';
458  $version = end($reg);
459  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
460  // MS products at end
461  $name = 'ie';
462  $version = end($reg);
463  } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
464  // MS products at end
465  $name = 'lynxlinks';
466  $version = empty($reg[3]) ? '' : $reg[3];
467  }
468 
469  if ($tablet) {
470  $layout = 'tablet';
471  } elseif ($phone) {
472  $layout = 'phone';
473  } else {
474  $layout = 'classic';
475  }
476 
477  return array(
478  'browsername' => $name,
479  'browserversion' => $version,
480  'browseros' => $os,
481  'browserua' => $user_agent,
482  'layout' => $layout, // tablet, phone, classic
483  'phone' => $phone, // deprecated
484  'tablet' => $tablet // deprecated
485  );
486 }
487 
493 function dol_shutdown()
494 {
495  global $user, $langs, $db;
496  $disconnectdone = false;
497  $depth = 0;
498  if (is_object($db) && !empty($db->connected)) {
499  $depth = $db->transaction_opened;
500  $disconnectdone = $db->close();
501  }
502  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
503 }
504 
514 function GETPOSTISSET($paramname)
515 {
516  $isset = false;
517 
518  $relativepathstring = $_SERVER["PHP_SELF"];
519  // Clean $relativepathstring
520  if (constant('DOL_URL_ROOT')) {
521  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
522  }
523  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
524  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
525  //var_dump($relativepathstring);
526  //var_dump($user->default_values);
527 
528  // Code for search criteria persistence.
529  // Retrieve values if restore_lastsearch_values
530  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
531  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
532  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
533  if (is_array($tmp)) {
534  foreach ($tmp as $key => $val) {
535  if ($key == $paramname) { // We are on the requested parameter
536  $isset = true;
537  break;
538  }
539  }
540  }
541  }
542  // If there is saved contextpage, limit, page or mode
543  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
544  $isset = true;
545  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
546  $isset = true;
547  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
548  $isset = true;
549  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
550  $isset = true;
551  }
552  } else {
553  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
554  }
555 
556  return $isset;
557 }
558 
567 function GETPOSTISARRAY($paramname, $method = 0)
568 {
569  // for $method test need return the same $val as GETPOST
570  if (empty($method)) {
571  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
572  } elseif ($method == 1) {
573  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
574  } elseif ($method == 2) {
575  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
576  } elseif ($method == 3) {
577  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
578  } else {
579  $val = 'BadFirstParameterForGETPOST';
580  }
581 
582  return is_array($val);
583 }
584 
614 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
615 {
616  global $mysoc, $user, $conf;
617 
618  if (empty($paramname)) {
619  return 'BadFirstParameterForGETPOST';
620  }
621  if (empty($check)) {
622  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
623  // Enable this line to know who call the GETPOST with '' $check parameter.
624  //var_dump(debug_backtrace()[0]);
625  }
626 
627  if (empty($method)) {
628  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
629  } elseif ($method == 1) {
630  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
631  } elseif ($method == 2) {
632  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
633  } elseif ($method == 3) {
634  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
635  } else {
636  return 'BadThirdParameterForGETPOST';
637  }
638 
639  if (empty($method) || $method == 3 || $method == 4) {
640  $relativepathstring = $_SERVER["PHP_SELF"];
641  // Clean $relativepathstring
642  if (constant('DOL_URL_ROOT')) {
643  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
644  }
645  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
646  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
647  //var_dump($relativepathstring);
648  //var_dump($user->default_values);
649 
650  // Code for search criteria persistence.
651  // Retrieve values if restore_lastsearch_values
652  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
653  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
654  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
655  if (is_array($tmp)) {
656  foreach ($tmp as $key => $val) {
657  if ($key == $paramname) { // We are on the requested parameter
658  $out = $val;
659  break;
660  }
661  }
662  }
663  }
664  // If there is saved contextpage, page or limit
665  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
666  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
667  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
668  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
669  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
670  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
671  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
672  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
673  }
674  } elseif (!isset($_GET['sortfield'])) {
675  // Else, retrieve default values if we are not doing a sort
676  // 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
677  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
678  // Search default value from $object->field
679  global $object;
680  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
681  $out = $object->fields[$paramname]['default'];
682  }
683  }
684  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
685  if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
686  // Now search in setup to overwrite default values
687  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
688  if (isset($user->default_values[$relativepathstring]['createform'])) {
689  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
690  $qualified = 0;
691  if ($defkey != '_noquery_') {
692  $tmpqueryarraytohave = explode('&', $defkey);
693  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
694  $foundintru = 0;
695  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
696  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
697  $foundintru = 1;
698  }
699  }
700  if (!$foundintru) {
701  $qualified = 1;
702  }
703  //var_dump($defkey.'-'.$qualified);
704  } else {
705  $qualified = 1;
706  }
707 
708  if ($qualified) {
709  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
710  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
711  break;
712  }
713  }
714  }
715  }
716  }
717  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
718  // Management of default search_filters and sort order
719  if (!empty($user->default_values)) {
720  // $user->default_values defined from menu 'Setup - Default values'
721  //var_dump($user->default_values[$relativepathstring]);
722  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
723  // Sorted on which fields ? ASC or DESC ?
724  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
725  // Even if paramname is sortfield, data are stored into ['sortorder...']
726  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
727  $qualified = 0;
728  if ($defkey != '_noquery_') {
729  $tmpqueryarraytohave = explode('&', $defkey);
730  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
731  $foundintru = 0;
732  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
733  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
734  $foundintru = 1;
735  }
736  }
737  if (!$foundintru) {
738  $qualified = 1;
739  }
740  //var_dump($defkey.'-'.$qualified);
741  } else {
742  $qualified = 1;
743  }
744 
745  if ($qualified) {
746  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
747  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
748  if ($out) {
749  $out .= ', ';
750  }
751  if ($paramname == 'sortfield') {
752  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
753  }
754  if ($paramname == 'sortorder') {
755  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
756  }
757  }
758  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
759  }
760  }
761  }
762  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
763  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
764  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
765  continue;
766  }
767  $qualified = 0;
768  if ($defkey != '_noquery_') {
769  $tmpqueryarraytohave = explode('&', $defkey);
770  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
771  $foundintru = 0;
772  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
773  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
774  $foundintru = 1;
775  }
776  }
777  if (!$foundintru) {
778  $qualified = 1;
779  }
780  //var_dump($defkey.'-'.$qualified);
781  } else {
782  $qualified = 1;
783  }
784 
785  if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
786  // We must keep $_POST and $_GET here
787  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
788  // We made a search from quick search menu, do we still use default filter ?
789  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
790  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
791  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
792  }
793  } else {
794  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
795  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
796  }
797  break;
798  }
799  }
800  }
801  }
802  }
803  }
804  }
805  }
806 
807  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
808  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
809  // 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.
810  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
811  $reg = array();
812  $maxloop = 20;
813  $loopnb = 0; // Protection against infinite loop
814  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.
815  $loopnb++;
816  $newout = '';
817 
818  if ($reg[1] == 'DAY') {
819  $tmp = dol_getdate(dol_now(), true);
820  $newout = $tmp['mday'];
821  } elseif ($reg[1] == 'MONTH') {
822  $tmp = dol_getdate(dol_now(), true);
823  $newout = $tmp['mon'];
824  } elseif ($reg[1] == 'YEAR') {
825  $tmp = dol_getdate(dol_now(), true);
826  $newout = $tmp['year'];
827  } elseif ($reg[1] == 'PREVIOUS_DAY') {
828  $tmp = dol_getdate(dol_now(), true);
829  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
830  $newout = $tmp2['day'];
831  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
832  $tmp = dol_getdate(dol_now(), true);
833  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
834  $newout = $tmp2['month'];
835  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
836  $tmp = dol_getdate(dol_now(), true);
837  $newout = ($tmp['year'] - 1);
838  } elseif ($reg[1] == 'NEXT_DAY') {
839  $tmp = dol_getdate(dol_now(), true);
840  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
841  $newout = $tmp2['day'];
842  } elseif ($reg[1] == 'NEXT_MONTH') {
843  $tmp = dol_getdate(dol_now(), true);
844  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
845  $newout = $tmp2['month'];
846  } elseif ($reg[1] == 'NEXT_YEAR') {
847  $tmp = dol_getdate(dol_now(), true);
848  $newout = ($tmp['year'] + 1);
849  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
850  $newout = $mysoc->country_id;
851  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
852  $newout = $user->id;
853  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
854  $newout = $user->fk_user;
855  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
856  $newout = $conf->entity;
857  } else {
858  $newout = ''; // Key not found, we replace with empty string
859  }
860  //var_dump('__'.$reg[1].'__ -> '.$newout);
861  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
862  }
863  }
864 
865  // Check rule
866  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
867  if (!is_array($out) || empty($out)) {
868  $out = array();
869  } else {
870  $tmparray = explode(':', $check);
871  if (!empty($tmparray[1])) {
872  $tmpcheck = $tmparray[1];
873  } else {
874  $tmpcheck = 'alphanohtml';
875  }
876  foreach ($out as $outkey => $outval) {
877  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
878  }
879  }
880  } else {
881  // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
882  // 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
883  if (strpos($paramname, 'search_') === 0) {
884  $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
885  }
886 
887  $out = sanitizeVal($out, $check, $filter, $options);
888  }
889 
890  // Sanitizing for special parameters.
891  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
892  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
893  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
894  $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.
895  do {
896  $oldstringtoclean = $out;
897  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
898  $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'
899  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
900  } while ($oldstringtoclean != $out);
901  }
902 
903  // Code for search criteria persistence.
904  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
905  if (empty($method) || $method == 3 || $method == 4) {
906  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
907  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
908 
909  // We save search key only if $out not empty that means:
910  // - posted value not empty, or
911  // - 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).
912 
913  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
914  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
915  }
916  }
917  }
918 
919  return $out;
920 }
921 
931 function GETPOSTINT($paramname, $method = 0)
932 {
933  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
934 }
935 
936 
947 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
948 {
949  return sanitizeVal($out, $check, $filter, $options);
950 }
951 
961 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
962 {
963  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
964  // Check is done after replacement
965  switch ($check) {
966  case 'none':
967  break;
968  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
969  if (!is_numeric($out)) {
970  $out = '';
971  }
972  break;
973  case 'intcomma':
974  if (is_array($out)) {
975  $out = implode(',', $out);
976  }
977  if (preg_match('/[^0-9,-]+/i', $out)) {
978  $out = '';
979  }
980  break;
981  case 'san_alpha':
982  $out = filter_var($out, FILTER_SANITIZE_STRING);
983  break;
984  case 'email':
985  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
986  break;
987  case 'aZ':
988  if (!is_array($out)) {
989  $out = trim($out);
990  if (preg_match('/[^a-z]+/i', $out)) {
991  $out = '';
992  }
993  }
994  break;
995  case 'aZ09':
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 'aZ09arobase': // great to sanitize $objecttype parameter
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 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1012  if (!is_array($out)) {
1013  $out = trim($out);
1014  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1015  $out = '';
1016  }
1017  }
1018  break;
1019  case 'alpha': // No html and no ../ and "
1020  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1021  if (!is_array($out)) {
1022  $out = trim($out);
1023  do {
1024  $oldstringtoclean = $out;
1025  // Remove html tags
1026  $out = dol_string_nohtmltag($out, 0);
1027  // Remove also other dangerous string sequences
1028  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
1029  // '../' or '..\' is dangerous because it allows dir transversals
1030  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
1031  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
1032  } while ($oldstringtoclean != $out);
1033  // keep lines feed
1034  }
1035  break;
1036  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'
1037  if (!is_array($out)) {
1038  $out = trim($out);
1039  do {
1040  $oldstringtoclean = $out;
1041  // Remove html tags
1042  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1043  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
1044  // '../' or '..\' is dangerous because it allows dir transversals
1045  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
1046  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
1047  } while ($oldstringtoclean != $out);
1048  }
1049  break;
1050  case 'nohtml': // No html
1051  $out = dol_string_nohtmltag($out, 0);
1052  break;
1053  case 'restricthtmlnolink':
1054  case 'restricthtml': // Recommended for most html textarea
1055  case 'restricthtmlallowclass':
1056  case 'restricthtmlallowunvalid':
1057  $out = dol_htmlwithnojs($out, 1, $check);
1058  break;
1059  case 'custom':
1060  if (!empty($out)) {
1061  if (empty($filter)) {
1062  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1063  }
1064  /*if (empty($options)) {
1065  return 'BadParameterForGETPOST - Param 4 of sanitizeVal()';
1066  }*/
1067  $out = filter_var($out, $filter, $options);
1068  }
1069  break;
1070  }
1071 
1072  return $out;
1073 }
1074 
1075 
1076 if (!function_exists('dol_getprefix')) {
1086  function dol_getprefix($mode = '')
1087  {
1088  // If prefix is for email (we need to have $conf already loaded for this case)
1089  if ($mode == 'email') {
1090  global $conf;
1091 
1092  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1093  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
1094  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1095  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1096  return $_SERVER["SERVER_NAME"];
1097  }
1098  }
1099 
1100  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1101  if (!empty($conf->file->instance_unique_id)) {
1102  return sha1('dolibarr'.$conf->file->instance_unique_id);
1103  }
1104 
1105  // For backward compatibility when instance_unique_id is not set
1106  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1107  }
1108 
1109  // If prefix is for session (no need to have $conf loaded)
1110  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1111  $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
1112 
1113  // The recommended value (may be not defined for old versions)
1114  if (!empty($tmp_instance_unique_id)) {
1115  return sha1('dolibarr'.$tmp_instance_unique_id);
1116  }
1117 
1118  // For backward compatibility when instance_unique_id is not set
1119  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1120  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1121  } else {
1122  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1123  }
1124  }
1125 }
1126 
1137 function dol_include_once($relpath, $classname = '')
1138 {
1139  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']
1140 
1141  $fullpath = dol_buildpath($relpath);
1142 
1143  if (!file_exists($fullpath)) {
1144  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1145  return false;
1146  }
1147 
1148  if (!empty($classname) && !class_exists($classname)) {
1149  return include $fullpath;
1150  } else {
1151  return include_once $fullpath;
1152  }
1153 }
1154 
1155 
1166 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1167 {
1168  global $conf;
1169 
1170  $path = preg_replace('/^\//', '', $path);
1171 
1172  if (empty($type)) { // For a filesystem path
1173  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1174  if (is_array($conf->file->dol_document_root)) {
1175  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1176  if ($key == 'main') {
1177  continue;
1178  }
1179  // if (@file_exists($dirroot.'/'.$path)) {
1180  if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1181  $res = $dirroot.'/'.$path;
1182  return $res;
1183  }
1184  }
1185  }
1186  if ($returnemptyifnotfound) {
1187  // Not found into alternate dir
1188  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1189  return '';
1190  }
1191  }
1192  } else {
1193  // For an url path
1194  // We try to get local path of file on filesystem from url
1195  // Note that trying to know if a file on disk exist by forging path on disk from url
1196  // works only for some web server and some setup. This is bugged when
1197  // using proxy, rewriting, virtual path, etc...
1198  $res = '';
1199  if ($type == 1) {
1200  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1201  }
1202  if ($type == 2) {
1203  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1204  }
1205  if ($type == 3) {
1206  $res = DOL_URL_ROOT.'/'.$path;
1207  }
1208 
1209  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1210  if ($key == 'main') {
1211  if ($type == 3) {
1212  global $dolibarr_main_url_root;
1213 
1214  // Define $urlwithroot
1215  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1216  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1217  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1218 
1219  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1220  }
1221  continue;
1222  }
1223  $regs = array();
1224  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1225  if (!empty($regs[1])) {
1226  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1227  //if (file_exists($dirroot.'/'.$regs[1])) {
1228  if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1229  if ($type == 1) {
1230  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1231  }
1232  if ($type == 2) {
1233  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1234  }
1235  if ($type == 3) {
1236  global $dolibarr_main_url_root;
1237 
1238  // Define $urlwithroot
1239  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1240  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1241  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1242 
1243  $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
1244  }
1245  break;
1246  }
1247  }
1248  }
1249  }
1250 
1251  return $res;
1252 }
1253 
1265 function dol_clone($object, $native = 0)
1266 {
1267  if ($native == 0) {
1268  // deprecated method, use the method with native = 2 instead
1269  $tmpsavdb = null;
1270  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1271  $tmpsavdb = $object->db;
1272  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1273  }
1274 
1275  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1276 
1277  if (!empty($tmpsavdb)) {
1278  $object->db = $tmpsavdb;
1279  }
1280  } elseif ($native == 2) {
1281  // recommended method to have a full isolated cloned object
1282  $myclone = new stdClass();
1283  $tmparray = get_object_vars($object); // return only public properties
1284 
1285  if (is_array($tmparray)) {
1286  foreach ($tmparray as $propertykey => $propertyval) {
1287  if (is_scalar($propertyval) || is_array($propertyval)) {
1288  $myclone->$propertykey = $propertyval;
1289  }
1290  }
1291  }
1292  } else {
1293  $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)
1294  }
1295 
1296  return $myclone;
1297 }
1298 
1308 function dol_size($size, $type = '')
1309 {
1310  global $conf;
1311  if (empty($conf->dol_optimize_smallscreen)) {
1312  return $size;
1313  }
1314  if ($type == 'width' && $size > 250) {
1315  return 250;
1316  } else {
1317  return 10;
1318  }
1319 }
1320 
1321 
1333 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1334 {
1335  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1336  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1337  // Char '/' and '\' are file delimiters.
1338  // 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
1339  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1340  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1341  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1342  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1343  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1344  $tmp = str_replace('..', '', $tmp);
1345  return $tmp;
1346 }
1347 
1359 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1360 {
1361  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1362  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1363  // 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
1364  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1365  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1366  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1367  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1368  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1369  $tmp = str_replace('..', '', $tmp);
1370  return $tmp;
1371 }
1372 
1380 function dol_sanitizeUrl($stringtoclean, $type = 1)
1381 {
1382  // 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)
1383  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1384  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1385  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1386  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1387 
1388  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1389  if ($type == 1) {
1390  // removing : should disable links to external url like http:aaa)
1391  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1392  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1393  }
1394 
1395  do {
1396  $oldstringtoclean = $stringtoclean;
1397  // removing '&colon' should disable links to external url like http:aaa)
1398  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1399  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1400  } while ($oldstringtoclean != $stringtoclean);
1401 
1402  if ($type == 1) {
1403  // removing '//' should disable links to external url like //aaa or http//)
1404  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1405  }
1406 
1407  return $stringtoclean;
1408 }
1409 
1416 function dol_sanitizeEmail($stringtoclean)
1417 {
1418  do {
1419  $oldstringtoclean = $stringtoclean;
1420  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1421  } while ($oldstringtoclean != $stringtoclean);
1422 
1423  return $stringtoclean;
1424 }
1425 
1434 function dol_string_unaccent($str)
1435 {
1436  global $conf;
1437 
1438  if (is_null($str)) {
1439  return '';
1440  }
1441 
1442  if (utf8_check($str)) {
1443  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1444  $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1445  return $transliterator->transliterate($str);
1446  }
1447  // See http://www.utf8-chartable.de/
1448  $string = rawurlencode($str);
1449  $replacements = array(
1450  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1451  '%C3%87' => 'C',
1452  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1453  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1454  '%C3%91' => 'N',
1455  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1456  '%C5%A0' => 'S',
1457  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1458  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1459  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1460  '%C3%A7' => 'c',
1461  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1462  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1463  '%C3%B1' => 'n',
1464  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1465  '%C5%A1' => 's',
1466  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1467  '%C3%BD' => 'y', '%C3%BF' => 'y'
1468  );
1469  $string = strtr($string, $replacements);
1470  return rawurldecode($string);
1471  } else {
1472  // See http://www.ascii-code.com/
1473  $string = strtr(
1474  $str,
1475  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1476  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1477  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1478  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1479  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1480  \xF9\xFA\xFB\xFC\xFD\xFF",
1481  "AAAAAAC
1482  EEEEIIIIDN
1483  OOOOOUUUY
1484  aaaaaaceeee
1485  iiiidnooooo
1486  uuuuyy"
1487  );
1488  $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"));
1489  return $string;
1490  }
1491 }
1492 
1506 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1507 {
1508  $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1509  if (empty($keepspaces)) {
1510  $forbidden_chars_to_replace[] = " ";
1511  }
1512  $forbidden_chars_to_remove = array();
1513  //$forbidden_chars_to_remove=array("(",")");
1514 
1515  if (is_array($badcharstoreplace)) {
1516  $forbidden_chars_to_replace = $badcharstoreplace;
1517  }
1518  if (is_array($badcharstoremove)) {
1519  $forbidden_chars_to_remove = $badcharstoremove;
1520  }
1521 
1522  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1523 }
1524 
1525 
1539 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1540 {
1541  if ($removetabcrlf) {
1542  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1543  } else {
1544  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
1545  }
1546 }
1547 
1556 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1557 {
1558  if (is_null($stringtoescape)) {
1559  return '';
1560  }
1561 
1562  // escape quotes and backslashes, newlines, etc.
1563  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1564  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1565  if (empty($noescapebackslashn)) {
1566  $substitjs["\n"] = '\\n';
1567  $substitjs['\\'] = '\\\\';
1568  }
1569  if (empty($mode)) {
1570  $substitjs["'"] = "\\'";
1571  $substitjs['"'] = "\\'";
1572  } elseif ($mode == 1) {
1573  $substitjs["'"] = "\\'";
1574  } elseif ($mode == 2) {
1575  $substitjs['"'] = '\\"';
1576  } elseif ($mode == 3) {
1577  $substitjs["'"] = "\\'";
1578  $substitjs['"'] = "\\\"";
1579  }
1580  return strtr($stringtoescape, $substitjs);
1581 }
1582 
1589 function dol_escape_json($stringtoescape)
1590 {
1591  return str_replace('"', '\"', $stringtoescape);
1592 }
1593 
1601 function dolPrintLabel($s)
1602 {
1604 }
1605 
1613 function dolPrintHTML($s)
1614 {
1615  return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1)), 1, 1, 'common', 0, 1);
1616 }
1617 
1626 {
1628 }
1629 
1630 
1647 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1648 {
1649  if ($noescapetags == 'common') {
1650  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1651  }
1652  if ($cleanalsojavascript) {
1653  $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1654  }
1655 
1656  // escape quotes and backslashes, newlines, etc.
1657  if ($escapeonlyhtmltags) {
1658  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1659  } else {
1660  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1661  }
1662  if (!$keepb) {
1663  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>'', '<strong>'=>'', '</strong>'=>''));
1664  }
1665  if (!$keepn) {
1666  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1667  }
1668 
1669  if ($escapeonlyhtmltags) {
1670  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1671  } else {
1672  // Escape tags to keep
1673  // TODO Does not works yet when there is attributes into tag
1674  $tmparrayoftags = array();
1675  if ($noescapetags) {
1676  $tmparrayoftags = explode(',', $noescapetags);
1677  }
1678  if (count($tmparrayoftags)) {
1679  foreach ($tmparrayoftags as $tagtoreplace) {
1680  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1681  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1682  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1683  }
1684  }
1685 
1686  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1687 
1688  if (count($tmparrayoftags)) {
1689  foreach ($tmparrayoftags as $tagtoreplace) {
1690  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1691  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1692  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1693  }
1694  }
1695 
1696  return $result;
1697  }
1698 }
1699 
1707 function dol_strtolower($string, $encoding = "UTF-8")
1708 {
1709  if (function_exists('mb_strtolower')) {
1710  return mb_strtolower($string, $encoding);
1711  } else {
1712  return strtolower($string);
1713  }
1714 }
1715 
1724 function dol_strtoupper($string, $encoding = "UTF-8")
1725 {
1726  if (function_exists('mb_strtoupper')) {
1727  return mb_strtoupper($string, $encoding);
1728  } else {
1729  return strtoupper($string);
1730  }
1731 }
1732 
1741 function dol_ucfirst($string, $encoding = "UTF-8")
1742 {
1743  if (function_exists('mb_substr')) {
1744  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1745  } else {
1746  return ucfirst($string);
1747  }
1748 }
1749 
1758 function dol_ucwords($string, $encoding = "UTF-8")
1759 {
1760  if (function_exists('mb_convert_case')) {
1761  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1762  } else {
1763  return ucwords($string);
1764  }
1765 }
1766 
1788 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1789 {
1790  global $conf, $user, $debugbar;
1791 
1792  // If syslog module enabled
1793  if (!isModEnabled('syslog')) {
1794  return;
1795  }
1796 
1797  // Check if we are into execution of code of a website
1798  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1799  global $website, $websitekey;
1800  if (is_object($website) && !empty($website->ref)) {
1801  $suffixinfilename .= '_website_'.$website->ref;
1802  } elseif (!empty($websitekey)) {
1803  $suffixinfilename .= '_website_'.$websitekey;
1804  }
1805  }
1806 
1807  // Check if we have a forced suffix
1808  if (defined('USESUFFIXINLOG')) {
1809  $suffixinfilename .= constant('USESUFFIXINLOG');
1810  }
1811 
1812  if ($ident < 0) {
1813  foreach ($conf->loghandlers as $loghandlerinstance) {
1814  $loghandlerinstance->setIdent($ident);
1815  }
1816  }
1817 
1818  if (!empty($message)) {
1819  // Test log level
1820  $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');
1821  if (!array_key_exists($level, $logLevels)) {
1822  throw new Exception('Incorrect log level');
1823  }
1824  if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
1825  return;
1826  }
1827 
1828  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1829  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1830  }
1831 
1832  // If adding log inside HTML page is required
1833  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1834  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1835  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1836  }
1837 
1838  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1839  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1840  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1841  print "\n\n<!-- Log start\n";
1842  print dol_escape_htmltag($message)."\n";
1843  print "Log end -->\n";
1844  }
1845 
1846  $data = array(
1847  'message' => $message,
1848  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1849  'level' => $level,
1850  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1851  'ip' => false
1852  );
1853 
1854  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1855  if (!empty($remoteip)) {
1856  $data['ip'] = $remoteip;
1857  // This is when server run behind a reverse proxy
1858  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1859  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1860  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1861  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1862  }
1863  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1864  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1865  $data['ip'] = $_SERVER['SERVER_ADDR'];
1866  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1867  // 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).
1868  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1869  } elseif (!empty($_SERVER['LOGNAME'])) {
1870  // 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).
1871  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1872  }
1873 
1874  // Loop on each log handler and send output
1875  foreach ($conf->loghandlers as $loghandlerinstance) {
1876  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1877  continue;
1878  }
1879  $loghandlerinstance->export($data, $suffixinfilename);
1880  }
1881  unset($data);
1882  }
1883 
1884  if ($ident > 0) {
1885  foreach ($conf->loghandlers as $loghandlerinstance) {
1886  $loghandlerinstance->setIdent($ident);
1887  }
1888  }
1889 }
1890 
1907 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
1908 {
1909  global $conf;
1910 
1911  if (strpos($url, '?') > 0) {
1912  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1913  } else {
1914  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1915  }
1916 
1917  $out = '';
1918 
1919  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1920  if ($backtopagejsfields) {
1921  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1922  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1923  $backtopagejsfields = $name.":".$backtopagejsfields;
1924  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1925  } else {
1926  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1927  }
1928  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1929  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1930  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1931  }
1932 
1933  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1934  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
1935  $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
1936  if (empty($conf->use_javascript_ajax)) {
1937  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
1938  } elseif ($jsonopen) {
1939  $out .= ' href="#" onclick="'.$jsonopen.'"';
1940  } else {
1941  $out .= ' href="#"';
1942  }
1943  $out .= '>'.$buttonstring.'</a>';
1944 
1945  if (!empty($conf->use_javascript_ajax)) {
1946  // Add code to open url using the popup. Add also hidden field to retreive the returned variables
1947  $out .= '<!-- code to open popup and variables to retreive returned variables -->';
1948  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1949  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1950  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1951  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1952  $out .= '<script nonce="'.getNonce().'" type="text/javascript">
1953  jQuery(document).ready(function () {
1954  jQuery(".button_'.$name.'").click(function () {
1955  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1956  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1957  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1958  $tmpdialog.dialog({
1959  autoOpen: false,
1960  modal: true,
1961  height: (window.innerHeight - 150),
1962  width: \'80%\',
1963  title: \''.dol_escape_js($label).'\',
1964  open: function (event, ui) {
1965  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1966  },
1967  close: function (event, ui) {
1968  var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1969  var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1970  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1971  if (returnedid != "" && returnedid != "div for returned id") {
1972  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1973  }
1974  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1975  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1976  }
1977  }
1978  });
1979 
1980  $tmpdialog.dialog(\'open\');
1981  return false;
1982  });
1983  });
1984  </script>';
1985  }
1986  return $out;
1987 }
1988 
2005 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2006 {
2007  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2008 }
2009 
2026 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2027 {
2028  global $conf, $langs, $hookmanager;
2029 
2030  // Show title
2031  $showtitle = 1;
2032  if (!empty($conf->dol_optimize_smallscreen)) {
2033  $showtitle = 0;
2034  }
2035 
2036  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2037 
2038  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2039  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2040  }
2041 
2042  // Show right part
2043  if ($morehtmlright) {
2044  $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.
2045  }
2046 
2047  // Show title
2048  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2049  $limittitle = 30;
2050  $out .= '<a class="tabTitle">';
2051  if ($picto) {
2052  $noprefix = $pictoisfullpath;
2053  if (strpos($picto, 'fontawesome_') !== false) {
2054  $noprefix = 1;
2055  }
2056  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2057  }
2058  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2059  $out .= '</a>';
2060  }
2061 
2062  // Show tabs
2063 
2064  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2065  $maxkey = -1;
2066  if (is_array($links) && !empty($links)) {
2067  $keys = array_keys($links);
2068  if (count($keys)) {
2069  $maxkey = max($keys);
2070  }
2071  }
2072 
2073  // Show tabs
2074  // if =0 we don't use the feature
2075  if (empty($limittoshow)) {
2076  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2077  }
2078  if (!empty($conf->dol_optimize_smallscreen)) {
2079  $limittoshow = 2;
2080  }
2081 
2082  $displaytab = 0;
2083  $nbintab = 0;
2084  $popuptab = 0;
2085  $outmore = '';
2086  for ($i = 0; $i <= $maxkey; $i++) {
2087  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2088  // If active tab is already present
2089  if ($i >= $limittoshow) {
2090  $limittoshow--;
2091  }
2092  }
2093  }
2094 
2095  for ($i = 0; $i <= $maxkey; $i++) {
2096  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2097  $isactive = true;
2098  } else {
2099  $isactive = false;
2100  }
2101 
2102  if ($i < $limittoshow || $isactive) {
2103  // Output entry with a visible tab
2104  $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])).' -->';
2105 
2106  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2107  if (!empty($links[$i][0])) {
2108  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2109  } else {
2110  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2111  }
2112  } elseif (!empty($links[$i][1])) {
2113  //print "x $i $active ".$links[$i][2]." z";
2114  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
2115  if (!empty($links[$i][0])) {
2116  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2117  $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).'">';
2118  }
2119  $out .= $links[$i][1];
2120  if (!empty($links[$i][0])) {
2121  $out .= '</a>'."\n";
2122  }
2123  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2124  $out .= '</div>';
2125  }
2126 
2127  $out .= '</div>';
2128  } else {
2129  // Add entry into the combo popup with the other tabs
2130  if (!$popuptab) {
2131  $popuptab = 1;
2132  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2133  }
2134  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2135  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2136  if (!empty($links[$i][0])) {
2137  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2138  } else {
2139  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2140  }
2141  } elseif (!empty($links[$i][1])) {
2142  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2143  $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.
2144  $outmore .= '</a>'."\n";
2145  }
2146  $outmore .= '</div>';
2147 
2148  $nbintab++;
2149  }
2150  $displaytab = $i;
2151  }
2152  if ($popuptab) {
2153  $outmore .= '</div>';
2154  }
2155 
2156  if ($popuptab) { // If there is some tabs not shown
2157  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2158  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2159  $widthofpopup = 200;
2160 
2161  $tabsname = $moretabssuffix;
2162  if (empty($tabsname)) {
2163  $tabsname = str_replace("@", "", $picto);
2164  }
2165  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2166  $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".
2167  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2168  $out .= $outmore;
2169  $out .= '</div>';
2170  $out .= '<div></div>';
2171  $out .= "</div>\n";
2172 
2173  $out .= '<script nonce="'.getNonce().'">';
2174  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2175  var x = this.offsetLeft, y = this.offsetTop;
2176  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2177  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2178  $('#moretabsList".$tabsname."').css('".$right."','8px');
2179  }
2180  $('#moretabsList".$tabsname."').css('".$left."','auto');
2181  });
2182  ";
2183  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2184  $out .= "</script>";
2185  }
2186 
2187  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2188  $out .= "</div>\n";
2189  }
2190 
2191  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2192  $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom'))).'">'."\n";
2193  }
2194  if (!empty($dragdropfile)) {
2195  $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2196  }
2197  $parameters = array('tabname' => $active, 'out' => $out);
2198  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2199  if ($reshook > 0) {
2200  $out = $hookmanager->resPrint;
2201  }
2202 
2203  return $out;
2204 }
2205 
2213 function dol_fiche_end($notab = 0)
2214 {
2215  print dol_get_fiche_end($notab);
2216 }
2217 
2224 function dol_get_fiche_end($notab = 0)
2225 {
2226  if (!$notab || $notab == -1) {
2227  return "\n</div>\n";
2228  } else {
2229  return '';
2230  }
2231 }
2232 
2252 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2253 {
2254  global $conf, $form, $user, $langs, $hookmanager, $action;
2255 
2256  $error = 0;
2257 
2258  $maxvisiblephotos = 1;
2259  $showimage = 1;
2260  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2261  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2262  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2263  $showbarcode = 0;
2264  }
2265  $modulepart = 'unknown';
2266 
2267  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2268  $modulepart = $object->element;
2269  } elseif ($object->element == 'member') {
2270  $modulepart = 'memberphoto';
2271  } elseif ($object->element == 'user') {
2272  $modulepart = 'userphoto';
2273  }
2274 
2275  if (class_exists("Imagick")) {
2276  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2277  $modulepart = $object->element;
2278  } elseif ($object->element == 'fichinter') {
2279  $modulepart = 'ficheinter';
2280  } elseif ($object->element == 'contrat') {
2281  $modulepart = 'contract';
2282  } elseif ($object->element == 'order_supplier') {
2283  $modulepart = 'supplier_order';
2284  } elseif ($object->element == 'invoice_supplier') {
2285  $modulepart = 'supplier_invoice';
2286  }
2287  }
2288 
2289  if ($object->element == 'product') {
2290  $width = 80;
2291  $cssclass = 'photowithmargin photoref';
2292  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2293  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2294  if ($conf->browser->layout == 'phone') {
2295  $maxvisiblephotos = 1;
2296  }
2297  if ($showimage) {
2298  $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>';
2299  } else {
2300  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2301  $nophoto = '';
2302  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2303  } else { // Show no photo link
2304  $nophoto = '/public/theme/common/nophoto.png';
2305  $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>';
2306  }
2307  }
2308  } elseif ($object->element == 'ticket') {
2309  $width = 80;
2310  $cssclass = 'photoref';
2311  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2312  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2313  if ($conf->browser->layout == 'phone') {
2314  $maxvisiblephotos = 1;
2315  }
2316 
2317  if ($showimage) {
2318  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2319  if ($object->nbphoto > 0) {
2320  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2321  } else {
2322  $showimage = 0;
2323  }
2324  }
2325  if (!$showimage) {
2326  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2327  $nophoto = '';
2328  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2329  } else { // Show no photo link
2330  $nophoto = img_picto('No photo', 'object_ticket');
2331  $morehtmlleft .= '<!-- No photo to show -->';
2332  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2333  $morehtmlleft .= $nophoto;
2334  $morehtmlleft .= '</div></div>';
2335  }
2336  }
2337  } else {
2338  if ($showimage) {
2339  if ($modulepart != 'unknown') {
2340  $phototoshow = '';
2341  // Check if a preview file is available
2342  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2343  $objectref = dol_sanitizeFileName($object->ref);
2344  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2345  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2346  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2347  $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
2348  } else {
2349  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2350  }
2351  if (empty($subdir)) {
2352  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2353  }
2354 
2355  $filepath = $dir_output.$subdir."/";
2356 
2357  $filepdf = $filepath.$objectref.".pdf";
2358  $relativepath = $subdir.'/'.$objectref.'.pdf';
2359 
2360  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2361  $fileimage = $filepdf.'_preview.png';
2362  $relativepathimage = $relativepath.'_preview.png';
2363 
2364  $pdfexists = file_exists($filepdf);
2365 
2366  // If PDF file exists
2367  if ($pdfexists) {
2368  // Conversion du PDF en image png si fichier png non existant
2369  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2370  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2371  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2372  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2373  if ($ret < 0) {
2374  $error++;
2375  }
2376  }
2377  }
2378  }
2379 
2380  if ($pdfexists && !$error) {
2381  $heightforphotref = 80;
2382  if (!empty($conf->dol_optimize_smallscreen)) {
2383  $heightforphotref = 60;
2384  }
2385  // If the preview file is found
2386  if (file_exists($fileimage)) {
2387  $phototoshow = '<div class="photoref">';
2388  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2389  $phototoshow .= '</div>';
2390  }
2391  }
2392  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2393  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2394  }
2395 
2396  if ($phototoshow) {
2397  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2398  $morehtmlleft .= $phototoshow;
2399  $morehtmlleft .= '</div>';
2400  }
2401  }
2402 
2403  if (empty($phototoshow)) { // Show No photo link (picto of object)
2404  if ($object->element == 'action') {
2405  $width = 80;
2406  $cssclass = 'photorefcenter';
2407  $nophoto = img_picto('No photo', 'title_agenda');
2408  } else {
2409  $width = 14;
2410  $cssclass = 'photorefcenter';
2411  $picto = $object->picto;
2412  $prefix = 'object_';
2413  if ($object->element == 'project' && !$object->public) {
2414  $picto = 'project'; // instead of projectpub
2415  }
2416  if (strpos($picto, 'fontawesome_') !== false) {
2417  $prefix = '';
2418  }
2419  $nophoto = img_picto('No photo', $prefix.$picto);
2420  }
2421  $morehtmlleft .= '<!-- No photo to show -->';
2422  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2423  $morehtmlleft .= $nophoto;
2424  $morehtmlleft .= '</div></div>';
2425  }
2426  }
2427  }
2428 
2429  // Show barcode
2430  if ($showbarcode) {
2431  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2432  }
2433 
2434  if ($object->element == 'societe') {
2435  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2436  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2437  } else {
2438  $morehtmlstatus .= $object->getLibStatut(6);
2439  }
2440  } elseif ($object->element == 'product') {
2441  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2442  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2443  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2444  } else {
2445  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2446  }
2447  $morehtmlstatus .= ' &nbsp; ';
2448  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2449  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2450  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2451  } else {
2452  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2453  }
2454  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2455  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2456  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2457  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2458  }
2459  $morehtmlstatus .= $tmptxt;
2460  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2461  if ($object->statut == 0) {
2462  $morehtmlstatus .= $object->getLibStatut(5);
2463  } else {
2464  $morehtmlstatus .= $object->getLibStatut(4);
2465  }
2466  } elseif ($object->element == 'facturerec') {
2467  if ($object->frequency == 0) {
2468  $morehtmlstatus .= $object->getLibStatut(2);
2469  } else {
2470  $morehtmlstatus .= $object->getLibStatut(5);
2471  }
2472  } elseif ($object->element == 'project_task') {
2473  $object->fk_statut = 1;
2474  if ($object->progress > 0) {
2475  $object->fk_statut = 2;
2476  }
2477  if ($object->progress >= 100) {
2478  $object->fk_statut = 3;
2479  }
2480  $tmptxt = $object->getLibStatut(5);
2481  $morehtmlstatus .= $tmptxt; // No status on task
2482  } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
2483  $tmptxt = $object->getLibStatut(6);
2484  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2485  $tmptxt = $object->getLibStatut(5);
2486  }
2487  $morehtmlstatus .= $tmptxt;
2488  }
2489 
2490  // Add if object was dispatched "into accountancy"
2491  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2492  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2493  if (method_exists($object, 'getVentilExportCompta')) {
2494  $accounted = $object->getVentilExportCompta();
2495  $langs->load("accountancy");
2496  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2497  }
2498  }
2499 
2500  // Add alias for thirdparty
2501  if (!empty($object->name_alias)) {
2502  $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
2503  }
2504 
2505  // Add label
2506  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2507  if (!empty($object->label)) {
2508  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2509  }
2510  }
2511 
2512  // Show address and email
2513  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2514  $moreaddress = $object->getBannerAddress('refaddress', $object);
2515  if ($moreaddress) {
2516  $morehtmlref .= '<div class="refidno refaddress">';
2517  $morehtmlref .= $moreaddress;
2518  $morehtmlref .= '</div>';
2519  }
2520  }
2521  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)) {
2522  $morehtmlref .= '<div style="clear: both;"></div>';
2523  $morehtmlref .= '<div class="refidno opacitymedium">';
2524  $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
2525  $morehtmlref .= '</div>';
2526  }
2527 
2528  $parameters=array('morehtmlref'=>$morehtmlref);
2529  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2530  if ($reshook < 0) {
2531  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2532  } elseif (empty($reshook)) {
2533  $morehtmlref .= $hookmanager->resPrint;
2534  } elseif ($reshook > 0) {
2535  $morehtmlref = $hookmanager->resPrint;
2536  }
2537 
2538 
2539  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2540  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2541  print '</div>';
2542  print '<div class="underrefbanner clearboth"></div>';
2543 }
2544 
2554 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2555 {
2556  global $langs;
2557  $ret = '';
2558  if ($fieldrequired) {
2559  $ret .= '<span class="fieldrequired">';
2560  }
2561  $ret .= '<label for="'.$fieldkey.'">';
2562  $ret .= $langs->trans($langkey);
2563  $ret .= '</label>';
2564  if ($fieldrequired) {
2565  $ret .= '</span>';
2566  }
2567  return $ret;
2568 }
2569 
2577 function dol_bc($var, $moreclass = '')
2578 {
2579  global $bc;
2580  $ret = ' '.$bc[$var];
2581  if ($moreclass) {
2582  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2583  }
2584  return $ret;
2585 }
2586 
2600 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2601 {
2602  global $conf, $langs, $hookmanager;
2603 
2604  $ret = '';
2605  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2606 
2607  // See format of addresses on https://en.wikipedia.org/wiki/Address
2608  // Address
2609  if (empty($mode)) {
2610  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
2611  }
2612  // Zip/Town/State
2613  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2614  // US: title firstname name \n address lines \n town, state, zip \n country
2615  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2616  $ret .= (($ret && $town) ? $sep : '').$town;
2617 
2618  if (!empty($object->state)) {
2619  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2620  }
2621  if (!empty($object->zip)) {
2622  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2623  }
2624  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2625  // UK: title firstname name \n address lines \n town state \n zip \n country
2626  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2627  $ret .= ($ret ? $sep : '').$town;
2628  if (!empty($object->state)) {
2629  $ret .= ($ret ? ", " : '').$object->state;
2630  }
2631  if (!empty($object->zip)) {
2632  $ret .= ($ret ? $sep : '').$object->zip;
2633  }
2634  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2635  // ES: title firstname name \n address lines \n zip town \n state \n country
2636  $ret .= ($ret ? $sep : '').$object->zip;
2637  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2638  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2639  if (!empty($object->state)) {
2640  $ret .= $sep.$object->state;
2641  }
2642  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2643  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2644  // See https://www.sljfaq.org/afaq/addresses.html
2645  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2646  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2647  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2648  // IT: title firstname name\n address lines \n zip town state_code \n country
2649  $ret .= ($ret ? $sep : '').$object->zip;
2650  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2651  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2652  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2653  } else {
2654  // Other: title firstname name \n address lines \n zip town[, state] \n country
2655  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2656  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2657  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2658  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2659  $ret .= ($ret ? ", " : '').$object->state;
2660  }
2661  }
2662 
2663  if (!is_object($outputlangs)) {
2664  $outputlangs = $langs;
2665  }
2666  if ($withcountry) {
2667  $langs->load("dict");
2668  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2669  }
2670  if ($hookmanager) {
2671  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2672  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2673  if ($reshook > 0) {
2674  $ret = '';
2675  }
2676  $ret .= $hookmanager->resPrint;
2677  }
2678 
2679  return $ret;
2680 }
2681 
2682 
2683 
2692 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2693 {
2694  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2695  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2696  } else {
2697  return 'Error date into a not supported range';
2698  }
2699 }
2700 
2722 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2723 {
2724  global $conf, $langs;
2725 
2726  // If date undefined or "", we return ""
2727  if (dol_strlen($time) == 0) {
2728  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2729  }
2730 
2731  if ($tzoutput === 'auto') {
2732  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2733  }
2734 
2735  // Clean parameters
2736  $to_gmt = false;
2737  $offsettz = $offsetdst = 0;
2738  if ($tzoutput) {
2739  $to_gmt = true; // For backward compatibility
2740  if (is_string($tzoutput)) {
2741  if ($tzoutput == 'tzserver') {
2742  $to_gmt = false;
2743  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2744  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
2745  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
2746  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2747  $to_gmt = true;
2748  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2749 
2750  if (class_exists('DateTimeZone')) {
2751  $user_date_tz = new DateTimeZone($offsettzstring);
2752  $user_dt = new DateTime();
2753  $user_dt->setTimezone($user_date_tz);
2754  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2755  $offsettz = $user_dt->getOffset(); // should include dst ?
2756  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
2757  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2758  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2759  }
2760  }
2761  }
2762  }
2763  if (!is_object($outputlangs)) {
2764  $outputlangs = $langs;
2765  }
2766  if (!$format) {
2767  $format = 'daytextshort';
2768  }
2769 
2770  // Do we have to reduce the length of date (year on 2 chars) to save space.
2771  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2772  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2773  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2774  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2775  if ($formatwithoutreduce != $format) {
2776  $format = $formatwithoutreduce;
2777  $reduceformat = 1;
2778  } // so format 'dayreduceformat' is processed like day
2779 
2780  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2781  // TODO Add format daysmallyear and dayhoursmallyear
2782  if ($format == 'day') {
2783  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2784  } elseif ($format == 'hour') {
2785  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2786  } elseif ($format == 'hourduration') {
2787  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2788  } elseif ($format == 'daytext') {
2789  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2790  } elseif ($format == 'daytextshort') {
2791  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2792  } elseif ($format == 'dayhour') {
2793  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2794  } elseif ($format == 'dayhoursec') {
2795  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2796  } elseif ($format == 'dayhourtext') {
2797  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2798  } elseif ($format == 'dayhourtextshort') {
2799  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2800  } elseif ($format == 'dayhourlog') {
2801  // Format not sensitive to language
2802  $format = '%Y%m%d%H%M%S';
2803  } elseif ($format == 'dayhourlogsmall') {
2804  // Format not sensitive to language
2805  $format = '%y%m%d%H%M';
2806  } elseif ($format == 'dayhourldap') {
2807  $format = '%Y%m%d%H%M%SZ';
2808  } elseif ($format == 'dayhourxcard') {
2809  $format = '%Y%m%dT%H%M%SZ';
2810  } elseif ($format == 'dayxcard') {
2811  $format = '%Y%m%d';
2812  } elseif ($format == 'dayrfc') {
2813  $format = '%Y-%m-%d'; // DATE_RFC3339
2814  } elseif ($format == 'dayhourrfc') {
2815  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2816  } elseif ($format == 'standard') {
2817  $format = '%Y-%m-%d %H:%M:%S';
2818  }
2819 
2820  if ($reduceformat) {
2821  $format = str_replace('%Y', '%y', $format);
2822  $format = str_replace('yyyy', 'yy', $format);
2823  }
2824 
2825  // Clean format
2826  if (preg_match('/%b/i', $format)) { // There is some text to translate
2827  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2828  $format = str_replace('%b', '__b__', $format);
2829  $format = str_replace('%B', '__B__', $format);
2830  }
2831  if (preg_match('/%a/i', $format)) { // There is some text to translate
2832  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2833  $format = str_replace('%a', '__a__', $format);
2834  $format = str_replace('%A', '__A__', $format);
2835  }
2836 
2837  // Analyze date
2838  $reg = array();
2839  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
2840  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2841  return '';
2842  } 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
2843  // This part of code should not be used anymore.
2844  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2845  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2846  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2847  $syear = (!empty($reg[1]) ? $reg[1] : '');
2848  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2849  $sday = (!empty($reg[3]) ? $reg[3] : '');
2850  $shour = (!empty($reg[4]) ? $reg[4] : '');
2851  $smin = (!empty($reg[5]) ? $reg[5] : '');
2852  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2853 
2854  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2855 
2856  if ($to_gmt) {
2857  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2858  } else {
2859  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2860  }
2861  $dtts = new DateTime();
2862  $dtts->setTimestamp($time);
2863  $dtts->setTimezone($tzo);
2864  $newformat = str_replace(
2865  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2866  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2867  $format);
2868  $ret = $dtts->format($newformat);
2869  $ret = str_replace(
2870  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2871  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2872  $ret
2873  );
2874  } else {
2875  // Date is a timestamps
2876  if ($time < 100000000000) { // Protection against bad date values
2877  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2878 
2879  if ($to_gmt) {
2880  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2881  } else {
2882  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2883  }
2884  $dtts = new DateTime();
2885  $dtts->setTimestamp($timetouse);
2886  $dtts->setTimezone($tzo);
2887  $newformat = str_replace(
2888  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2889  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2890  $format);
2891  $ret = $dtts->format($newformat);
2892  $ret = str_replace(
2893  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2894  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2895  $ret
2896  );
2897  //var_dump($ret);exit;
2898  } else {
2899  $ret = 'Bad value '.$time.' for date';
2900  }
2901  }
2902 
2903  if (preg_match('/__b__/i', $format)) {
2904  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2905 
2906  if ($to_gmt) {
2907  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2908  } else {
2909  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2910  }
2911  $dtts = new DateTime();
2912  $dtts->setTimestamp($timetouse);
2913  $dtts->setTimezone($tzo);
2914  $month = $dtts->format("m");
2915  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2916  if ($encodetooutput) {
2917  $monthtext = $outputlangs->transnoentities('Month'.$month);
2918  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2919  } else {
2920  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2921  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2922  }
2923  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2924  $ret = str_replace('__b__', $monthtextshort, $ret);
2925  $ret = str_replace('__B__', $monthtext, $ret);
2926  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2927  //return $ret;
2928  }
2929  if (preg_match('/__a__/i', $format)) {
2930  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2931  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2932 
2933  if ($to_gmt) {
2934  $tzo = new DateTimeZone('UTC');
2935  } else {
2936  $tzo = new DateTimeZone(date_default_timezone_get());
2937  }
2938  $dtts = new DateTime();
2939  $dtts->setTimestamp($timetouse);
2940  $dtts->setTimezone($tzo);
2941  $w = $dtts->format("w");
2942  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2943 
2944  $ret = str_replace('__A__', $dayweek, $ret);
2945  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2946  }
2947 
2948  return $ret;
2949 }
2950 
2951 
2972 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2973 {
2974  $datetimeobj = new DateTime();
2975  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2976  if ($forcetimezone) {
2977  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2978  }
2979  $arrayinfo = array(
2980  'year'=>((int) date_format($datetimeobj, 'Y')),
2981  'mon'=>((int) date_format($datetimeobj, 'm')),
2982  'mday'=>((int) date_format($datetimeobj, 'd')),
2983  'wday'=>((int) date_format($datetimeobj, 'w')),
2984  'yday'=>((int) date_format($datetimeobj, 'z')),
2985  'hours'=>((int) date_format($datetimeobj, 'H')),
2986  'minutes'=>((int) date_format($datetimeobj, 'i')),
2987  'seconds'=>((int) date_format($datetimeobj, 's')),
2988  '0'=>$timestamp
2989  );
2990 
2991  return $arrayinfo;
2992 }
2993 
3015 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3016 {
3017  global $conf;
3018  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3019 
3020  if ($gm === 'auto') {
3021  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3022  }
3023  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3024 
3025  // Clean parameters
3026  if ($hour == -1 || empty($hour)) {
3027  $hour = 0;
3028  }
3029  if ($minute == -1 || empty($minute)) {
3030  $minute = 0;
3031  }
3032  if ($second == -1 || empty($second)) {
3033  $second = 0;
3034  }
3035 
3036  // Check parameters
3037  if ($check) {
3038  if (!$month || !$day) {
3039  return '';
3040  }
3041  if ($day > 31) {
3042  return '';
3043  }
3044  if ($month > 12) {
3045  return '';
3046  }
3047  if ($hour < 0 || $hour > 24) {
3048  return '';
3049  }
3050  if ($minute < 0 || $minute > 60) {
3051  return '';
3052  }
3053  if ($second < 0 || $second > 60) {
3054  return '';
3055  }
3056  }
3057 
3058  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3059  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3060  $localtz = new DateTimeZone($default_timezone);
3061  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3062  // We use dol_tz_string first because it is more reliable.
3063  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3064  try {
3065  $localtz = new DateTimeZone($default_timezone);
3066  } catch (Exception $e) {
3067  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
3068  $default_timezone = @date_default_timezone_get();
3069  }
3070  } elseif (strrpos($gm, "tz,") !== false) {
3071  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3072  try {
3073  $localtz = new DateTimeZone($timezone);
3074  } catch (Exception $e) {
3075  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3076  }
3077  }
3078 
3079  if (empty($localtz)) {
3080  $localtz = new DateTimeZone('UTC');
3081  }
3082  //var_dump($localtz);
3083  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3084  $dt = new DateTime('now', $localtz);
3085  $dt->setDate((int) $year, (int) $month, (int) $day);
3086  $dt->setTime((int) $hour, (int) $minute, (int) $second);
3087  $date = $dt->getTimestamp(); // should include daylight saving time
3088  //var_dump($date);
3089  return $date;
3090 }
3091 
3092 
3103 function dol_now($mode = 'auto')
3104 {
3105  $ret = 0;
3106 
3107  if ($mode === 'auto') {
3108  $mode = 'gmt';
3109  }
3110 
3111  if ($mode == 'gmt') {
3112  $ret = time(); // Time for now at greenwich.
3113  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3114  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3115  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3116  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3117  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3118  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3119  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3120  // $ret=dol_now('gmt')+($tzsecond*3600);
3121  //}
3122  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3123  // Time for now with user timezone added
3124  //print 'time: '.time();
3125  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3126  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3127  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3128  }
3129 
3130  return $ret;
3131 }
3132 
3133 
3142 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3143 {
3144  global $conf, $langs;
3145  $level = 1024;
3146 
3147  if (!empty($conf->dol_optimize_smallscreen)) {
3148  $shortunit = 1;
3149  }
3150 
3151  // Set value text
3152  if (empty($shortvalue) || $size < ($level * 10)) {
3153  $ret = $size;
3154  $textunitshort = $langs->trans("b");
3155  $textunitlong = $langs->trans("Bytes");
3156  } else {
3157  $ret = round($size / $level, 0);
3158  $textunitshort = $langs->trans("Kb");
3159  $textunitlong = $langs->trans("KiloBytes");
3160  }
3161  // Use long or short text unit
3162  if (empty($shortunit)) {
3163  $ret .= ' '.$textunitlong;
3164  } else {
3165  $ret .= ' '.$textunitshort;
3166  }
3167 
3168  return $ret;
3169 }
3170 
3181 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = 'float')
3182 {
3183  global $langs;
3184 
3185  if (empty($url)) {
3186  return '';
3187  }
3188 
3189  $link = '<a href="';
3190  if (!preg_match('/^http/i', $url)) {
3191  $link .= 'http://';
3192  }
3193  $link .= $url;
3194  $link .= '"';
3195  if ($target) {
3196  $link .= ' target="'.$target.'"';
3197  }
3198  $link .= '>';
3199  if (!preg_match('/^http/i', $url)) {
3200  $link .= 'http://';
3201  }
3202  $link .= dol_trunc($url, $max);
3203  $link .= '</a>';
3204 
3205  if ($morecss == 'float') {
3206  return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
3207  } else {
3208  return '<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</span>';
3209  }
3210 }
3211 
3224 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3225 {
3226  global $conf, $user, $langs, $hookmanager;
3227 
3228  $newemail = dol_escape_htmltag($email);
3229 
3230  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
3231  $withpicto = 0;
3232  }
3233 
3234  if (empty($email)) {
3235  return '&nbsp;';
3236  }
3237 
3238  if (!empty($addlink)) {
3239  $newemail = '<a style="text-overflow: ellipsis;" href="';
3240  if (!preg_match('/^mailto:/i', $email)) {
3241  $newemail .= 'mailto:';
3242  }
3243  $newemail .= $email;
3244  $newemail .= '">';
3245  $newemail .= dol_trunc($email, $max);
3246  $newemail .= '</a>';
3247  if ($showinvalid && !isValidEmail($email)) {
3248  $langs->load("errors");
3249  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3250  }
3251 
3252  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3253  $type = 'AC_EMAIL';
3254  $link = '';
3255  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
3256  $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>';
3257  }
3258  if ($link) {
3259  $newemail = '<div>'.$newemail.' '.$link.'</div>';
3260  }
3261  }
3262  } else {
3263  if ($showinvalid && !isValidEmail($email)) {
3264  $langs->load("errors");
3265  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3266  }
3267  }
3268 
3269  //$rep = '<div class="nospan" style="margin-right: 10px">';
3270  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3271  //$rep .= '</div>';
3272  if ($hookmanager) {
3273  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3274 
3275  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3276  if ($reshook > 0) {
3277  $rep = '';
3278  }
3279  $rep .= $hookmanager->resPrint;
3280  }
3281 
3282  return $rep;
3283 }
3284 
3291 {
3292  global $conf, $db;
3293 
3294  $socialnetworks = array();
3295  // Enable caching of array
3296  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3297  $cachekey = 'socialnetworks_' . $conf->entity;
3298  $dataretrieved = dol_getcache($cachekey);
3299  if (!is_null($dataretrieved)) {
3300  $socialnetworks = $dataretrieved;
3301  } else {
3302  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3303  $sql .= " WHERE entity=".$conf->entity;
3304  $resql = $db->query($sql);
3305  if ($resql) {
3306  while ($obj = $db->fetch_object($resql)) {
3307  $socialnetworks[$obj->code] = array(
3308  'rowid' => $obj->rowid,
3309  'label' => $obj->label,
3310  'url' => $obj->url,
3311  'icon' => $obj->icon,
3312  'active' => $obj->active,
3313  );
3314  }
3315  }
3316  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3317  }
3318 
3319  return $socialnetworks;
3320 }
3321 
3332 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3333 {
3334  global $conf, $user, $langs;
3335 
3336  $htmllink = $value;
3337 
3338  if (empty($value)) {
3339  return '&nbsp;';
3340  }
3341 
3342  if (!empty($type)) {
3343  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3344  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3345  $htmllink .= '<span class="fa pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3346  if ($type == 'skype') {
3347  $htmllink .= dol_escape_htmltag($value);
3348  $htmllink .= '&nbsp; <a href="skype:';
3349  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3350  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3351  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3352  $htmllink .= '</a><a href="skype:';
3353  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3354  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3355  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3356  $htmllink .= '</a>';
3357  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3358  $addlink = 'AC_SKYPE';
3359  $link = '';
3360  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3361  $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>';
3362  }
3363  $htmllink .= ($link ? ' '.$link : '');
3364  }
3365  } else {
3366  if (!empty($dictsocialnetworks[$type]['url'])) {
3367  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3368  if ($tmpvirginurl) {
3369  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3370  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3371 
3372  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3373  if ($tmpvirginurl3) {
3374  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3375  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3376  }
3377 
3378  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3379  if ($tmpvirginurl2) {
3380  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3381  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3382  }
3383  }
3384  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3385  if (preg_match('/^https?:\/\//i', $link)) {
3386  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3387  } else {
3388  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3389  }
3390  } else {
3391  $htmllink .= dol_escape_htmltag($value);
3392  }
3393  }
3394  $htmllink .= '</div>';
3395  } else {
3396  $langs->load("errors");
3397  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3398  }
3399  return $htmllink;
3400 }
3401 
3412 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3413 {
3414  global $mysoc;
3415 
3416  if (empty($profID) || empty($profIDtype)) {
3417  return '';
3418  }
3419  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3420  $newProfID = $profID;
3421  $id = substr($profIDtype, -1);
3422  $ret = '';
3423  if (strtoupper($countrycode) == 'FR') {
3424  // France
3425  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3426  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);
3427  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);
3428  }
3429  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3430  else $ret = $newProfID;
3431  return $ret;
3432 }
3433 
3448 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3449 {
3450  global $conf, $user, $langs, $mysoc, $hookmanager;
3451 
3452  // Clean phone parameter
3453  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3454  if (empty($phone)) {
3455  return '';
3456  }
3457  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3458  $separ = $conf->global->MAIN_PHONE_SEPAR;
3459  }
3460  if (empty($countrycode) && is_object($mysoc)) {
3461  $countrycode = $mysoc->country_code;
3462  }
3463 
3464  // Short format for small screens
3465  if ($conf->dol_optimize_smallscreen) {
3466  $separ = '';
3467  }
3468 
3469  $newphone = $phone;
3470  if (strtoupper($countrycode) == "FR") {
3471  // France
3472  if (dol_strlen($phone) == 10) {
3473  $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);
3474  } elseif (dol_strlen($phone) == 7) {
3475  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3476  } elseif (dol_strlen($phone) == 9) {
3477  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3478  } elseif (dol_strlen($phone) == 11) {
3479  $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);
3480  } elseif (dol_strlen($phone) == 12) {
3481  $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);
3482  } elseif (dol_strlen($phone) == 13) {
3483  $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);
3484  }
3485  } elseif (strtoupper($countrycode) == "CA") {
3486  if (dol_strlen($phone) == 10) {
3487  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3488  }
3489  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3490  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3491  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3492  }
3493  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3494  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3495  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3496  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3497  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3498  }
3499  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3500  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3501  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3502  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3503  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3504  }
3505  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3506  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3507  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3508  }
3509  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3510  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3511  $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);
3512  }
3513  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3514  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3515  $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);
3516  }
3517  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3518  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3519  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3520  }
3521  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3522  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3523  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3524  }
3525  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3526  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3527  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3528  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3529  $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);
3530  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3531  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3532  }
3533  } elseif (strtoupper($countrycode) == "ML") {//Mali
3534  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3535  $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);
3536  }
3537  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3538  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3539  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3540  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3541  $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);
3542  }
3543  } elseif (strtoupper($countrycode) == "MU") {
3544  //Maurice
3545  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3546  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3547  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3548  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3549  }
3550  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3551  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3552  $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);
3553  }
3554  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3555  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3556  $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);
3557  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3558  $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);
3559  }
3560  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3561  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3562  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3563  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3564  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3565  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3566  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3567  }
3568  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3569  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3570  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3571  }
3572  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3573  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3574  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3575  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3576  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3577  }
3578  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3579  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3580  $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);
3581  }
3582  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3583  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3584  $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);
3585  }
3586  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3587  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3588  $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);
3589  }
3590  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3591  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3592  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3593  }
3594  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3595  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3596  $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);
3597  }
3598  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3599  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3600  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3601  }
3602  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3603  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3604  $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);
3605  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3606  $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);
3607  }
3608  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3609  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3610  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3611  }
3612  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3613  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3614  $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);
3615  }
3616  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3617  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3618  $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);
3619  }
3620  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3621  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3622  $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);
3623  }
3624  } elseif (strtoupper($countrycode) == "IT") {//Italie
3625  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3626  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3627  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3628  $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);
3629  }
3630  } elseif (strtoupper($countrycode) == "AU") {
3631  //Australie
3632  if (dol_strlen($phone) == 12) {
3633  //ex: +61_A_BCDE_FGHI
3634  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3635  }
3636  } elseif (strtoupper($countrycode) == "LU") {
3637  // Luxembourg
3638  if (dol_strlen($phone) == 10) {// fixe 6 chiffres +352_AA_BB_CC
3639  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
3640  } elseif (dol_strlen($phone) == 11) {// fixe 7 chiffres +352_AA_BB_CC_D
3641  $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);
3642  } elseif (dol_strlen($phone) == 12) {// fixe 8 chiffres +352_AA_BB_CC_DD
3643  $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);
3644  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
3645  $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);
3646  }
3647  }
3648  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3649  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
3650  $newphoneform = $newphone;
3651  $newphone = '<a href="tel:'.$phone.'"';
3652  $newphone .= '>'.$newphoneform.'</a>';
3653  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3654  if (empty($user->clicktodial_loaded)) {
3655  $user->fetch_clicktodial();
3656  }
3657 
3658  // Define urlmask
3659  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3660  if (!empty($conf->global->CLICKTODIAL_URL)) {
3661  $urlmask = $conf->global->CLICKTODIAL_URL;
3662  }
3663  if (!empty($user->clicktodial_url)) {
3664  $urlmask = $user->clicktodial_url;
3665  }
3666 
3667  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3668  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3669  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3670  // This line is for backward compatibility
3671  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3672  // Thoose lines are for substitution
3673  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3674  '__PHONETO__'=>urlencode($phone),
3675  '__LOGIN__'=>$clicktodial_login,
3676  '__PASS__'=>$clicktodial_password);
3677  $url = make_substitutions($url, $substitarray);
3678  $newphonesav = $newphone;
3679  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3680  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3681  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3682  $newphone .= '>'.$newphonesav.'</a>';
3683  } else {
3684  // Old method
3685  $newphone = '<a href="'.$url.'"';
3686  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3687  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3688  }
3689  $newphone .= '>'.$newphonesav.'</a>';
3690  }
3691  }
3692 
3693  //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
3694  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3695  $type = 'AC_TEL';
3696  $link = '';
3697  if ($addlink == 'AC_FAX') {
3698  $type = 'AC_FAX';
3699  }
3700  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3701  $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>';
3702  }
3703  if ($link) {
3704  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3705  }
3706  }
3707  }
3708 
3709  if (empty($titlealt)) {
3710  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3711  }
3712  $rep = '';
3713 
3714  if ($hookmanager) {
3715  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3716  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3717  $rep .= $hookmanager->resPrint;
3718  }
3719  if (empty($reshook)) {
3720  $picto = '';
3721  if ($withpicto) {
3722  if ($withpicto == 'fax') {
3723  $picto = 'phoning_fax';
3724  } elseif ($withpicto == 'phone') {
3725  $picto = 'phoning';
3726  } elseif ($withpicto == 'mobile') {
3727  $picto = 'phoning_mobile';
3728  } else {
3729  $picto = '';
3730  }
3731  }
3732  if ($adddivfloat == 1) {
3733  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3734  } elseif (empty($adddivfloat)) {
3735  $rep .= '<span style="margin-right: 10px;">';
3736  }
3737  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3738  if ($adddivfloat == 1) {
3739  $rep .= '</div>';
3740  } elseif (empty($adddivfloat)) {
3741  $rep .= '</span>';
3742  }
3743  }
3744 
3745  return $rep;
3746 }
3747 
3755 function dol_print_ip($ip, $mode = 0)
3756 {
3757  global $conf, $langs;
3758 
3759  $ret = '';
3760 
3761  if (empty($mode)) {
3762  $ret .= $ip;
3763  }
3764 
3765  if ($mode != 2) {
3766  $countrycode = dolGetCountryCodeFromIp($ip);
3767  if ($countrycode) { // If success, countrycode is us, fr, ...
3768  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3769  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3770  } else {
3771  $ret .= ' ('.$countrycode.')';
3772  }
3773  } else {
3774  // Nothing
3775  }
3776  }
3777 
3778  return $ret;
3779 }
3780 
3790 {
3791  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3792  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3793  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3794  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3795  } else {
3796  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3797  }
3798  } else {
3799  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3800  }
3801  } else {
3802  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3803  }
3804  return $ip;
3805 }
3806 
3815 function isHTTPS()
3816 {
3817  $isSecure = false;
3818  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3819  $isSecure = true;
3820  } 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') {
3821  $isSecure = true;
3822  }
3823  return $isSecure;
3824 }
3825 
3833 {
3834  global $conf;
3835 
3836  $countrycode = '';
3837 
3838  if (!empty($conf->geoipmaxmind->enabled)) {
3839  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3840  //$ip='24.24.24.24';
3841  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3842  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3843  $geoip = new DolGeoIP('country', $datafile);
3844  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3845  $countrycode = $geoip->getCountryCodeFromIP($ip);
3846  }
3847 
3848  return $countrycode;
3849 }
3850 
3851 
3859 {
3860  global $conf, $langs, $user;
3861 
3862  //$ret=$user->xxx;
3863  $ret = '';
3864  if (!empty($conf->geoipmaxmind->enabled)) {
3865  $ip = getUserRemoteIP();
3866  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3867  //$ip='24.24.24.24';
3868  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3869  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3870  $geoip = new DolGeoIP('country', $datafile);
3871  $countrycode = $geoip->getCountryCodeFromIP($ip);
3872  $ret = $countrycode;
3873  }
3874  return $ret;
3875 }
3876 
3889 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3890 {
3891  global $conf, $user, $langs, $hookmanager;
3892 
3893  $out = '';
3894 
3895  if ($address) {
3896  if ($hookmanager) {
3897  $parameters = array('element' => $element, 'id' => $id);
3898  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3899  $out .= $hookmanager->resPrint;
3900  }
3901  if (empty($reshook)) {
3902  if (empty($charfornl)) {
3903  $out .= nl2br($address);
3904  } else {
3905  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3906  }
3907 
3908  // TODO Remove this block, we can add this using the hook now
3909  $showgmap = $showomap = 0;
3910  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3911  $showgmap = 1;
3912  }
3913  if ($element == 'contact' && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3914  $showgmap = 1;
3915  }
3916  if ($element == 'member' && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3917  $showgmap = 1;
3918  }
3919  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3920  $showomap = 1;
3921  }
3922  if ($element == 'contact' && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3923  $showomap = 1;
3924  }
3925  if ($element == 'member' && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3926  $showomap = 1;
3927  }
3928  if ($showgmap) {
3929  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3930  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3931  }
3932  if ($showomap) {
3933  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3934  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3935  }
3936  }
3937  }
3938  if ($noprint) {
3939  return $out;
3940  } else {
3941  print $out;
3942  }
3943 }
3944 
3945 
3955 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3956 {
3957  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3958  return true;
3959  }
3960  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3961  return true;
3962  }
3963  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3964  return true;
3965  }
3966 
3967  return false;
3968 }
3969 
3978 function isValidMXRecord($domain)
3979 {
3980  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3981  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3982  return 0;
3983  }
3984  if (function_exists('getmxrr')) {
3985  $mxhosts = array();
3986  $weight = array();
3987  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3988  if (count($mxhosts) > 1) {
3989  return 1;
3990  }
3991  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3992  return 1;
3993  }
3994 
3995  return 0;
3996  }
3997  }
3998 
3999  // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4000  return -1;
4001 }
4002 
4010 function isValidPhone($phone)
4011 {
4012  return true;
4013 }
4014 
4015 
4025 function dolGetFirstLetters($s, $nbofchar = 1)
4026 {
4027  $ret = '';
4028  $tmparray = explode(' ', $s);
4029  foreach ($tmparray as $tmps) {
4030  $ret .= dol_substr($tmps, 0, $nbofchar);
4031  }
4032 
4033  return $ret;
4034 }
4035 
4036 
4044 function dol_strlen($string, $stringencoding = 'UTF-8')
4045 {
4046  if (is_null($string)) {
4047  return 0;
4048  }
4049 
4050  if (function_exists('mb_strlen')) {
4051  return mb_strlen($string, $stringencoding);
4052  } else {
4053  return strlen($string);
4054  }
4055 }
4056 
4067 function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4068 {
4069  global $langs;
4070 
4071  if (empty($stringencoding)) {
4072  $stringencoding = $langs->charset_output;
4073  }
4074 
4075  $ret = '';
4076  if (empty($trunconbytes)) {
4077  if (function_exists('mb_substr')) {
4078  $ret = mb_substr($string, $start, $length, $stringencoding);
4079  } else {
4080  $ret = substr($string, $start, $length);
4081  }
4082  } else {
4083  if (function_exists('mb_strcut')) {
4084  $ret = mb_strcut($string, $start, $length, $stringencoding);
4085  } else {
4086  $ret = substr($string, $start, $length);
4087  }
4088  }
4089  return $ret;
4090 }
4091 
4092 
4106 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4107 {
4108  global $conf;
4109 
4110  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
4111  return $string;
4112  }
4113 
4114  if (empty($stringencoding)) {
4115  $stringencoding = 'UTF-8';
4116  }
4117  // reduce for small screen
4118  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
4119  $size = round($size / 3);
4120  }
4121 
4122  // We go always here
4123  if ($trunc == 'right') {
4124  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4125  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4126  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4127  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4128  } else {
4129  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4130  return $string;
4131  }
4132  } elseif ($trunc == 'middle') {
4133  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4134  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4135  $size1 = round($size / 2);
4136  $size2 = round($size / 2);
4137  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4138  } else {
4139  return $string;
4140  }
4141  } elseif ($trunc == 'left') {
4142  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4143  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4144  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4145  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4146  } else {
4147  return $string;
4148  }
4149  } elseif ($trunc == 'wrap') {
4150  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4151  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4152  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4153  } else {
4154  return $string;
4155  }
4156  } else {
4157  return 'BadParam3CallingDolTrunc';
4158  }
4159 }
4160 
4182 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4183 {
4184  global $conf, $langs;
4185 
4186  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4187  $url = DOL_URL_ROOT;
4188  $theme = isset($conf->theme) ? $conf->theme : null;
4189  $path = 'theme/'.$theme;
4190  // Define fullpathpicto to use into src
4191  if ($pictoisfullpath) {
4192  // Clean parameters
4193  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4194  $picto .= '.png';
4195  }
4196  $fullpathpicto = $picto;
4197  $reg = array();
4198  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4199  $morecss .= ($morecss ? ' ' : '').$reg[1];
4200  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4201  }
4202  } else {
4203  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
4204  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4205  $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4206 
4207  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
4208  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
4209  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4210  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4211 
4212  $pictowithouttextarray = explode('_', $pictowithouttext);
4213  $marginleftonlyshort = 0;
4214 
4215  if (!empty($pictowithouttextarray[1])) {
4216  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4217  $fakey = 'fa-'.$pictowithouttextarray[0];
4218  $fa = empty($pictowithouttextarray[1]) ? 'fa' : $pictowithouttextarray[1];
4219  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4220  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4221  } else {
4222  $fakey = 'fa-'.$pictowithouttext;
4223  $fa = 'fa';
4224  $facolor = '';
4225  $fasize = '';
4226  }
4227 
4228  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4229  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4230  $morestyle = '';
4231  $reg = array();
4232  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4233  $morecss .= ($morecss ? ' ' : '').$reg[1];
4234  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4235  }
4236  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4237  $morestyle = $reg[1];
4238  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4239  }
4240  $moreatt = trim($moreatt);
4241 
4242  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4243  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4244  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4245  $enabledisablehtml .= $titlealt;
4246  }*/
4247  $enabledisablehtml .= '</span>';
4248 
4249  return $enabledisablehtml;
4250  }
4251 
4252  if (empty($srconly) && in_array($pictowithouttext, array(
4253  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4254  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
4255  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
4256  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4257  'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4258  'currency', 'multicurrency',
4259  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4260  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4261  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
4262  'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4263  'hands-helping', 'help', 'holiday',
4264  'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4265  'knowledgemanagement',
4266  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4267  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4268  'off', 'on', 'order',
4269  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4270  'stock', 'resize', 'service', 'stats', 'trip',
4271  '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',
4272  'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4273  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4274  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4275  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4276  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4277  'technic', 'ticket',
4278  'error', 'warning',
4279  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4280  'shapes', 'skill', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4281  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
4282  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4283  'conferenceorbooth', 'eventorganization',
4284  'stamp', 'signature'
4285  ))) {
4286  $fakey = $pictowithouttext;
4287  $facolor = '';
4288  $fasize = '';
4289  $fa = 'fas';
4290  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'))) {
4291  $fa = 'far';
4292  }
4293  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4294  $fa = 'fab';
4295  }
4296 
4297  $arrayconvpictotofa = array(
4298  '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',
4299  'bank_account'=>'university',
4300  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
4301  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
4302  'bom'=>'shapes',
4303  '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',
4304  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
4305  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
4306  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4307  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4308  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4309  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4310  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4311  'generic'=>'file', 'holiday'=>'umbrella-beach',
4312  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'jobprofile'=>'cogs',
4313  'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4314  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4315  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4316  'sign-out'=>'sign-out-alt',
4317  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_warning'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4318  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4319  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4320  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4321  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4322  'other'=>'square',
4323  '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',
4324  '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',
4325  'recent' => 'check-square', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4326  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4327  'refresh'=>'redo', 'region'=>'map-marked', 'replacement'=>'exchange-alt', 'resource'=>'laptop-house', 'recurring'=>'history',
4328  'service'=>'concierge-bell',
4329  'skill'=>'shapes', 'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4330  'supplier'=>'building', 'technic'=>'cogs',
4331  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4332  'title_agenda'=>'calendar-alt',
4333  'uncheck'=>'times', 'uparrow'=>'share', 'url'=>'external-link-alt', 'vat'=>'money-check-alt', 'vcard'=>'arrow-alt-circle-down',
4334  'jabber'=>'comment-o',
4335  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4336  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4337  );
4338  if ($pictowithouttext == 'off') {
4339  $fakey = 'fa-square';
4340  $fasize = '1.3em';
4341  } elseif ($pictowithouttext == 'on') {
4342  $fakey = 'fa-check-square';
4343  $fasize = '1.3em';
4344  } elseif ($pictowithouttext == 'listlight') {
4345  $fakey = 'fa-download';
4346  $marginleftonlyshort = 1;
4347  } elseif ($pictowithouttext == 'printer') {
4348  $fakey = 'fa-print';
4349  $fasize = '1.2em';
4350  } elseif ($pictowithouttext == 'note') {
4351  $fakey = 'fa-sticky-note';
4352  $marginleftonlyshort = 1;
4353  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4354  $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');
4355  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4356  if (preg_match('/selected/', $pictowithouttext)) {
4357  $facolor = '#888';
4358  }
4359  $marginleftonlyshort = 1;
4360  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4361  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4362  } else {
4363  $fakey = 'fa-'.$pictowithouttext;
4364  }
4365 
4366  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4367  $morecss .= ' em092';
4368  }
4369  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4370  $morecss .= ' em088';
4371  }
4372  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4373  $morecss .= ' em080';
4374  }
4375 
4376  // Define $marginleftonlyshort
4377  $arrayconvpictotomarginleftonly = array(
4378  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4379  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4380  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4381  );
4382  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4383  $marginleftonlyshort = 0;
4384  }
4385 
4386  // Add CSS
4387  $arrayconvpictotomorcess = array(
4388  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4389  'bank_account'=>'infobox-bank_account',
4390  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4391  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4392  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4393  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4394  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4395  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4396  'incoterm'=>'infobox-supplier_proposal',
4397  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4398  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4399  'order'=>'infobox-commande',
4400  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4401  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_warning'=>'font-status4 warning', 'switch_on_red'=>'font-status8',
4402  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4403  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4404  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4405  'propal'=>'infobox-propal', 'proposal'=>'infobox-propal','private'=>'infobox-project',
4406  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4407  'resource'=>'infobox-action',
4408  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4409  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4410  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4411  'vat'=>'infobox-bank_account',
4412  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4413  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4414  );
4415  if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4416  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4417  }
4418 
4419  // Define $color
4420  $arrayconvpictotocolor = array(
4421  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4422  'clone'=>'#999', 'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4423  'dynamicprice'=>'#a69944',
4424  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4425  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4426  'lock'=>'#ddd', 'lot'=>'#a69944',
4427  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4428  'other'=>'#ddd', 'world'=>'#986c6a',
4429  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4430  //'shipment'=>'#a69944',
4431  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4432  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4433  'website'=>'#304', 'workstation'=>'#a69944'
4434  );
4435  if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4436  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4437  }
4438 
4439  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4440  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4441  $morestyle = '';
4442  $reg = array();
4443  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4444  $morecss .= ($morecss ? ' ' : '').$reg[1];
4445  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4446  }
4447  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4448  $morestyle = $reg[1];
4449  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4450  }
4451  $moreatt = trim($moreatt);
4452 
4453  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4454  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4455  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4456  $enabledisablehtml .= $titlealt;
4457  }*/
4458  $enabledisablehtml .= '</span>';
4459 
4460  return $enabledisablehtml;
4461  }
4462 
4463  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4464  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4465  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4466  $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
4467  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4468  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4469  }
4470 
4471  // If we ask an image into $url/$mymodule/img (instead of default path)
4472  $regs = array();
4473  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4474  $picto = $regs[1];
4475  $path = $regs[2]; // $path is $mymodule
4476  }
4477 
4478  // Clean parameters
4479  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4480  $picto .= '.png';
4481  }
4482  // If alt path are defined, define url where img file is, according to physical path
4483  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4484  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4485  if ($type == 'main') {
4486  continue;
4487  }
4488  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4489  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4490  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4491  break;
4492  }
4493  }
4494 
4495  // $url is '' or '/custom', $path is current theme or
4496  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4497  }
4498 
4499  if ($srconly) {
4500  return $fullpathpicto;
4501  }
4502 
4503  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4504  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
4505 }
4506 
4520 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4521 {
4522  if (strpos($picto, '^') === 0) {
4523  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4524  } else {
4525  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4526  }
4527 }
4528 
4540 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4541 {
4542  global $conf;
4543 
4544  if (is_numeric($picto)) {
4545  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4546  //$picto = $leveltopicto[$picto];
4547  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4548  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4549  $picto .= '.png';
4550  }
4551 
4552  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4553 
4554  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4555 }
4556 
4568 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4569 {
4570  global $conf;
4571 
4572  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4573  $picto .= '.png';
4574  }
4575 
4576  if ($pictoisfullpath) {
4577  $path = $picto;
4578  } else {
4579  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4580 
4581  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4582  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4583 
4584  if (file_exists($themepath)) {
4585  $path = $themepath;
4586  }
4587  }
4588  }
4589 
4590  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4591 }
4592 
4606 function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
4607 {
4608  global $langs;
4609 
4610  if (empty($titlealt) || $titlealt == 'default') {
4611  if ($numaction == '-1' || $numaction == 'ST_NO') {
4612  $numaction = -1;
4613  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4614  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4615  $numaction = 0;
4616  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4617  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4618  $numaction = 1;
4619  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4620  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4621  $numaction = 2;
4622  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4623  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4624  $numaction = 3;
4625  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4626  } else {
4627  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4628  $numaction = 0;
4629  }
4630  }
4631  if (!is_numeric($numaction)) {
4632  $numaction = 0;
4633  }
4634 
4635  return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
4636 }
4637 
4645 function img_pdf($titlealt = 'default', $size = 3)
4646 {
4647  global $langs;
4648 
4649  if ($titlealt == 'default') {
4650  $titlealt = $langs->trans('Show');
4651  }
4652 
4653  return img_picto($titlealt, 'pdf'.$size.'.png');
4654 }
4655 
4663 function img_edit_add($titlealt = 'default', $other = '')
4664 {
4665  global $langs;
4666 
4667  if ($titlealt == 'default') {
4668  $titlealt = $langs->trans('Add');
4669  }
4670 
4671  return img_picto($titlealt, 'edit_add.png', $other);
4672 }
4680 function img_edit_remove($titlealt = 'default', $other = '')
4681 {
4682  global $langs;
4683 
4684  if ($titlealt == 'default') {
4685  $titlealt = $langs->trans('Remove');
4686  }
4687 
4688  return img_picto($titlealt, 'edit_remove.png', $other);
4689 }
4690 
4699 function img_edit($titlealt = 'default', $float = 0, $other = '')
4700 {
4701  global $langs;
4702 
4703  if ($titlealt == 'default') {
4704  $titlealt = $langs->trans('Modify');
4705  }
4706 
4707  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4708 }
4709 
4718 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4719 {
4720  global $langs;
4721 
4722  if ($titlealt == 'default') {
4723  $titlealt = $langs->trans('View');
4724  }
4725 
4726  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4727 
4728  return img_picto($titlealt, 'eye', $moreatt);
4729 }
4730 
4739 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4740 {
4741  global $langs;
4742 
4743  if ($titlealt == 'default') {
4744  $titlealt = $langs->trans('Delete');
4745  }
4746 
4747  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4748 }
4749 
4757 function img_printer($titlealt = "default", $other = '')
4758 {
4759  global $langs;
4760  if ($titlealt == "default") {
4761  $titlealt = $langs->trans("Print");
4762  }
4763  return img_picto($titlealt, 'printer.png', $other);
4764 }
4765 
4773 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4774 {
4775  global $langs;
4776 
4777  if ($titlealt == 'default') {
4778  $titlealt = $langs->trans('Split');
4779  }
4780 
4781  return img_picto($titlealt, 'split.png', $other);
4782 }
4783 
4791 function img_help($usehelpcursor = 1, $usealttitle = 1)
4792 {
4793  global $langs;
4794 
4795  if ($usealttitle) {
4796  if (is_string($usealttitle)) {
4797  $usealttitle = dol_escape_htmltag($usealttitle);
4798  } else {
4799  $usealttitle = $langs->trans('Info');
4800  }
4801  }
4802 
4803  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4804 }
4805 
4812 function img_info($titlealt = 'default')
4813 {
4814  global $langs;
4815 
4816  if ($titlealt == 'default') {
4817  $titlealt = $langs->trans('Informations');
4818  }
4819 
4820  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4821 }
4822 
4831 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4832 {
4833  global $langs;
4834 
4835  if ($titlealt == 'default') {
4836  $titlealt = $langs->trans('Warning');
4837  }
4838 
4839  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4840  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4841 }
4842 
4849 function img_error($titlealt = 'default')
4850 {
4851  global $langs;
4852 
4853  if ($titlealt == 'default') {
4854  $titlealt = $langs->trans('Error');
4855  }
4856 
4857  return img_picto($titlealt, 'error.png');
4858 }
4859 
4867 function img_next($titlealt = 'default', $moreatt = '')
4868 {
4869  global $langs;
4870 
4871  if ($titlealt == 'default') {
4872  $titlealt = $langs->trans('Next');
4873  }
4874 
4875  //return img_picto($titlealt, 'next.png', $moreatt);
4876  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4877 }
4878 
4886 function img_previous($titlealt = 'default', $moreatt = '')
4887 {
4888  global $langs;
4889 
4890  if ($titlealt == 'default') {
4891  $titlealt = $langs->trans('Previous');
4892  }
4893 
4894  //return img_picto($titlealt, 'previous.png', $moreatt);
4895  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4896 }
4897 
4906 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4907 {
4908  global $langs;
4909 
4910  if ($titlealt == 'default') {
4911  $titlealt = $langs->trans('Down');
4912  }
4913 
4914  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4915 }
4916 
4925 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4926 {
4927  global $langs;
4928 
4929  if ($titlealt == 'default') {
4930  $titlealt = $langs->trans('Up');
4931  }
4932 
4933  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4934 }
4935 
4944 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4945 {
4946  global $langs;
4947 
4948  if ($titlealt == 'default') {
4949  $titlealt = $langs->trans('Left');
4950  }
4951 
4952  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4953 }
4954 
4963 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4964 {
4965  global $langs;
4966 
4967  if ($titlealt == 'default') {
4968  $titlealt = $langs->trans('Right');
4969  }
4970 
4971  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4972 }
4973 
4981 function img_allow($allow, $titlealt = 'default')
4982 {
4983  global $langs;
4984 
4985  if ($titlealt == 'default') {
4986  $titlealt = $langs->trans('Active');
4987  }
4988 
4989  if ($allow == 1) {
4990  return img_picto($titlealt, 'tick.png');
4991  }
4992 
4993  return '-';
4994 }
4995 
5003 function img_credit_card($brand, $morecss = null)
5004 {
5005  if (is_null($morecss)) {
5006  $morecss = 'fa-2x';
5007  }
5008 
5009  if ($brand == 'visa' || $brand == 'Visa') {
5010  $brand = 'cc-visa';
5011  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5012  $brand = 'cc-mastercard';
5013  } elseif ($brand == 'amex' || $brand == 'American Express') {
5014  $brand = 'cc-amex';
5015  } elseif ($brand == 'discover' || $brand == 'Discover') {
5016  $brand = 'cc-discover';
5017  } elseif ($brand == 'jcb' || $brand == 'JCB') {
5018  $brand = 'cc-jcb';
5019  } elseif ($brand == 'diners' || $brand == 'Diners club') {
5020  $brand = 'cc-diners-club';
5021  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5022  $brand = 'credit-card';
5023  }
5024 
5025  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5026 }
5027 
5036 function img_mime($file, $titlealt = '', $morecss = '')
5037 {
5038  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5039 
5040  $mimetype = dol_mimetype($file, '', 1);
5041  $mimeimg = dol_mimetype($file, '', 2);
5042  $mimefa = dol_mimetype($file, '', 4);
5043 
5044  if (empty($titlealt)) {
5045  $titlealt = 'Mime type: '.$mimetype;
5046  }
5047 
5048  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5049  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5050 }
5051 
5052 
5060 function img_search($titlealt = 'default', $other = '')
5061 {
5062  global $conf, $langs;
5063 
5064  if ($titlealt == 'default') {
5065  $titlealt = $langs->trans('Search');
5066  }
5067 
5068  $img = img_picto($titlealt, 'search.png', $other, false, 1);
5069 
5070  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5071  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5072 
5073  return $input;
5074 }
5075 
5083 function img_searchclear($titlealt = 'default', $other = '')
5084 {
5085  global $conf, $langs;
5086 
5087  if ($titlealt == 'default') {
5088  $titlealt = $langs->trans('Search');
5089  }
5090 
5091  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
5092 
5093  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5094  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5095 
5096  return $input;
5097 }
5098 
5110 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
5111 {
5112  global $conf, $langs;
5113 
5114  if ($infoonimgalt) {
5115  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5116  } else {
5117  if (empty($conf->use_javascript_ajax)) {
5118  $textfordropdown = '';
5119  }
5120 
5121  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5122  $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>');
5123 
5124  if ($textfordropdown) {
5125  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5126  $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5127  jQuery(document).ready(function() {
5128  jQuery(".'.$class.'text").click(function() {
5129  console.log("toggle text");
5130  jQuery(".'.$class.'").toggle();
5131  });
5132  });
5133  </script>';
5134 
5135  $result = $tmpresult.$result;
5136  }
5137  }
5138 
5139  return $result;
5140 }
5141 
5142 
5154 function dol_print_error($db = '', $error = '', $errors = null)
5155 {
5156  global $conf, $langs, $argv;
5157  global $dolibarr_main_prod;
5158 
5159  $out = '';
5160  $syslog = '';
5161 
5162  // If error occurs before the $lang object was loaded
5163  if (!$langs) {
5164  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5165  $langs = new Translate('', $conf);
5166  $langs->load("main");
5167  }
5168 
5169  // Load translation files required by the error messages
5170  $langs->loadLangs(array('main', 'errors'));
5171 
5172  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5173  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5174  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5175  $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";
5176  }
5177  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5178 
5179  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5180  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5181  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5182  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5183  }
5184  if (function_exists("phpversion")) {
5185  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5186  }
5187  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5188  if (function_exists("php_uname")) {
5189  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5190  }
5191  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5192  $out .= "<br>\n";
5193  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5194  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5195  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5196  $out .= "<br>\n";
5197  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5198  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5199  } else { // Mode CLI
5200  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5201  $syslog .= "pid=".dol_getmypid();
5202  }
5203 
5204  if (!empty($conf->modules)) {
5205  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
5206  }
5207 
5208  if (is_object($db)) {
5209  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5210  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5211  $lastqueryerror = $db->lastqueryerror();
5212  if (!utf8_check($lastqueryerror)) {
5213  $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5214  }
5215  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5216  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5217  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5218  $out .= "<br>\n";
5219  } else { // Mode CLI
5220  // No dol_escape_htmltag for output, we are in CLI mode
5221  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5222  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5223  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5224  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5225  }
5226  $syslog .= ", sql=".$db->lastquery();
5227  $syslog .= ", db_error=".$db->lasterror();
5228  }
5229 
5230  if ($error || $errors) {
5231  $langs->load("errors");
5232 
5233  // Merge all into $errors array
5234  if (is_array($error) && is_array($errors)) {
5235  $errors = array_merge($error, $errors);
5236  } elseif (is_array($error)) {
5237  $errors = $error;
5238  } elseif (is_array($errors)) {
5239  $errors = array_merge(array($error), $errors);
5240  } else {
5241  $errors = array_merge(array($error), array($errors));
5242  }
5243 
5244  foreach ($errors as $msg) {
5245  if (empty($msg)) {
5246  continue;
5247  }
5248  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5249  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5250  } else // Mode CLI
5251  {
5252  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5253  }
5254  $syslog .= ", msg=".$msg;
5255  }
5256  }
5257  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5258  xdebug_print_function_stack();
5259  $out .= '<b>XDebug informations:</b>'."<br>\n";
5260  $out .= 'File: '.xdebug_call_file()."<br>\n";
5261  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5262  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5263  $out .= "<br>\n";
5264  }
5265 
5266  // Return a http header with error code if possible
5267  if (!headers_sent()) {
5268  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5269  top_httphead();
5270  }
5271  //http_response_code(500); // If we use 500, message is not ouput with some command line tools
5272  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
5273  }
5274 
5275  if (empty($dolibarr_main_prod)) {
5276  print $out;
5277  } else {
5278  if (empty($langs->defaultlang)) {
5279  $langs->setDefaultLang();
5280  }
5281  $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.
5282  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5283  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";
5284  print $langs->trans("DolibarrHasDetectedError").'. ';
5285  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5286  if (!defined("MAIN_CORE_ERROR")) {
5287  define("MAIN_CORE_ERROR", 1);
5288  }
5289  }
5290 
5291  dol_syslog("Error ".$syslog, LOG_ERR);
5292 }
5293 
5304 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5305 {
5306  global $langs, $conf;
5307 
5308  if (empty($email)) {
5309  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
5310  }
5311 
5312  $langs->load("errors");
5313  $now = dol_now();
5314 
5315  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5316  print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
5317  if ($errormessage) {
5318  print '<br><br>'.$errormessage;
5319  }
5320  if (is_array($errormessages) && count($errormessages)) {
5321  foreach ($errormessages as $mesgtoshow) {
5322  print '<br><br>'.$mesgtoshow;
5323  }
5324  }
5325  print '</div></div>';
5326 }
5327 
5344 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5345 {
5346  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5347 }
5348 
5367 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5368 {
5369  global $conf, $langs, $form;
5370  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5371 
5372  if ($moreattrib == 'class="right"') {
5373  $prefix .= 'right '; // For backward compatibility
5374  }
5375 
5376  $sortorder = strtoupper($sortorder);
5377  $out = '';
5378  $sortimg = '';
5379 
5380  $tag = 'th';
5381  if ($thead == 2) {
5382  $tag = 'div';
5383  }
5384 
5385  $tmpsortfield = explode(',', $sortfield);
5386  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5387  $tmpfield = explode(',', $field);
5388  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5389 
5390  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5391  $prefix = 'wrapcolumntitle '.$prefix;
5392  }
5393 
5394  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5395  // If field is used as sort criteria we use a specific css class liste_titre_sel
5396  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5397  $liste_titre = 'liste_titre';
5398  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5399  $liste_titre = 'liste_titre_sel';
5400  }
5401 
5402  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5403  //$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)).'"' : '');
5404  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5405  $tagstart .= '>';
5406 
5407  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5408  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5409  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5410  $options = preg_replace('/&+/i', '&', $options);
5411  if (!preg_match('/^&/', $options)) {
5412  $options = '&'.$options;
5413  }
5414 
5415  $sortordertouseinlink = '';
5416  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5417  if (preg_match('/^DESC/i', $sortorder)) {
5418  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5419  } else { // We reverse the var $sortordertouseinlink
5420  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5421  }
5422  } else { // We are on field that is the first current sorting criteria
5423  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5424  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5425  } else {
5426  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5427  }
5428  }
5429  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5430  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5431  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5432  $out .= '>';
5433  }
5434  if ($tooltip) {
5435  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
5436  if (preg_match('/:\w+$/', $tooltip)) {
5437  $tmptooltip = explode(':', $tooltip);
5438  } else {
5439  $tmptooltip = array($tooltip);
5440  }
5441  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5442  } else {
5443  $out .= $langs->trans($name);
5444  }
5445 
5446  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5447  $out .= '</a>';
5448  }
5449 
5450  if (empty($thead) && $field) { // If this is a sort field
5451  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5452  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5453  $options = preg_replace('/&+/i', '&', $options);
5454  if (!preg_match('/^&/', $options)) {
5455  $options = '&'.$options;
5456  }
5457 
5458  if (!$sortorder || ($field1 != $sortfield1)) {
5459  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5460  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5461  } else {
5462  if (preg_match('/^DESC/', $sortorder)) {
5463  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5464  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5465  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5466  }
5467  if (preg_match('/^ASC/', $sortorder)) {
5468  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5469  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5470  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5471  }
5472  }
5473  }
5474 
5475  $tagend = '</'.$tag.'>';
5476 
5477  $out = $tagstart.$sortimg.$out.$tagend;
5478 
5479  return $out;
5480 }
5481 
5490 function print_titre($title)
5491 {
5492  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5493 
5494  print '<div class="titre">'.$title.'</div>';
5495 }
5496 
5508 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5509 {
5510  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5511 }
5512 
5526 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5527 {
5528  global $conf;
5529 
5530  $return = '';
5531 
5532  if ($picto == 'setup') {
5533  $picto = 'generic';
5534  }
5535 
5536  $return .= "\n";
5537  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5538  $return .= '<tr class="titre">';
5539  if ($picto) {
5540  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5541  }
5542  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5543  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5544  $return .= '</td>';
5545  if (dol_strlen($morehtmlcenter)) {
5546  $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5547  }
5548  if (dol_strlen($morehtmlright)) {
5549  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
5550  }
5551  $return .= '</tr></table>'."\n";
5552 
5553  return $return;
5554 }
5555 
5579 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 = '')
5580 {
5581  global $conf, $langs;
5582 
5583  $savlimit = $limit;
5584  $savtotalnboflines = $totalnboflines;
5585  $totalnboflines = abs((int) $totalnboflines);
5586 
5587  $page = (int) $page;
5588 
5589  if ($picto == 'setup') {
5590  $picto = 'title_setup.png';
5591  }
5592  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5593  $picto = 'title.gif';
5594  }
5595  if ($limit < 0) {
5596  $limit = $conf->liste_limit;
5597  }
5598 
5599  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5600  $nextpage = 1;
5601  } else {
5602  $nextpage = 0;
5603  }
5604  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
5605 
5606  print "\n";
5607  print "<!-- Begin title -->\n";
5608  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5609 
5610  // Left
5611 
5612  if ($picto && $titre) {
5613  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5614  }
5615 
5616  print '<td class="nobordernopadding valignmiddle col-title">';
5617  print '<div class="titre inline-block">'.$titre;
5618  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5619  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5620  }
5621  print '</div></td>';
5622 
5623  // Center
5624  if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
5625  print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5626  }
5627 
5628  // Right
5629  print '<td class="nobordernopadding valignmiddle right col-right">';
5630  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5631  if ($sortfield) {
5632  $options .= "&sortfield=".urlencode($sortfield);
5633  }
5634  if ($sortorder) {
5635  $options .= "&sortorder=".urlencode($sortorder);
5636  }
5637  // Show navigation bar
5638  $pagelist = '';
5639  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5640  if ($totalnboflines) { // If we know total nb of lines
5641  // Define nb of extra page links before and after selected page + ... + first or last
5642  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5643 
5644  if ($limit > 0) {
5645  $nbpages = ceil($totalnboflines / $limit);
5646  } else {
5647  $nbpages = 1;
5648  }
5649  $cpt = ($page - $maxnbofpage);
5650  if ($cpt < 0) {
5651  $cpt = 0;
5652  }
5653 
5654  if ($cpt >= 1) {
5655  if (empty($pagenavastextinput)) {
5656  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5657  if ($cpt > 2) {
5658  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5659  } elseif ($cpt == 2) {
5660  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5661  }
5662  }
5663  }
5664 
5665  do {
5666  if ($pagenavastextinput) {
5667  if ($cpt == $page) {
5668  $pagelist .= '<li class="pagination"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5669  $pagelist .= '/';
5670  }
5671  } else {
5672  if ($cpt == $page) {
5673  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5674  } else {
5675  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5676  }
5677  }
5678  $cpt++;
5679  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5680 
5681  if (empty($pagenavastextinput)) {
5682  if ($cpt < $nbpages) {
5683  if ($cpt < $nbpages - 2) {
5684  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5685  } elseif ($cpt == $nbpages - 2) {
5686  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5687  }
5688  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5689  }
5690  } else {
5691  //var_dump($page.' '.$cpt.' '.$nbpages);
5692  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5693  }
5694  } else {
5695  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5696  }
5697  }
5698 
5699  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5700  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
5701  }
5702 
5703  // js to autoselect page field on focus
5704  if ($pagenavastextinput) {
5705  print ajax_autoselect('.pageplusone');
5706  }
5707 
5708  print '</td>';
5709  print '</tr>';
5710 
5711  print '</table>'."\n";
5712 
5713  // Center
5714  if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
5715  print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
5716  }
5717 
5718  print "<!-- End title -->\n\n";
5719 }
5720 
5737 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
5738 {
5739  global $conf, $langs;
5740 
5741  print '<div class="pagination"><ul>';
5742  if ($beforearrows) {
5743  print '<li class="paginationbeforearrows">';
5744  print $beforearrows;
5745  print '</li>';
5746  }
5747 
5748  if (empty($hidenavigation)) {
5749  if ((int) $limit > 0 && empty($hideselectlimit)) {
5750  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5751  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5752  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5753  //$pagesizechoices.=',2:2';
5754  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5755  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5756  }
5757 
5758  print '<li class="pagination">';
5759  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5760  $tmpchoice = explode(',', $pagesizechoices);
5761  $tmpkey = $limit.':'.$limit;
5762  if (!in_array($tmpkey, $tmpchoice)) {
5763  $tmpchoice[] = $tmpkey;
5764  }
5765  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5766  if (!in_array($tmpkey, $tmpchoice)) {
5767  $tmpchoice[] = $tmpkey;
5768  }
5769  asort($tmpchoice, SORT_NUMERIC);
5770  foreach ($tmpchoice as $val) {
5771  $selected = '';
5772  $tmp = explode(':', $val);
5773  $key = $tmp[0];
5774  $val = $tmp[1];
5775  if ($key != '' && $val != '') {
5776  if ((int) $key == (int) $limit) {
5777  $selected = ' selected="selected"';
5778  }
5779  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5780  }
5781  }
5782  print '</select>';
5783  if ($conf->use_javascript_ajax) {
5784  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5785  <script>
5786  jQuery(document).ready(function () {
5787  jQuery(".selectlimit").change(function() {
5788  console.log("Change limit. Send submit");
5789  $(this).parents(\'form:first\').submit();
5790  });
5791  });
5792  </script>
5793  ';
5794  }
5795  print '</li>';
5796  }
5797  if ($page > 0) {
5798  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>';
5799  }
5800  if ($betweenarrows) {
5801  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5802  print $betweenarrows;
5803  print '<!--</div>-->';
5804  }
5805  if ($nextpage > 0) {
5806  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>';
5807  }
5808  if ($afterarrows) {
5809  print '<li class="paginationafterarrows">';
5810  print $afterarrows;
5811  print '</li>';
5812  }
5813  }
5814  print '</ul></div>'."\n";
5815 }
5816 
5817 
5829 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5830 {
5831  $morelabel = '';
5832 
5833  if (preg_match('/%/', $rate)) {
5834  $rate = str_replace('%', '', $rate);
5835  $addpercent = true;
5836  }
5837  $reg = array();
5838  if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
5839  $morelabel = ' ('.$reg[1].')';
5840  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5841  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5842  }
5843  if (preg_match('/\*/', $rate)) {
5844  $rate = str_replace('*', '', $rate);
5845  $info_bits |= 1;
5846  }
5847 
5848  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5849  if (!preg_match('/\//', $rate)) {
5850  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5851  } else {
5852  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5853  $ret = $rate.($addpercent ? '%' : '');
5854  }
5855  if (($info_bits & 1) && $usestarfornpr >= 0) {
5856  $ret .= ' *';
5857  }
5858  $ret .= $morelabel;
5859  return $ret;
5860 }
5861 
5862 
5878 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5879 {
5880  global $langs, $conf;
5881 
5882  // Clean parameters
5883  if (empty($amount)) {
5884  $amount = 0; // To have a numeric value if amount not defined or = ''
5885  }
5886  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5887  if ($rounding == -1) {
5888  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5889  }
5890  $nbdecimal = $rounding;
5891 
5892  if ($outlangs === 'none') {
5893  // Use international separators
5894  $dec = '.';
5895  $thousand = '';
5896  } else {
5897  // Output separators by default (french)
5898  $dec = ',';
5899  $thousand = ' ';
5900 
5901  // If $outlangs not forced, we use use language
5902  if (!is_object($outlangs)) {
5903  $outlangs = $langs;
5904  }
5905 
5906  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5907  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5908  }
5909  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5910  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5911  }
5912  if ($thousand == 'None') {
5913  $thousand = '';
5914  } elseif ($thousand == 'Space') {
5915  $thousand = ' ';
5916  }
5917  }
5918  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5919 
5920  //print "amount=".$amount."-";
5921  $amount = str_replace(',', '.', $amount); // should be useless
5922  //print $amount."-";
5923  $datas = explode('.', $amount);
5924  $decpart = isset($datas[1]) ? $datas[1] : '';
5925  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5926  //print "decpart=".$decpart."<br>";
5927  $end = '';
5928 
5929  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5930  if (dol_strlen($decpart) > $nbdecimal) {
5931  $nbdecimal = dol_strlen($decpart);
5932  }
5933  // Si on depasse max
5934  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5935  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5936  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5937  // Si un affichage est tronque, on montre des ...
5938  $end = '...';
5939  }
5940  }
5941 
5942  // If force rounding
5943  if ((string) $forcerounding != '-1') {
5944  if ($forcerounding === 'MU') {
5945  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5946  } elseif ($forcerounding === 'MT') {
5947  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_TOT;
5948  } elseif ($forcerounding >= 0) {
5949  $nbdecimal = $forcerounding;
5950  }
5951  }
5952 
5953  // Format number
5954  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5955  if ($form) {
5956  $output = preg_replace('/\s/', '&nbsp;', $output);
5957  $output = preg_replace('/\'/', '&#039;', $output);
5958  }
5959  // Add symbol of currency if requested
5960  $cursymbolbefore = $cursymbolafter = '';
5961  if ($currency_code && is_object($outlangs)) {
5962  if ($currency_code == 'auto') {
5963  $currency_code = $conf->currency;
5964  }
5965 
5966  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC');
5967  $listoflanguagesbefore = array('nl_NL');
5968  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5969  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5970  } else {
5971  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5972  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5973  }
5974  }
5975  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5976 
5977  return $output;
5978 }
5979 
6004 function price2num($amount, $rounding = '', $option = 0)
6005 {
6006  global $langs, $conf;
6007 
6008  // Clean parameters
6009  if (is_null($amount)) {
6010  $amount = '';
6011  }
6012 
6013  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6014  // Numbers must be '1234.56'
6015  // Decimal delimiter for PHP and database SQL requests must be '.'
6016  $dec = ',';
6017  $thousand = ' ';
6018  if (is_null($langs)) { // $langs is not defined, we use english values.
6019  $dec = '.';
6020  $thousand = ',';
6021  } else {
6022  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6023  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6024  }
6025  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6026  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6027  }
6028  }
6029  if ($thousand == 'None') {
6030  $thousand = '';
6031  } elseif ($thousand == 'Space') {
6032  $thousand = ' ';
6033  }
6034  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6035 
6036  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6037  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6038  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6039  if (!is_numeric($amount)) {
6040  $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6041  }
6042 
6043  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
6044  $amount = str_replace($thousand, '', $amount);
6045  }
6046 
6047  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6048  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6049  // So if number was already a good number, it is converted into local 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, $nbofdec, $dec, $thousand);
6056  }
6057  //print "QQ".$amount."<br>\n";
6058 
6059  // Now make replace (the main goal of function)
6060  if ($thousand != ',' && $thousand != '.') {
6061  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6062  }
6063 
6064  $amount = str_replace(' ', '', $amount); // To avoid spaces
6065  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6066  $amount = str_replace($dec, '.', $amount);
6067 
6068  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6069  }
6070  //print ' XX'.$amount.' '.$rounding;
6071 
6072  // Now, $amount is a real PHP float number. We make a rounding if required.
6073  if ($rounding) {
6074  $nbofdectoround = '';
6075  if ($rounding == 'MU') {
6076  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
6077  } elseif ($rounding == 'MT') {
6078  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
6079  } elseif ($rounding == 'MS') {
6080  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6081  } elseif ($rounding == 'CU') {
6082  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
6083  } elseif ($rounding == 'CT') {
6084  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
6085  } elseif (is_numeric($rounding)) {
6086  $nbofdectoround = (int) $rounding;
6087  }
6088 
6089  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6090  if (dol_strlen($nbofdectoround)) {
6091  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6092  } else {
6093  return 'ErrorBadParameterProvidedToFunction';
6094  }
6095  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6096 
6097  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6098  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6099  if (is_numeric($amount)) {
6100  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6101  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6102  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6103  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6104  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6105  }
6106  //print "TT".$amount.'<br>';
6107 
6108  // Always make replace because each math function (like round) replace
6109  // with local values and we want a number that has a SQL string format x.y
6110  if ($thousand != ',' && $thousand != '.') {
6111  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6112  }
6113 
6114  $amount = str_replace(' ', '', $amount); // To avoid spaces
6115  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6116  $amount = str_replace($dec, '.', $amount);
6117 
6118  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6119  }
6120 
6121  return $amount;
6122 }
6123 
6136 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6137 {
6138  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6139 
6140  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6141  $dimension = $dimension * 1000000;
6142  $unit = $unit - 6;
6143  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6144  $dimension = $dimension * 1000;
6145  $unit = $unit - 3;
6146  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6147  $dimension = $dimension / 1000000;
6148  $unit = $unit + 6;
6149  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6150  $dimension = $dimension / 1000;
6151  $unit = $unit + 3;
6152  }
6153  // Special case when we want output unit into pound or ounce
6154  /* TODO
6155  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6156  {
6157  $dimension = // convert dimension from standard unit into ounce or pound
6158  $unit = $forceunitoutput;
6159  }
6160  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6161  {
6162  $dimension = // convert dimension from standard unit into ounce or pound
6163  $unit = $forceunitoutput;
6164  }*/
6165 
6166  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6167  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6168 
6169  return $ret;
6170 }
6171 
6172 
6185 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
6186 {
6187  global $db, $conf, $mysoc;
6188 
6189  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6190  $thirdparty_seller = $mysoc;
6191  }
6192 
6193  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);
6194 
6195  $vatratecleaned = $vatrate;
6196  $reg = array();
6197  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6198  $vatratecleaned = trim($reg[1]);
6199  $vatratecode = $reg[2];
6200  }
6201 
6202  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6203  {
6204  return 0;
6205  }*/
6206 
6207  // Some test to guess with no need to make database access
6208  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6209  if ($local == 1) {
6210  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6211  return 0;
6212  }
6213  if ($thirdparty_seller->id == $mysoc->id) {
6214  if (!$thirdparty_buyer->localtax1_assuj) {
6215  return 0;
6216  }
6217  } else {
6218  if (!$thirdparty_seller->localtax1_assuj) {
6219  return 0;
6220  }
6221  }
6222  }
6223 
6224  if ($local == 2) {
6225  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6226  if (!$mysoc->localtax2_assuj) {
6227  return 0; // If main vat is 0, IRPF may be different than 0.
6228  }
6229  if ($thirdparty_seller->id == $mysoc->id) {
6230  if (!$thirdparty_buyer->localtax2_assuj) {
6231  return 0;
6232  }
6233  } else {
6234  if (!$thirdparty_seller->localtax2_assuj) {
6235  return 0;
6236  }
6237  }
6238  }
6239  } else {
6240  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6241  return 0;
6242  }
6243  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6244  return 0;
6245  }
6246  }
6247 
6248  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6249  if (in_array($mysoc->country_code, array('ES'))) {
6250  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6251  }
6252 
6253  // Search local taxes
6254  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
6255  if ($local == 1) {
6256  if ($thirdparty_seller != $mysoc) {
6257  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6258  return $thirdparty_seller->localtax1_value;
6259  }
6260  } else { // i am the seller
6261  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6262  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6263  }
6264  }
6265  }
6266  if ($local == 2) {
6267  if ($thirdparty_seller != $mysoc) {
6268  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6269  // TODO We should also return value defined on thirdparty only if defined
6270  return $thirdparty_seller->localtax2_value;
6271  }
6272  } else { // i am the seller
6273  if (in_array($mysoc->country_code, array('ES'))) {
6274  return $thirdparty_buyer->localtax2_value;
6275  } else {
6276  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6277  }
6278  }
6279  }
6280  }
6281 
6282  // By default, search value of local tax on line of common tax
6283  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6284  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6285  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6286  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6287  if (!empty($vatratecode)) {
6288  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6289  } else {
6290  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
6291  }
6292 
6293  $resql = $db->query($sql);
6294 
6295  if ($resql) {
6296  $obj = $db->fetch_object($resql);
6297  if ($obj) {
6298  if ($local == 1) {
6299  return $obj->localtax1;
6300  } elseif ($local == 2) {
6301  return $obj->localtax2;
6302  }
6303  }
6304  }
6305 
6306  return 0;
6307 }
6308 
6309 
6318 function isOnlyOneLocalTax($local)
6319 {
6320  $tax = get_localtax_by_third($local);
6321 
6322  $valors = explode(":", $tax);
6323 
6324  if (count($valors) > 1) {
6325  return false;
6326  } else {
6327  return true;
6328  }
6329 }
6330 
6337 function get_localtax_by_third($local)
6338 {
6339  global $db, $mysoc;
6340 
6341  $sql = " SELECT t.localtax".$local." as localtax";
6342  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
6343  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = (";
6344  $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";
6345  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)";
6346  $sql .= " AND t.localtax".$local."_type <> '0'";
6347  $sql .= " ORDER BY t.rowid DESC";
6348 
6349  $resql = $db->query($sql);
6350  if ($resql) {
6351  $obj = $db->fetch_object($resql);
6352  if ($obj) {
6353  return $obj->localtax;
6354  } else {
6355  return '0';
6356  }
6357  }
6358 
6359  return 'Error';
6360 }
6361 
6362 
6374 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6375 {
6376  global $db, $mysoc;
6377 
6378  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6379 
6380  // Search local taxes
6381  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6382  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6383  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6384  if ($firstparamisid) {
6385  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6386  } else {
6387  $vatratecleaned = $vatrate;
6388  $vatratecode = '';
6389  $reg = array();
6390  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6391  $vatratecleaned = $reg[1];
6392  $vatratecode = $reg[2];
6393  }
6394 
6395  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6396  /*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 ??
6397  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6398  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6399  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6400  if ($vatratecode) {
6401  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6402  }
6403  }
6404 
6405  $resql = $db->query($sql);
6406  if ($resql) {
6407  $obj = $db->fetch_object($resql);
6408  if ($obj) {
6409  return array(
6410  'rowid'=>$obj->rowid,
6411  'code'=>$obj->code,
6412  'rate'=>$obj->rate,
6413  'localtax1'=>$obj->localtax1,
6414  'localtax1_type'=>$obj->localtax1_type,
6415  'localtax2'=>$obj->localtax2,
6416  'localtax2_type'=>$obj->localtax2_type,
6417  'npr'=>$obj->npr,
6418  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6419  'accountancy_code_buy'=>$obj->accountancy_code_buy
6420  );
6421  } else {
6422  return array();
6423  }
6424  } else {
6425  dol_print_error($db);
6426  }
6427 
6428  return array();
6429 }
6430 
6447 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6448 {
6449  global $db, $mysoc;
6450 
6451  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6452 
6453  // Search local taxes
6454  $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";
6455  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6456  if ($firstparamisid) {
6457  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6458  } else {
6459  $vatratecleaned = $vatrate;
6460  $vatratecode = '';
6461  $reg = array();
6462  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6463  $vatratecleaned = $reg[1];
6464  $vatratecode = $reg[2];
6465  }
6466 
6467  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6468  if (!empty($mysoc) && $mysoc->country_code == 'ES') {
6469  $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
6470  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
6471  } else {
6472  $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
6473  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
6474  }
6475  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6476  if ($vatratecode) {
6477  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6478  }
6479  }
6480 
6481  $resql = $db->query($sql);
6482  if ($resql) {
6483  $obj = $db->fetch_object($resql);
6484 
6485  if ($obj) {
6486  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6487 
6488  if ($local == 1) {
6489  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6490  } elseif ($local == 2) {
6491  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6492  } else {
6493  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);
6494  }
6495  }
6496  }
6497 
6498  return array();
6499 }
6500 
6511 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6512 {
6513  global $db, $conf, $mysoc;
6514 
6515  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6516 
6517  $ret = 0;
6518  $found = 0;
6519 
6520  if ($idprod > 0) {
6521  // Load product
6522  $product = new Product($db);
6523  $product->fetch($idprod);
6524 
6525  if ($mysoc->country_code == $thirdpartytouse->country_code) {
6526  // If country to consider is ours
6527  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6528  $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
6529  if ($result > 0) {
6530  $ret = $product->vatrate_supplier;
6531  if ($product->default_vat_code_supplier) {
6532  $ret .= ' ('.$product->default_vat_code_supplier.')';
6533  }
6534  $found = 1;
6535  }
6536  }
6537  if (!$found) {
6538  $ret = $product->tva_tx; // Default sales vat of product
6539  if ($product->default_vat_code) {
6540  $ret .= ' ('.$product->default_vat_code.')';
6541  }
6542  $found = 1;
6543  }
6544  } else {
6545  // TODO Read default product vat according to product and another countrycode.
6546  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6547  }
6548  }
6549 
6550  if (!$found) {
6551  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6552  // 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).
6553  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6554  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6555  $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
6556  $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
6557  $sql .= $db->plimit(1);
6558 
6559  $resql = $db->query($sql);
6560  if ($resql) {
6561  $obj = $db->fetch_object($resql);
6562  if ($obj) {
6563  $ret = $obj->vat_rate;
6564  if ($obj->default_vat_code) {
6565  $ret .= ' ('.$obj->default_vat_code.')';
6566  }
6567  }
6568  $db->free($resql);
6569  } else {
6570  dol_print_error($db);
6571  }
6572  } else {
6573  // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
6574  // '1.23'
6575  // or '1.23 (CODE)'
6576  $defaulttx = '';
6577  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6578  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6579  }
6580  /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6581  $defaultcode = $reg[1];
6582  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6583  }*/
6584 
6585  $ret = $defaulttx;
6586  }
6587  }
6588 
6589  dol_syslog("get_product_vat_for_country: ret=".$ret);
6590  return $ret;
6591 }
6592 
6602 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6603 {
6604  global $db, $mysoc;
6605 
6606  if (!class_exists('Product')) {
6607  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6608  }
6609 
6610  $ret = 0;
6611  $found = 0;
6612 
6613  if ($idprod > 0) {
6614  // Load product
6615  $product = new Product($db);
6616  $result = $product->fetch($idprod);
6617 
6618  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6619  /* Not defined yet, so we don't use this
6620  if ($local==1) $ret=$product->localtax1_tx;
6621  elseif ($local==2) $ret=$product->localtax2_tx;
6622  $found=1;
6623  */
6624  } else {
6625  // TODO Read default product vat according to product and another countrycode.
6626  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6627  }
6628  }
6629 
6630  if (!$found) {
6631  // If vat of product for the country not found or not defined, we return higher vat of country.
6632  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6633  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6634  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6635  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6636  $sql .= $db->plimit(1);
6637 
6638  $resql = $db->query($sql);
6639  if ($resql) {
6640  $obj = $db->fetch_object($resql);
6641  if ($obj) {
6642  if ($local == 1) {
6643  $ret = $obj->localtax1;
6644  } elseif ($local == 2) {
6645  $ret = $obj->localtax2;
6646  }
6647  }
6648  } else {
6649  dol_print_error($db);
6650  }
6651  }
6652 
6653  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6654  return $ret;
6655 }
6656 
6673 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6674 {
6675  global $conf;
6676 
6677  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6678 
6679  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6680  $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;
6681 
6682  $seller_country_code = $thirdparty_seller->country_code;
6683  $seller_in_cee = isInEEC($thirdparty_seller);
6684 
6685  $buyer_country_code = $thirdparty_buyer->country_code;
6686  $buyer_in_cee = isInEEC($thirdparty_buyer);
6687 
6688  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 : ''));
6689 
6690  // 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)
6691  // we use the buyer VAT.
6692  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
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 0';
6704  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6705  }
6706  }
6707  }
6708 
6709  // If seller does not use VAT, default VAT is 0. End of rule.
6710  if (!$seller_use_vat) {
6711  //print 'VATRULE 1';
6712  return 0;
6713  }
6714 
6715  // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
6716  if (($seller_country_code == $buyer_country_code)
6717  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6718  //print 'VATRULE 2';
6719  $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6720 
6721  if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
6722  // Special case for india.
6723  //print 'VATRULE 2b';
6724  $reg = array();
6725  if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
6726  // we must revert the C+S into I
6727  $tmpvat = str_replace("C+S", "I", $tmpvat);
6728  } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
6729  // we must revert the I into C+S
6730  $tmpvat = str_replace("I", "C+S", $tmpvat);
6731  }
6732  }
6733 
6734  return $tmpvat;
6735  }
6736 
6737  // 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.
6738  // 'VATRULE 3' - Not supported
6739 
6740  // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
6741  // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
6742  if (($seller_in_cee && $buyer_in_cee)) {
6743  $isacompany = $thirdparty_buyer->isACompany();
6744  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6745  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6746  if (!isValidVATID($thirdparty_buyer)) {
6747  $isacompany = 0;
6748  }
6749  }
6750 
6751  if (!$isacompany) {
6752  //print 'VATRULE 4';
6753  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6754  } else {
6755  //print 'VATRULE 5';
6756  return 0;
6757  }
6758  }
6759 
6760  // 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
6761  // I don't see any use case that need this rule.
6762  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6763  $isacompany = $thirdparty_buyer->isACompany();
6764  if (!$isacompany) {
6765  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6766  //print 'VATRULE extra';
6767  }
6768  }
6769 
6770  // Otherwise the VAT proposed by default=0. End of rule.
6771  // Rem: This means that at least one of the 2 is outside the European Community and the country differs
6772  //print 'VATRULE 6';
6773  return 0;
6774 }
6775 
6776 
6787 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6788 {
6789  global $db;
6790 
6791  if ($idprodfournprice > 0) {
6792  if (!class_exists('ProductFournisseur')) {
6793  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6794  }
6795  $prodprice = new ProductFournisseur($db);
6796  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6797  return $prodprice->fourn_tva_npr;
6798  } elseif ($idprod > 0) {
6799  if (!class_exists('Product')) {
6800  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6801  }
6802  $prod = new Product($db);
6803  $prod->fetch($idprod);
6804  return $prod->tva_npr;
6805  }
6806 
6807  return 0;
6808 }
6809 
6823 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6824 {
6825  global $mysoc;
6826 
6827  if (!is_object($thirdparty_seller)) {
6828  return -1;
6829  }
6830  if (!is_object($thirdparty_buyer)) {
6831  return -1;
6832  }
6833 
6834  if ($local == 1) { // Localtax 1
6835  if ($mysoc->country_code == 'ES') {
6836  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6837  return 0;
6838  }
6839  } else {
6840  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6841  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6842  return 0;
6843  }
6844  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6845  return 0;
6846  }
6847  }
6848  } elseif ($local == 2) { //I Localtax 2
6849  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6850  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6851  return 0;
6852  }
6853  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6854  return 0;
6855  }
6856  }
6857 
6858  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6859  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6860  }
6861 
6862  return 0;
6863 }
6864 
6873 function yn($yesno, $case = 1, $color = 0)
6874 {
6875  global $langs;
6876 
6877  $result = 'unknown';
6878  $classname = '';
6879  if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // A mettre avant test sur no a cause du == 0
6880  $result = $langs->trans('yes');
6881  if ($case == 1 || $case == 3) {
6882  $result = $langs->trans("Yes");
6883  }
6884  if ($case == 2) {
6885  $result = '<input type="checkbox" value="1" checked disabled>';
6886  }
6887  if ($case == 3) {
6888  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6889  }
6890 
6891  $classname = 'ok';
6892  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6893  $result = $langs->trans("no");
6894  if ($case == 1 || $case == 3) {
6895  $result = $langs->trans("No");
6896  }
6897  if ($case == 2) {
6898  $result = '<input type="checkbox" value="0" disabled>';
6899  }
6900  if ($case == 3) {
6901  $result = '<input type="checkbox" value="0" disabled> '.$result;
6902  }
6903 
6904  if ($color == 2) {
6905  $classname = 'ok';
6906  } else {
6907  $classname = 'error';
6908  }
6909  }
6910  if ($color) {
6911  return '<span class="'.$classname.'">'.$result.'</span>';
6912  }
6913  return $result;
6914 }
6915 
6931 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6932 {
6933  global $conf;
6934 
6935  if (empty($modulepart) && !empty($object->module)) {
6936  $modulepart = $object->module;
6937  }
6938 
6939  $path = '';
6940 
6941  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6942  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
6943  $arrayforoldpath[] = 'product';
6944  }
6945  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6946  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6947  if (empty($alpha)) {
6948  $num = preg_replace('/([^0-9])/i', '', $num);
6949  } else {
6950  $num = preg_replace('/^.*\-/i', '', $num);
6951  }
6952  $num = substr("000".$num, -$level);
6953  if ($level == 1) {
6954  $path = substr($num, 0, 1);
6955  }
6956  if ($level == 2) {
6957  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6958  }
6959  if ($level == 3) {
6960  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6961  }
6962  } else {
6963  // We will enhance here a common way of forging path for document storage.
6964  // In a future, we may distribute directories on several levels depending on setup and object.
6965  // Here, $object->id, $object->ref and $modulepart are required.
6966  //var_dump($modulepart);
6967  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6968  }
6969 
6970  if (empty($withoutslash) && !empty($path)) {
6971  $path .= '/';
6972  }
6973 
6974  return $path;
6975 }
6976 
6985 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6986 {
6987  global $conf;
6988 
6989  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6990 
6991  $dir_osencoded = dol_osencode($dir);
6992  if (@is_dir($dir_osencoded)) {
6993  return 0;
6994  }
6995 
6996  $nberr = 0;
6997  $nbcreated = 0;
6998 
6999  $ccdir = '';
7000  if (!empty($dataroot)) {
7001  // Remove data root from loop
7002  $dir = str_replace($dataroot.'/', '', $dir);
7003  $ccdir = $dataroot.'/';
7004  }
7005 
7006  $cdir = explode("/", $dir);
7007  $num = count($cdir);
7008  for ($i = 0; $i < $num; $i++) {
7009  if ($i > 0) {
7010  $ccdir .= '/'.$cdir[$i];
7011  } else {
7012  $ccdir .= $cdir[$i];
7013  }
7014  $regs = array();
7015  if (preg_match("/^.:$/", $ccdir, $regs)) {
7016  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
7017  }
7018 
7019  // Attention, le is_dir() peut echouer bien que le rep existe.
7020  // (ex selon config de open_basedir)
7021  if ($ccdir) {
7022  $ccdir_osencoded = dol_osencode($ccdir);
7023  if (!@is_dir($ccdir_osencoded)) {
7024  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7025 
7026  umask(0);
7027  $dirmaskdec = octdec((string) $newmask);
7028  if (empty($newmask)) {
7029  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7030  }
7031  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7032  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7033  // Si le is_dir a renvoye une fausse info, alors on passe ici.
7034  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7035  $nberr++;
7036  } else {
7037  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7038  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
7039  $nbcreated++;
7040  }
7041  } else {
7042  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
7043  }
7044  }
7045  }
7046  return ($nberr ? -$nberr : $nbcreated);
7047 }
7048 
7049 
7057 function dolChmod($filepath, $newmask = '')
7058 {
7059  global $conf;
7060 
7061  if (!empty($newmask)) {
7062  @chmod($filepath, octdec($newmask));
7063  } elseif (!empty($conf->global->MAIN_UMASK)) {
7064  @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7065  }
7066 }
7067 
7068 
7074 function picto_required()
7075 {
7076  return '<span class="fieldrequired">*</span>';
7077 }
7078 
7079 
7096 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
7097 {
7098  if (is_null($stringtoclean)) {
7099  return '';
7100  }
7101 
7102  if ($removelinefeed == 2) {
7103  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
7104  }
7105  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
7106 
7107  // 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)
7108  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7109 
7110  $temp = str_replace('< ', '__ltspace__', $temp);
7111 
7112  if ($strip_tags) {
7113  $temp = strip_tags($temp);
7114  } else {
7115  // 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).
7116  $pattern = "/<[^<>]+>/";
7117  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
7118  // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
7119  // pass 2 - $temp after pass 2: 0000-021
7120  $tempbis = $temp;
7121  do {
7122  $temp = $tempbis;
7123  $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
7124  $tempbis = preg_replace($pattern, '', $tempbis);
7125  //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
7126  } while ($tempbis != $temp);
7127 
7128  $temp = $tempbis;
7129 
7130  // 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).
7131  $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
7132  }
7133 
7134  $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
7135 
7136  // Remove also carriage returns
7137  if ($removelinefeed == 1) {
7138  $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
7139  }
7140 
7141  // And double quotes
7142  if ($removedoublespaces) {
7143  while (strpos($temp, " ")) {
7144  $temp = str_replace(" ", " ", $temp);
7145  }
7146  }
7147 
7148  $temp = str_replace('__ltspace__', '< ', $temp);
7149 
7150  return trim($temp);
7151 }
7152 
7168 function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0)
7169 {
7170  if (empty($allowed_tags)) {
7171  $allowed_tags = array(
7172  "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
7173  "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"
7174  );
7175  }
7176  $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
7177  if ($allowiframe) {
7178  if (!in_array('iframe', $allowed_tags)) {
7179  $allowed_tags[] = "iframe";
7180  }
7181  }
7182  if ($allowlink) {
7183  if (!in_array('link', $allowed_tags)) {
7184  $allowed_tags[] = "link";
7185  }
7186  }
7187 
7188  $allowed_tags_string = join("><", $allowed_tags);
7189  $allowed_tags_string = '<'.$allowed_tags_string.'>';
7190 
7191  $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
7192 
7193  $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
7194 
7195  //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
7196  $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
7197 
7198  $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
7199  $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
7200 
7201  $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
7202 
7203  if ($cleanalsosomestyles) { // Clean for remaining html tags
7204  $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
7205  }
7206  if ($removeclassattribute) { // Clean for remaining html tags
7207  $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
7208  }
7209 
7210  // Remove 'javascript:' that we should not find into a text with
7211  // 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)).
7212  if ($cleanalsojavascript) {
7213  $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);
7214  }
7215 
7216  $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
7217 
7218  $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
7219 
7220 
7221  return $temp;
7222 }
7223 
7224 
7236 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"))
7237 {
7238  if (class_exists('DOMDocument') && !empty($stringtoclean)) {
7239  $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
7240 
7241  $dom = new DOMDocument(null, 'UTF-8');
7242  $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
7243 
7244  if (is_object($dom)) {
7245  for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
7246  for ($attrs = $els->item($i)->attributes, $ii = $attrs->length - 1; $ii >= 0; $ii--) {
7247  //var_dump($attrs->item($ii));
7248  if (!empty($attrs->item($ii)->name)) {
7249  if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
7250  // Delete attribute if not into allowed_attributes
7251  $els->item($i)->removeAttribute($attrs->item($ii)->name);
7252  } elseif (in_array($attrs->item($ii)->name, array('style'))) {
7253  // If attribute is 'style'
7254  $valuetoclean = $attrs->item($ii)->value;
7255 
7256  if (isset($valuetoclean)) {
7257  do {
7258  $oldvaluetoclean = $valuetoclean;
7259  $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
7260  $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
7261  if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
7262  $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
7263  $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
7264  $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
7265  }
7266 
7267  // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
7268  $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
7269  $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
7270  } while ($oldvaluetoclean != $valuetoclean);
7271  }
7272 
7273  $attrs->item($ii)->value = $valuetoclean;
7274  }
7275  }
7276  }
7277  }
7278  }
7279 
7280  $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
7281  //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
7282 
7283  $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
7284  $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
7285  $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
7286  return trim($return);
7287  } else {
7288  return $stringtoclean;
7289  }
7290 }
7291 
7303 function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
7304 {
7305  $temp = $stringtoclean;
7306  foreach ($disallowed_tags as $tagtoremove) {
7307  $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
7308  $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
7309  }
7310 
7311  if ($cleanalsosomestyles) {
7312  $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
7313  }
7314 
7315  return $temp;
7316 }
7317 
7318 
7328 function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
7329 {
7330  if ($nboflines == 1) {
7331  if (dol_textishtml($text)) {
7332  $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
7333  $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
7334  } else {
7335  if (isset($text)) {
7336  $firstline = preg_replace('/[\n\r].*/', '', $text);
7337  } else {
7338  $firstline = '';
7339  }
7340  }
7341  return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
7342  } else {
7343  $ishtml = 0;
7344  if (dol_textishtml($text)) {
7345  $text = preg_replace('/\n/', '', $text);
7346  $ishtml = 1;
7347  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7348  } else {
7349  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7350  }
7351 
7352  $text = strtr($text, $repTable);
7353  if ($charset == 'UTF-8') {
7354  $pattern = '/(<br[^>]*>)/Uu';
7355  } else {
7356  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7357  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7358  }
7359  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7360 
7361  $firstline = '';
7362  $i = 0;
7363  $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
7364  while (($i < $nba) && ($i < ($nboflines * 2))) {
7365  if ($i % 2 == 0) {
7366  $firstline .= $a[$i];
7367  } elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) {
7368  $firstline .= ($ishtml ? "<br>\n" : "\n");
7369  }
7370  $i++;
7371  }
7372  unset($a);
7373  return $firstline.(($i < $nba) ? '...' : '');
7374  }
7375 }
7376 
7377 
7389 function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
7390 {
7391  if (is_null($stringtoencode)) {
7392  return '';
7393  }
7394 
7395  if (!$nl2brmode) {
7396  return nl2br($stringtoencode, $forxml);
7397  } else {
7398  $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
7399  return $ret;
7400  }
7401 }
7402 
7411 function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
7412 {
7413  global $conf;
7414 
7415  if (empty($nouseofiframesandbox) && !empty($conf->global->MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS)) {
7416  // TODO using sandbox on inline html content is not possible yet with current browsers
7417  //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
7418  //$s .= $stringtoencode;
7419  //$s .= '</body></html></iframe>';
7420  return $stringtoencode;
7421  } else {
7422  $out = $stringtoencode;
7423 
7424  do {
7425  $oldstringtoclean = $out;
7426 
7427  if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') {
7428  try {
7429  libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
7430 
7431  $dom = new DOMDocument;
7432  // Add a trick to solve pb with text without parent tag
7433  // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
7434  // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
7435  $out = '<div class="tricktoremove">'.$out.'</div>';
7436  $dom->loadHTML($out, LIBXML_HTML_NODEFDTD|LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
7437  $out = trim($dom->saveHTML());
7438 
7439  // Remove the trick added to solve pb with text without parent tag
7440  $out = preg_replace('/^<div class="tricktoremove">/', '', $out);
7441  $out = preg_replace('/<\/div>$/', '', $out);
7442  } catch (Exception $e) {
7443  // If error, invalid HTML string with no way to clean it
7444  //print $e->getMessage();
7445  $out = 'InvalidHTMLStringCantBeCleaned';
7446  }
7447  }
7448 
7449  // Clean some html entities that are useless so text is cleaner
7450  $out = preg_replace('/&(tab|newline);/i', ' ', $out);
7451 
7452  // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are
7453  // encoded using text entities) so we can then exclude all numeric entities.
7454  $out = preg_replace('/&#39;/i', '&apos;', $out);
7455 
7456  // 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).
7457  // 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
7458  // using a non coventionnel way to be encoded, to not have them sanitized just after)
7459  $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', function ($m) {
7460  return realCharForNumericEntities($m); }, $out);
7461 
7462 
7463  // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
7464  $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'.
7465 
7466  // Keep only some html tags and remove also some 'javascript:' strings
7467  $out = dol_string_onlythesehtmltags($out, 0, ($check == 'restricthtmlallowclass' ? 0 : 1), 1);
7468 
7469  // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
7470  if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) {
7472  }
7473 
7474  // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
7475  $out = preg_replace('/&apos;/i', "&#39;", $out);
7476  } while ($oldstringtoclean != $out);
7477 
7478  // Check the limit of external links that are automatically executed in a Rich text content. We count:
7479  // '<img' to avoid <img src="http...">
7480  // 'url(' to avoid inline style like background: url(http...
7481  // '<link' to avoid <link href="http...">
7482  $reg = array();
7483  preg_match_all('/(<img|url\‍(|<link)/i', $out, $reg);
7484  $nbextlink = count($reg[0]);
7485  if ($nbextlink > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
7486  $out = 'TooManyLinksIntoHTMLString';
7487  }
7488  //
7489  if (!empty($conf->global->MAIN_DISALLOW_EXT_URL_INTO_DESCRIPTIONS) || $check == 'restricthtmlnolink') {
7490  if ($nbextlink > 0) {
7491  $out = 'ExternalLinksNotAllowed';
7492  }
7493  }
7494 
7495  return $out;
7496  }
7497 }
7498 
7520 function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
7521 {
7522  if (is_null($stringtoencode)) {
7523  return '';
7524  }
7525 
7526  $newstring = $stringtoencode;
7527  if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
7528  $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.
7529  if ($removelasteolbr) {
7530  $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
7531  }
7532  $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__'));
7533  $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
7534  $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"'));
7535  } else {
7536  if ($removelasteolbr) {
7537  $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
7538  }
7539  $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
7540  }
7541  // Other substitutions that htmlentities does not do
7542  //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
7543  return $newstring;
7544 }
7545 
7553 function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
7554 {
7555  $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7556  $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
7557  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
7558  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
7559  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
7560  return $ret;
7561 }
7562 
7569 function dol_htmlcleanlastbr($stringtodecode)
7570 {
7571  $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
7572  $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
7573  return $ret;
7574 }
7575 
7585 function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
7586 {
7587  $newstring = $a;
7588  if ($keepsomeentities) {
7589  $newstring = strtr($newstring, array('&amp;'=>'__andamp__', '&lt;'=>'__andlt__', '&gt;'=>'__andgt__', '"'=>'__dquot__'));
7590  }
7591  $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
7592  if ($keepsomeentities) {
7593  $newstring = strtr($newstring, array('__andamp__'=>'&amp;', '__andlt__'=>'&lt;', '__andgt__'=>'&gt;', '__dquot__'=>'"'));
7594  }
7595  return $newstring;
7596 }
7597 
7608 function dol_htmlentities($string, $flags = ENT_QUOTES|ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
7609 {
7610  return htmlentities($string, $flags, $encoding, $double_encode);
7611 }
7612 
7624 function dol_string_is_good_iso($s, $clean = 0)
7625 {
7626  $len = dol_strlen($s);
7627  $out = '';
7628  $ok = 1;
7629  for ($scursor = 0; $scursor < $len; $scursor++) {
7630  $ordchar = ord($s[$scursor]);
7631  //print $scursor.'-'.$ordchar.'<br>';
7632  if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
7633  $ok = 0;
7634  break;
7635  } elseif ($ordchar > 126 && $ordchar < 160) {
7636  $ok = 0;
7637  break;
7638  } elseif ($clean) {
7639  $out .= $s[$scursor];
7640  }
7641  }
7642  if ($clean) {
7643  return $out;
7644  }
7645  return $ok;
7646 }
7647 
7656 function dol_nboflines($s, $maxchar = 0)
7657 {
7658  if ($s == '') {
7659  return 0;
7660  }
7661  $arraystring = explode("\n", $s);
7662  $nb = count($arraystring);
7663 
7664  return $nb;
7665 }
7666 
7667 
7677 function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
7678 {
7679  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7680  if (dol_textishtml($text)) {
7681  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7682  }
7683 
7684  $text = strtr($text, $repTable);
7685  if ($charset == 'UTF-8') {
7686  $pattern = '/(<br[^>]*>)/Uu';
7687  } else {
7688  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7689  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7690  }
7691  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7692 
7693  $nblines = (int) floor((count($a) + 1) / 2);
7694  // count possible auto line breaks
7695  if ($maxlinesize) {
7696  foreach ($a as $line) {
7697  if (dol_strlen($line) > $maxlinesize) {
7698  //$line_dec = html_entity_decode(strip_tags($line));
7699  $line_dec = html_entity_decode($line);
7700  if (dol_strlen($line_dec) > $maxlinesize) {
7701  $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
7702  $nblines += substr_count($line_dec, '\n');
7703  }
7704  }
7705  }
7706  }
7707 
7708  unset($a);
7709  return $nblines;
7710 }
7711 
7720 function dol_textishtml($msg, $option = 0)
7721 {
7722  if (is_null($msg)) {
7723  return false;
7724  }
7725 
7726  if ($option == 1) {
7727  if (preg_match('/<html/i', $msg)) {
7728  return true;
7729  } elseif (preg_match('/<body/i', $msg)) {
7730  return true;
7731  } elseif (preg_match('/<\/textarea/i', $msg)) {
7732  return true;
7733  } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
7734  return true;
7735  } elseif (preg_match('/<br/i', $msg)) {
7736  return true;
7737  }
7738  return false;
7739  } else {
7740  // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
7741  $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
7742  if (preg_match('/<html/i', $msg)) {
7743  return true;
7744  } elseif (preg_match('/<body/i', $msg)) {
7745  return true;
7746  } elseif (preg_match('/<\/textarea/i', $msg)) {
7747  return true;
7748  } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
7749  return true;
7750  } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
7751  return true;
7752  } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
7753  return true;
7754  } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
7755  return true;
7756  } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
7757  return true; // must accept <img src="http://example.com/aaa.png" />
7758  } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
7759  return true; // must accept <a href="http://example.com/aaa.png" />
7760  } elseif (preg_match('/<h[0-9]>/i', $msg)) {
7761  return true;
7762  } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
7763  // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
7764  return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
7765  } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
7766  return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
7767  }
7768 
7769  return false;
7770  }
7771 }
7772 
7787 function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
7788 {
7789  if (!empty($invert)) {
7790  $tmp = $text1;
7791  $text1 = $text2;
7792  $text2 = $tmp;
7793  }
7794 
7795  $ret = '';
7796  $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
7797  $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
7798  $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
7799  return $ret;
7800 }
7801 
7802 
7803 
7815 function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
7816 {
7817  global $db, $conf, $mysoc, $user, $extrafields;
7818 
7819  $substitutionarray = array();
7820 
7821  if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
7822  // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
7823  // this will include signature content first and then replace var found into content of signature
7824  //var_dump($onlykey);
7825  $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()
7826  $usersignature = $user->signature;
7827  $substitutionarray = array_merge($substitutionarray, array(
7828  '__SENDEREMAIL_SIGNATURE__' => (string) ((empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
7829  '__USER_SIGNATURE__' => (string) (($usersignature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
7830  ));
7831 
7832  if (is_object($user)) {
7833  $substitutionarray = array_merge($substitutionarray, array(
7834  '__USER_ID__' => (string) $user->id,
7835  '__USER_LOGIN__' => (string) $user->login,
7836  '__USER_EMAIL__' => (string) $user->email,
7837  '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
7838  '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
7839  '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
7840  '__USER_FAX__' => (string) $user->office_fax,
7841  '__USER_LASTNAME__' => (string) $user->lastname,
7842  '__USER_FIRSTNAME__' => (string) $user->firstname,
7843  '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
7844  '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
7845  '__USER_JOB__' => (string) $user->job,
7846  '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
7847  '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
7848  ));
7849  }
7850  }
7851  if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
7852  $substitutionarray = array_merge($substitutionarray, array(
7853  '__MYCOMPANY_NAME__' => $mysoc->name,
7854  '__MYCOMPANY_EMAIL__' => $mysoc->email,
7855  '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
7856  '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
7857  '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
7858  '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
7859  '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
7860  '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
7861  '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
7862  '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
7863  '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
7864  '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
7865  '__MYCOMPANY_ADDRESS__' => $mysoc->address,
7866  '__MYCOMPANY_ZIP__' => $mysoc->zip,
7867  '__MYCOMPANY_TOWN__' => $mysoc->town,
7868  '__MYCOMPANY_COUNTRY__' => $mysoc->country,
7869  '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
7870  '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
7871  '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
7872  ));
7873  }
7874 
7875  if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
7876  if ($onlykey) {
7877  $substitutionarray['__ID__'] = '__ID__';
7878  $substitutionarray['__REF__'] = '__REF__';
7879  $substitutionarray['__NEWREF__'] = '__NEWREF__';
7880  $substitutionarray['__LABEL__'] = '__LABEL__';
7881  $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
7882  $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
7883  $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
7884  $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
7885  $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
7886 
7887  if (isModEnabled("societe")) { // Most objects are concerned
7888  $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
7889  $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
7890  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
7891  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
7892  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
7893  $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
7894  $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
7895  $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
7896  $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
7897  $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
7898  $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
7899  $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
7900  $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
7901  $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
7902  $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
7903  $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
7904  $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
7905  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
7906  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
7907  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
7908  }
7909  if (isModEnabled('adherent') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
7910  $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
7911  $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
7912  $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
7913  $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
7914  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
7915  /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
7916  $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
7917  }
7918  // add variables subtitutions ticket
7919  if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
7920  $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
7921  $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
7922  $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
7923  $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
7924  $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
7925  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
7926  $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
7927  $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
7928  $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
7929  }
7930 
7931  if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
7932  $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
7933  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
7934  $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
7935  }
7936  if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
7937  $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
7938  $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
7939  $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
7940  /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
7941  $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
7942  }
7943  if (isModEnabled('contrat') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
7944  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
7945  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
7946  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
7947  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
7948  }
7949  if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
7950  $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
7951  }
7952  if (isModEnabled("ficheinter") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
7953  $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
7954  }
7955  $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
7956  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
7957  $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
7958  $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
7959  $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
7960  $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
7961  $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
7962 
7963  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
7964  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
7965  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
7966  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
7967  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
7968 
7969  if (isModEnabled("expedition") && (!is_object($object) || $object->element == 'shipping')) {
7970  $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
7971  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
7972  }
7973  if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
7974  $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment';
7975  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
7976  }
7977  } else {
7978  $substitutionarray['__ID__'] = $object->id;
7979  $substitutionarray['__REF__'] = $object->ref;
7980  $substitutionarray['__NEWREF__'] = $object->newref;
7981  $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
7982  $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7983  $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7984  $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
7985  $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
7986  if ($object->element == "shipping") {
7987  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', 0, $outputlangs) : '');
7988  } else {
7989  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7990  }
7991  $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%d") : '');
7992  $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%A") : '');
7993  $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%m") : '');
7994  $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%b") : '');
7995  $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%Y") : '');
7996  $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%H") : '');
7997  $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%M") : '');
7998  $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%S") : '');
7999 
8000  // For backward compatibility (deprecated)
8001  $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8002  $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8003  $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
8004  $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 : '')) : '');
8005 
8006  if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
8007  $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
8008 
8009  $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
8010  if (method_exists($object, 'getCivilityLabel')) {
8011  $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
8012  }
8013  $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
8014  $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
8015  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
8016  if (method_exists($object, 'getFullName')) {
8017  $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
8018  }
8019  $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
8020  $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
8021  $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
8022  $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
8023  $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
8024  $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
8025  $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
8026  $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
8027  $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
8028  $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
8029  $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
8030  $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
8031  $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
8032  $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
8033  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
8034  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
8035  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
8036  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
8037  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
8038  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
8039  }
8040 
8041  if (is_object($object) && $object->element == 'societe') {
8042  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
8043  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
8044  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
8045  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
8046  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
8047  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
8048  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
8049  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
8050  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
8051  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
8052  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
8053  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
8054  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
8055  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
8056  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
8057  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
8058  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
8059  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
8060  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
8061  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
8062  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
8063  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
8064  } elseif (is_object($object->thirdparty)) {
8065  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
8066  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
8067  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
8068  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
8069  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
8070  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
8071  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
8072  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
8073  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
8074  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
8075  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
8076  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
8077  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
8078  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
8079  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
8080  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
8081  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
8082  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
8083  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
8084  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
8085  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
8086  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
8087  }
8088 
8089  if (is_object($object) && $object->element == 'recruitmentcandidature') {
8090  $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
8091  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
8092  $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
8093  }
8094  if (is_object($object) && $object->element == 'conferenceorboothattendee') {
8095  $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
8096  $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
8097  $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
8098  }
8099 
8100  $project = null;
8101  if (is_object($object->project)) {
8102  $project = $object->project;
8103  } elseif (is_object($object->projet)) { // Deprecated, for backward compatibility
8104  $project = $object->projet;
8105  }
8106  if ($project) {
8107  $substitutionarray['__PROJECT_ID__'] = $project->id;
8108  $substitutionarray['__PROJECT_REF__'] = $project->ref;
8109  $substitutionarray['__PROJECT_NAME__'] = $project->title;
8110  }
8111  if (is_object($object) && $object->element == 'project') {
8112  $substitutionarray['__PROJECT_NAME__'] = $object->title;
8113  }
8114 
8115  if (is_object($object) && $object->element == 'shipping') {
8116  $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
8117  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
8118  }
8119  if (is_object($object) && $object->element == 'reception') {
8120  $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
8121  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
8122  }
8123 
8124  if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
8125  $dateplannedstart = '';
8126  $datenextexpiration = '';
8127  foreach ($object->lines as $line) {
8128  if ($line->date_start > $dateplannedstart) {
8129  $dateplannedstart = $line->date_start;
8130  }
8131  if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
8132  $datenextexpiration = $line->date_end;
8133  }
8134  }
8135  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
8136  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
8137  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
8138  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
8139  }
8140  // add substition variable for ticket
8141  if (is_object($object) && $object->element == 'ticket') {
8142  $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
8143  $substitutionarray['__REF__'] = $object->ref;
8144  $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
8145  $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
8146  $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
8147  $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
8148  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
8149  $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
8150  $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
8151  $userstat = new User($db);
8152  if ($object->fk_user_assign > 0) {
8153  $userstat->fetch($object->fk_user_assign);
8154  $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
8155  }
8156 
8157  if ($object->fk_user_create > 0) {
8158  $userstat->fetch($object->fk_user_create);
8159  $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
8160  }
8161  }
8162 
8163  // Create dynamic tags for __EXTRAFIELD_FIELD__
8164  if ($object->table_element && $object->id > 0) {
8165  if (!is_object($extrafields)) {
8166  $extrafields = new ExtraFields($db);
8167  }
8168  $extrafields->fetch_name_optionals_label($object->table_element, true);
8169 
8170  if ($object->fetch_optionals() > 0) {
8171  if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
8172  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
8173  if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
8174  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
8175  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
8176  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
8177  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
8178  $datetime = $object->array_options['options_'.$key];
8179  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
8180  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
8181  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
8182  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
8183  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
8184  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
8185  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
8186  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
8187  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]);
8188  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
8189  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] :'';
8190  }
8191  }
8192  }
8193  }
8194  }
8195 
8196  // Complete substitution array with the url to make online payment
8197  $paymenturl = '';
8198  if (empty($substitutionarray['__REF__'])) {
8199  $paymenturl = '';
8200  } else {
8201  // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
8202  require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
8203  $outputlangs->loadLangs(array('paypal', 'other'));
8204 
8205  $amounttouse = 0;
8206  $typeforonlinepayment = 'free';
8207  if (is_object($object) && $object->element == 'commande') {
8208  $typeforonlinepayment = 'order';
8209  }
8210  if (is_object($object) && $object->element == 'facture') {
8211  $typeforonlinepayment = 'invoice';
8212  }
8213  if (is_object($object) && $object->element == 'member') {
8214  $typeforonlinepayment = 'member';
8215  if (!empty($object->last_subscription_amount)) {
8216  $amounttouse = $object->last_subscription_amount;
8217  }
8218  }
8219  if (is_object($object) && $object->element == 'contrat') {
8220  $typeforonlinepayment = 'contract';
8221  }
8222  if (is_object($object) && $object->element == 'fichinter') {
8223  $typeforonlinepayment = 'ficheinter';
8224  }
8225 
8226  $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
8227  $paymenturl = $url;
8228  }
8229 
8230  if ($object->id > 0) {
8231  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
8232  $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
8233 
8234  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
8235  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
8236  } else {
8237  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
8238  }
8239  if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') {
8240  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
8241  } else {
8242  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
8243  }
8244  if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') {
8245  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
8246  } else {
8247  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
8248  }
8249  if (!empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'contrat') {
8250  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
8251  } else {
8252  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
8253  }
8254  if (!empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'fichinter') {
8255  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
8256  } else {
8257  $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
8258  }
8259  if (!empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'supplier_proposal') {
8260  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
8261  } else {
8262  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
8263  }
8264 
8265  if (is_object($object) && $object->element == 'propal') {
8266  $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
8267  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
8268  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
8269  }
8270  if (is_object($object) && $object->element == 'commande') {
8271  $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
8272  }
8273  if (is_object($object) && $object->element == 'facture') {
8274  $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
8275  }
8276  if (is_object($object) && $object->element == 'contrat') {
8277  $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
8278  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
8279  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref);
8280  }
8281  if (is_object($object) && $object->element == 'fichinter') {
8282  $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
8283  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
8284  $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref);
8285  }
8286  if (is_object($object) && $object->element == 'supplier_proposal') {
8287  $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
8288  }
8289  if (is_object($object) && $object->element == 'shipping') {
8290  $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
8291  }
8292  }
8293 
8294  if (is_object($object) && $object->element == 'action') {
8295  $substitutionarray['__EVENT_LABEL__'] = $object->label;
8296  $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y');
8297  $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S');
8298  }
8299  }
8300  }
8301  if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
8302  include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
8303 
8304  $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
8305  $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
8306 
8307  $already_payed_all = 0;
8308  if (is_object($object) && ($object instanceof Facture)) {
8309  $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
8310  }
8311 
8312  $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
8313 
8314  $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
8315  $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
8316  $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
8317 
8318  $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
8319 
8320  $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8321  $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)) : '';
8322  $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)) : '';
8323 
8324  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8325  $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
8326  }
8327  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8328  $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
8329  }
8330 
8331  // Amount keys formated in a currency
8332  $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8333  $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8334  $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) : '';
8335  $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)) : '';
8336  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
8337  $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8338  }
8339  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
8340  $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
8341  }
8342 
8343  $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
8344  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
8345  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
8346  // TODO Add other keys for foreign multicurrency
8347 
8348  // For backward compatibility
8349  if ($onlykey != 2) {
8350  $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
8351  $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
8352  $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
8353  }
8354  }
8355 
8356  //var_dump($substitutionarray['__AMOUNT_FORMATED__']);
8357  if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
8358  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8359 
8360  $now = dol_now();
8361 
8362  $tmp = dol_getdate($now, true);
8363  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8364  $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
8365  $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
8366  $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
8367 
8368  $daytext = $outputlangs->trans('Day'.$tmp['wday']);
8369 
8370  $substitutionarray = array_merge($substitutionarray, array(
8371  '__NOW_TMS__' => (int) $now,
8372  '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 0, $outputlangs),
8373  '__DAY__' => (string) $tmp['mday'],
8374  '__DAY_TEXT__' => $daytext, // Monday
8375  '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
8376  '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
8377  '__MONTH__' => (string) $tmp['mon'],
8378  '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
8379  '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
8380  '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
8381  '__YEAR__' => (string) $tmp['year'],
8382  '__PREVIOUS_DAY__' => (string) $tmp2['day'],
8383  '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
8384  '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
8385  '__NEXT_DAY__' => (string) $tmp4['day'],
8386  '__NEXT_MONTH__' => (string) $tmp5['month'],
8387  '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
8388  ));
8389  }
8390 
8391  if (isModEnabled('multicompany')) {
8392  $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
8393  }
8394  if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
8395  $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
8396  $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
8397  $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
8398  $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
8399  }
8400 
8401  return $substitutionarray;
8402 }
8403 
8420 function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
8421 {
8422  global $conf, $langs;
8423 
8424  if (!is_array($substitutionarray)) {
8425  return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
8426  }
8427 
8428  if (empty($outputlangs)) {
8429  $outputlangs = $langs;
8430  }
8431 
8432  // Is initial text HTML or simple text ?
8433  $msgishtml = 0;
8434  if (dol_textishtml($text, 1)) {
8435  $msgishtml = 1;
8436  }
8437 
8438  // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
8439  if (is_object($outputlangs)) {
8440  $reg = array();
8441  while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
8442  // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
8443  $tmp = explode('|', $reg[1]);
8444  if (!empty($tmp[1])) {
8445  $outputlangs->load($tmp[1]);
8446  }
8447 
8448  $value = $outputlangs->transnoentitiesnoconv($reg[1]);
8449 
8450  if (empty($converttextinhtmlifnecessary)) {
8451  // convert $newval into HTML is necessary
8452  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8453  } else {
8454  if (! $msgishtml) {
8455  $valueishtml = dol_textishtml($value, 1);
8456  //var_dump("valueishtml=".$valueishtml);
8457 
8458  if ($valueishtml) {
8459  $text = dol_htmlentitiesbr($text);
8460  $msgishtml = 1;
8461  }
8462  } else {
8463  $value = dol_nl2br("$value");
8464  }
8465 
8466  $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
8467  }
8468  }
8469  }
8470 
8471  // Make substitution for constant keys.
8472  // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
8473  $reg = array();
8474  while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
8475  $keyfound = $reg[1];
8476  if (isASecretKey($keyfound)) {
8477  $value = '*****forbidden*****';
8478  } else {
8479  $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
8480  }
8481 
8482  if (empty($converttextinhtmlifnecessary)) {
8483  // convert $newval into HTML is necessary
8484  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
8485  } else {
8486  if (! $msgishtml) {
8487  $valueishtml = dol_textishtml($value, 1);
8488 
8489  if ($valueishtml) {
8490  $text = dol_htmlentitiesbr($text);
8491  $msgishtml = 1;
8492  }
8493  } else {
8494  $value = dol_nl2br("$value");
8495  }
8496 
8497  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
8498  }
8499  }
8500 
8501  // Make substitution for array $substitutionarray
8502  foreach ($substitutionarray as $key => $value) {
8503  if (!isset($value)) {
8504  continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
8505  }
8506 
8507  if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) {
8508  $value = ''; // Protection
8509  }
8510 
8511  if (empty($converttextinhtmlifnecessary)) {
8512  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8513  } else {
8514  if (! $msgishtml) {
8515  $valueishtml = dol_textishtml($value, 1);
8516 
8517  if ($valueishtml) {
8518  $text = dol_htmlentitiesbr($text);
8519  $msgishtml = 1;
8520  }
8521  } else {
8522  $value = dol_nl2br("$value");
8523  }
8524  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
8525  }
8526  }
8527 
8528  return $text;
8529 }
8530 
8543 function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
8544 {
8545  global $conf, $user;
8546 
8547  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8548 
8549  // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
8550 
8551  // Check if there is external substitution to do, requested by plugins
8552  $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
8553 
8554  foreach ($dirsubstitutions as $reldir) {
8555  $dir = dol_buildpath($reldir, 0);
8556 
8557  // Check if directory exists
8558  if (!dol_is_dir($dir)) {
8559  continue;
8560  }
8561 
8562  $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
8563  foreach ($substitfiles as $substitfile) {
8564  $reg = array();
8565  if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
8566  $module = $reg[1];
8567 
8568  dol_syslog("Library ".$substitfile['name']." found into ".$dir);
8569  // Include the user's functions file
8570  require_once $dir.$substitfile['name'];
8571  // Call the user's function, and only if it is defined
8572  $function_name = $module."_".$callfunc;
8573  if (function_exists($function_name)) {
8574  $function_name($substitutionarray, $outputlangs, $object, $parameters);
8575  }
8576  }
8577  }
8578  }
8579  if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) {
8580  // to list all tags in odt template
8581  $tags = '';
8582  foreach ($substitutionarray as $key => $value) {
8583  $tags .= '{'.$key.'} => '.$value."\n";
8584  }
8585  $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
8586  }
8587 }
8588 
8598 function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
8599 {
8600  print get_date_range($date_start, $date_end, $format, $outputlangs);
8601 }
8602 
8613 function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
8614 {
8615  global $langs;
8616 
8617  $out = '';
8618 
8619  if (!is_object($outputlangs)) {
8620  $outputlangs = $langs;
8621  }
8622 
8623  if ($date_start && $date_end) {
8624  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8625  }
8626  if ($date_start && !$date_end) {
8627  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8628  }
8629  if (!$date_start && $date_end) {
8630  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8631  }
8632 
8633  return $out;
8634 }
8635 
8644 function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
8645 {
8646  global $conf;
8647 
8648  $ret = '';
8649  // If order not defined, we use the setup
8650  if ($nameorder < 0) {
8651  $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
8652  }
8653  if ($nameorder == 1) {
8654  $ret .= $firstname;
8655  if ($firstname && $lastname) {
8656  $ret .= ' ';
8657  }
8658  $ret .= $lastname;
8659  } elseif ($nameorder == 2 || $nameorder == 3) {
8660  $ret .= $firstname;
8661  if (empty($ret) && $nameorder == 3) {
8662  $ret .= $lastname;
8663  }
8664  } else { // 0, 4 or 5
8665  $ret .= $lastname;
8666  if (empty($ret) && $nameorder == 5) {
8667  $ret .= $firstname;
8668  }
8669  if ($nameorder == 0) {
8670  if ($firstname && $lastname) {
8671  $ret .= ' ';
8672  }
8673  $ret .= $firstname;
8674  }
8675  }
8676  return $ret;
8677 }
8678 
8679 
8691 function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0)
8692 {
8693  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
8694  if (!is_array($mesgs)) {
8695  // If mesgs is a string
8696  if ($mesgs) {
8697  if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
8698  return;
8699  }
8700  $_SESSION['dol_events'][$style][] = $mesgs;
8701  }
8702  } else {
8703  // If mesgs is an array
8704  foreach ($mesgs as $mesg) {
8705  if ($mesg) {
8706  if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
8707  return;
8708  }
8709  $_SESSION['dol_events'][$style][] = $mesg;
8710  }
8711  }
8712  }
8713 }
8714 
8727 function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0)
8728 {
8729  if (empty($mesg) && empty($mesgs)) {
8730  dol_syslog("Try to add a message in stack, but value to add is empty message", LOG_WARNING);
8731  } else {
8732  if ($messagekey) {
8733  // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
8734  // TODO
8735  $mesg .= '';
8736  }
8737  if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
8738  if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
8739  dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages');
8740  }
8741  if (empty($mesgs)) {
8742  setEventMessage($mesg, $style, $noduplicate);
8743  } else {
8744  if (!empty($mesg) && !in_array($mesg, $mesgs)) {
8745  setEventMessage($mesg, $style, $noduplicate); // Add message string if not already into array
8746  }
8747  setEventMessage($mesgs, $style, $noduplicate);
8748  }
8749  }
8750  }
8751 }
8752 
8762 function dol_htmloutput_events($disabledoutputofmessages = 0)
8763 {
8764  // Show mesgs
8765  if (isset($_SESSION['dol_events']['mesgs'])) {
8766  if (empty($disabledoutputofmessages)) {
8767  dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
8768  }
8769  unset($_SESSION['dol_events']['mesgs']);
8770  }
8771  // Show errors
8772  if (isset($_SESSION['dol_events']['errors'])) {
8773  if (empty($disabledoutputofmessages)) {
8774  dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
8775  }
8776  unset($_SESSION['dol_events']['errors']);
8777  }
8778 
8779  // Show warnings
8780  if (isset($_SESSION['dol_events']['warnings'])) {
8781  if (empty($disabledoutputofmessages)) {
8782  dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
8783  }
8784  unset($_SESSION['dol_events']['warnings']);
8785  }
8786 }
8787 
8802 function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
8803 {
8804  global $conf, $langs;
8805 
8806  $ret = 0;
8807  $return = '';
8808  $out = '';
8809  $divstart = $divend = '';
8810 
8811  // If inline message with no format, we add it.
8812  if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
8813  $divstart = '<div class="'.$style.' clearboth">';
8814  $divend = '</div>';
8815  }
8816 
8817  if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
8818  $langs->load("errors");
8819  $out .= $divstart;
8820  if (is_array($mesgarray) && count($mesgarray)) {
8821  foreach ($mesgarray as $message) {
8822  $ret++;
8823  $out .= $langs->trans($message);
8824  if ($ret < count($mesgarray)) {
8825  $out .= "<br>\n";
8826  }
8827  }
8828  }
8829  if ($mesgstring) {
8830  $ret++;
8831  $out .= $langs->trans($mesgstring);
8832  }
8833  $out .= $divend;
8834  }
8835 
8836  if ($out) {
8837  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) {
8838  $return = '<script nonce="'.getNonce().'">
8839  $(document).ready(function() {
8840  var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").'
8841  if (block) {
8842  $.dolEventValid("","'.dol_escape_js($out).'");
8843  } else {
8844  /* jnotify(message, preset of message type, keepmessage) */
8845  $.jnotify("'.dol_escape_js($out).'",
8846  "'.($style == "ok" ? 3000 : $style).'",
8847  '.($style == "ok" ? "false" : "true").',
8848  { remove: function (){} } );
8849  }
8850  });
8851  </script>';
8852  } else {
8853  $return = $out;
8854  }
8855  }
8856 
8857  return $return;
8858 }
8859 
8871 function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8872 {
8873  return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8874 }
8875 
8889 function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
8890 {
8891  if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
8892  return;
8893  }
8894 
8895  $iserror = 0;
8896  $iswarning = 0;
8897  if (is_array($mesgarray)) {
8898  foreach ($mesgarray as $val) {
8899  if ($val && preg_match('/class="error"/i', $val)) {
8900  $iserror++;
8901  break;
8902  }
8903  if ($val && preg_match('/class="warning"/i', $val)) {
8904  $iswarning++;
8905  break;
8906  }
8907  }
8908  } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
8909  $iserror++;
8910  } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
8911  $iswarning++;
8912  }
8913  if ($style == 'error') {
8914  $iserror++;
8915  }
8916  if ($style == 'warning') {
8917  $iswarning++;
8918  }
8919 
8920  if ($iserror || $iswarning) {
8921  // Remove div from texts
8922  $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
8923  $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
8924  $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
8925  // Remove div from texts array
8926  if (is_array($mesgarray)) {
8927  $newmesgarray = array();
8928  foreach ($mesgarray as $val) {
8929  if (is_string($val)) {
8930  $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
8931  $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
8932  $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
8933  $newmesgarray[] = $tmpmesgstring;
8934  } else {
8935  dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
8936  }
8937  }
8938  $mesgarray = $newmesgarray;
8939  }
8940  print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
8941  } else {
8942  print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
8943  }
8944 }
8945 
8957 function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8958 {
8959  dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8960 }
8961 
8976 function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
8977 {
8978  // Clean parameters
8979  $order = strtolower($order);
8980 
8981  if (is_array($array)) {
8982  $sizearray = count($array);
8983  if ($sizearray > 0) {
8984  $temp = array();
8985  foreach (array_keys($array) as $key) {
8986  if (is_object($array[$key])) {
8987  $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
8988  } else {
8989  $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
8990  }
8991  if ($natsort == -1) {
8992  $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
8993  }
8994  }
8995 
8996  if (empty($natsort) || $natsort == -1) {
8997  if ($order == 'asc') {
8998  asort($temp);
8999  } else {
9000  arsort($temp);
9001  }
9002  } else {
9003  if ($case_sensitive) {
9004  natsort($temp);
9005  } else {
9006  natcasesort($temp); // natecasesort is not sensible to case
9007  }
9008  if ($order != 'asc') {
9009  $temp = array_reverse($temp, true);
9010  }
9011  }
9012 
9013  $sorted = array();
9014 
9015  foreach (array_keys($temp) as $key) {
9016  (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
9017  }
9018 
9019  return $sorted;
9020  }
9021  }
9022  return $array;
9023 }
9024 
9025 
9032 function utf8_check($str)
9033 {
9034  $str = (string) $str; // Sometimes string is an int.
9035 
9036  // We must use here a binary strlen function (so not dol_strlen)
9037  $strLength = dol_strlen($str);
9038  for ($i = 0; $i < $strLength; $i++) {
9039  if (ord($str[$i]) < 0x80) {
9040  continue; // 0bbbbbbb
9041  } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
9042  $n = 1; // 110bbbbb
9043  } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
9044  $n = 2; // 1110bbbb
9045  } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
9046  $n = 3; // 11110bbb
9047  } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
9048  $n = 4; // 111110bb
9049  } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
9050  $n = 5; // 1111110b
9051  } else {
9052  return false; // Does not match any model
9053  }
9054  for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
9055  if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
9056  return false;
9057  }
9058  }
9059  }
9060  return true;
9061 }
9062 
9069 function utf8_valid($str)
9070 {
9071  /* 2 other methods to test if string is utf8
9072  $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
9073  $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
9074  */
9075  return preg_match('//u', $str) ? true : false;
9076 }
9077 
9078 
9085 function ascii_check($str)
9086 {
9087  if (function_exists('mb_check_encoding')) {
9088  //if (mb_detect_encoding($str, 'ASCII', true) return false;
9089  if (!mb_check_encoding($str, 'ASCII')) {
9090  return false;
9091  }
9092  } else {
9093  if (preg_match('/[^\x00-\x7f]/', $str)) {
9094  return false; // Contains a byte > 7f
9095  }
9096  }
9097 
9098  return true;
9099 }
9100 
9101 
9109 function dol_osencode($str)
9110 {
9111  global $conf;
9112 
9113  $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
9114  if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
9115  $tmp = 'iso-8859-1'; // By default for windows
9116  }
9117  if (empty($tmp)) {
9118  $tmp = 'utf-8'; // By default for other
9119  }
9120  if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) {
9121  $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING;
9122  }
9123 
9124  if ($tmp == 'iso-8859-1') {
9125  return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
9126  }
9127  return $str;
9128 }
9129 
9130 
9145 function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
9146 {
9147  global $cache_codes;
9148 
9149  // If key empty
9150  if ($key == '') {
9151  return '';
9152  }
9153 
9154  // Check in cache
9155  if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
9156  return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
9157  }
9158 
9159  dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
9160 
9161  $sql = "SELECT ".$fieldid." as valuetoget";
9162  $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
9163  $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
9164  if (!empty($entityfilter)) {
9165  $sql .= " AND entity IN (".getEntity($tablename).")";
9166  }
9167  if ($filters) {
9168  $sql .= $filters;
9169  }
9170 
9171  $resql = $db->query($sql);
9172  if ($resql) {
9173  $obj = $db->fetch_object($resql);
9174  if ($obj) {
9175  $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
9176  } else {
9177  $cache_codes[$tablename][$key][$fieldid] = '';
9178  }
9179  $db->free($resql);
9180  return $cache_codes[$tablename][$key][$fieldid];
9181  } else {
9182  return -1;
9183  }
9184 }
9185 
9192 function verifCond($strToEvaluate)
9193 {
9194  global $user, $conf, $langs;
9195  global $leftmenu;
9196  global $rights; // To export to dol_eval function
9197 
9198  //print $strToEvaluate."<br>\n";
9199  $rights = true;
9200  if (isset($strToEvaluate) && $strToEvaluate !== '') {
9201  //var_dump($strToEvaluate);
9202  //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
9203  $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
9204  $rights = $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
9205  //var_dump($rights);
9206  }
9207  return $rights;
9208 }
9209 
9220 function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
9221 {
9222  // Only global variables can be changed by eval function and returned to caller
9223  global $db, $langs, $user, $conf, $website, $websitepage;
9224  global $action, $mainmenu, $leftmenu;
9225  global $mysoc;
9226  global $objectoffield;
9227 
9228  // Old variables used
9229  global $rights;
9230  global $object;
9231  global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
9232  global $soc; // For backward compatibility
9233 
9234  try {
9235  // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
9236  if ($onlysimplestring == '1') {
9237  // We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
9238  // We must accept: '$conf->barcode->enabled || preg_match(\'/^AAA/\',$leftmenu)'
9239  // We must accept: '$user->rights->cabinetmed->read && !$object->canvas=="patient@cabinetmed"'
9240  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@', '/').']/i', $s)) {
9241  if ($returnvalue) {
9242  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
9243  } else {
9244  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
9245  return '';
9246  }
9247  // TODO
9248  // We can exclude all parenthesis ( that are not '($db' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match(' and 'isModEnabled('
9249  // ...
9250  }
9251  } elseif ($onlysimplestring == '2') {
9252  // 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"
9253  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@[]', '/').']/i', $s)) {
9254  if ($returnvalue) {
9255  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
9256  } else {
9257  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
9258  return '';
9259  }
9260  // TODO
9261  // We can exclude all parenthesis ( that are not '($db' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match(' and 'isModEnabled('
9262  // ...
9263  }
9264  }
9265  if (is_array($s) || $s === 'Array') {
9266  return 'Bad string syntax to evaluate (value is Array) '.var_export($s, true);
9267  }
9268  if (strpos($s, '::') !== false) {
9269  if ($returnvalue) {
9270  return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
9271  } else {
9272  dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s);
9273  return '';
9274  }
9275  }
9276  if (strpos($s, '`') !== false) {
9277  if ($returnvalue) {
9278  return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
9279  } else {
9280  dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s);
9281  return '';
9282  }
9283  }
9284  if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
9285  if ($returnvalue) {
9286  return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
9287  } else {
9288  dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s);
9289  return '';
9290  }
9291  }
9292 
9293  // We block use of php exec or php file functions
9294  $forbiddenphpstrings = array('$$');
9295  $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
9296 
9297  $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen");
9298  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
9299  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64_decode", "rawurldecode", "urldecode", "str_rot13", "hex2bin")); // decode string functions used to obfuscated function name
9300  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
9301  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
9302  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
9303  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
9304  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
9305 
9306  $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
9307 
9308  $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
9309 
9310  $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
9311 
9312  do {
9313  $oldstringtoclean = $s;
9314  $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
9315  $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
9316  $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
9317  //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
9318  } while ($oldstringtoclean != $s);
9319 
9320  if (strpos($s, '__forbiddenstring__') !== false) {
9321  dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
9322  if ($returnvalue) {
9323  return 'Bad string syntax to evaluate: '.$s;
9324  } else {
9325  dol_syslog('Bad string syntax to evaluate: '.$s);
9326  return '';
9327  }
9328  }
9329 
9330  //print $s."<br>\n";
9331  if ($returnvalue) {
9332  if ($hideerrors) {
9333  return @eval('return '.$s.';');
9334  } else {
9335  return eval('return '.$s.';');
9336  }
9337  } else {
9338  if ($hideerrors) {
9339  @eval($s);
9340  } else {
9341  eval($s);
9342  }
9343  }
9344  } catch (Error $e) {
9345  $error = 'dol_eval try/catch error : ';
9346  $error .= $e->getMessage();
9347  dol_syslog($error);
9348  }
9349 }
9350 
9357 function dol_validElement($element)
9358 {
9359  return (trim($element) != '');
9360 }
9361 
9370 function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
9371 {
9372  if (empty($codelang)) {
9373  return '';
9374  }
9375 
9376  if ($codelang == 'auto') {
9377  return '<span class="fa fa-language"></span>';
9378  }
9379 
9380  $langtocountryflag = array(
9381  'ar_AR' => '',
9382  'ca_ES' => 'catalonia',
9383  'da_DA' => 'dk',
9384  'fr_CA' => 'mq',
9385  'sv_SV' => 'se',
9386  'sw_SW' => 'unknown',
9387  'AQ' => 'unknown',
9388  'CW' => 'unknown',
9389  'IM' => 'unknown',
9390  'JE' => 'unknown',
9391  'MF' => 'unknown',
9392  'BL' => 'unknown',
9393  'SX' => 'unknown'
9394  );
9395 
9396  if (isset($langtocountryflag[$codelang])) {
9397  $flagImage = $langtocountryflag[$codelang];
9398  } else {
9399  $tmparray = explode('_', $codelang);
9400  $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
9401  }
9402 
9403  return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
9404 }
9405 
9413 function getLanguageCodeFromCountryCode($countrycode)
9414 {
9415  global $mysoc;
9416 
9417  if (empty($countrycode)) {
9418  return null;
9419  }
9420 
9421  if (strtoupper($countrycode) == 'MQ') {
9422  return 'fr_CA';
9423  }
9424  if (strtoupper($countrycode) == 'SE') {
9425  return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
9426  }
9427  if (strtoupper($countrycode) == 'CH') {
9428  if ($mysoc->country_code == 'FR') {
9429  return 'fr_CH';
9430  }
9431  if ($mysoc->country_code == 'DE') {
9432  return 'de_CH';
9433  }
9434  if ($mysoc->country_code == 'IT') {
9435  return 'it_CH';
9436  }
9437  }
9438 
9439  // Locale list taken from:
9440  // http://stackoverflow.com/questions/3191664/
9441  // list-of-all-locales-and-their-short-codes
9442  $locales = array(
9443  'af-ZA',
9444  'am-ET',
9445  'ar-AE',
9446  'ar-BH',
9447  'ar-DZ',
9448  'ar-EG',
9449  'ar-IQ',
9450  'ar-JO',
9451  'ar-KW',
9452  'ar-LB',
9453  'ar-LY',
9454  'ar-MA',
9455  'ar-OM',
9456  'ar-QA',
9457  'ar-SA',
9458  'ar-SY',
9459  'ar-TN',
9460  'ar-YE',
9461  //'as-IN', // Moved after en-IN
9462  'ba-RU',
9463  'be-BY',
9464  'bg-BG',
9465  'bn-BD',
9466  //'bn-IN', // Moved after en-IN
9467  'bo-CN',
9468  'br-FR',
9469  'ca-ES',
9470  'co-FR',
9471  'cs-CZ',
9472  'cy-GB',
9473  'da-DK',
9474  'de-AT',
9475  'de-CH',
9476  'de-DE',
9477  'de-LI',
9478  'de-LU',
9479  'dv-MV',
9480  'el-GR',
9481  'en-AU',
9482  'en-BZ',
9483  'en-CA',
9484  'en-GB',
9485  'en-IE',
9486  'en-IN',
9487  'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
9488  'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
9489  'en-JM',
9490  'en-MY',
9491  'en-NZ',
9492  'en-PH',
9493  'en-SG',
9494  'en-TT',
9495  'en-US',
9496  'en-ZA',
9497  'en-ZW',
9498  'es-AR',
9499  'es-BO',
9500  'es-CL',
9501  'es-CO',
9502  'es-CR',
9503  'es-DO',
9504  'es-EC',
9505  'es-ES',
9506  'es-GT',
9507  'es-HN',
9508  'es-MX',
9509  'es-NI',
9510  'es-PA',
9511  'es-PE',
9512  'es-PR',
9513  'es-PY',
9514  'es-SV',
9515  'es-US',
9516  'es-UY',
9517  'es-VE',
9518  'et-EE',
9519  'eu-ES',
9520  'fa-IR',
9521  'fi-FI',
9522  'fo-FO',
9523  'fr-BE',
9524  'fr-CA',
9525  'fr-CH',
9526  'fr-FR',
9527  'fr-LU',
9528  'fr-MC',
9529  'fy-NL',
9530  'ga-IE',
9531  'gd-GB',
9532  'gl-ES',
9533  'gu-IN',
9534  'he-IL',
9535  'hi-IN',
9536  'hr-BA',
9537  'hr-HR',
9538  'hu-HU',
9539  'hy-AM',
9540  'id-ID',
9541  'ig-NG',
9542  'ii-CN',
9543  'is-IS',
9544  'it-CH',
9545  'it-IT',
9546  'ja-JP',
9547  'ka-GE',
9548  'kk-KZ',
9549  'kl-GL',
9550  'km-KH',
9551  'kn-IN',
9552  'ko-KR',
9553  'ky-KG',
9554  'lb-LU',
9555  'lo-LA',
9556  'lt-LT',
9557  'lv-LV',
9558  'mi-NZ',
9559  'mk-MK',
9560  'ml-IN',
9561  'mn-MN',
9562  'mr-IN',
9563  'ms-BN',
9564  'ms-MY',
9565  'mt-MT',
9566  'nb-NO',
9567  'ne-NP',
9568  'nl-BE',
9569  'nl-NL',
9570  'nn-NO',
9571  'oc-FR',
9572  'or-IN',
9573  'pa-IN',
9574  'pl-PL',
9575  'ps-AF',
9576  'pt-BR',
9577  'pt-PT',
9578  'rm-CH',
9579  'ro-MD',
9580  'ro-RO',
9581  'ru-RU',
9582  'rw-RW',
9583  'sa-IN',
9584  'se-FI',
9585  'se-NO',
9586  'se-SE',
9587  'si-LK',
9588  'sk-SK',
9589  'sl-SI',
9590  'sq-AL',
9591  'sv-FI',
9592  'sv-SE',
9593  'sw-KE',
9594  'ta-IN',
9595  'te-IN',
9596  'th-TH',
9597  'tk-TM',
9598  'tn-ZA',
9599  'tr-TR',
9600  'tt-RU',
9601  'ug-CN',
9602  'uk-UA',
9603  'ur-PK',
9604  'vi-VN',
9605  'wo-SN',
9606  'xh-ZA',
9607  'yo-NG',
9608  'zh-CN',
9609  'zh-HK',
9610  'zh-MO',
9611  'zh-SG',
9612  'zh-TW',
9613  'zu-ZA',
9614  );
9615 
9616  $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
9617  if (in_array($buildprimarykeytotest, $locales)) {
9618  return strtolower($countrycode).'_'.strtoupper($countrycode);
9619  }
9620 
9621  if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
9622  foreach ($locales as $locale) {
9623  $locale_language = locale_get_primary_language($locale);
9624  $locale_region = locale_get_region($locale);
9625  if (strtoupper($countrycode) == $locale_region) {
9626  //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
9627  return strtolower($locale_language).'_'.strtoupper($locale_region);
9628  }
9629  }
9630  } else {
9631  dol_syslog("Warning Exention php-intl is not available", LOG_WARNING);
9632  }
9633 
9634  return null;
9635 }
9636 
9667 function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
9668 {
9669  global $hookmanager, $db;
9670 
9671  if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
9672  foreach ($conf->modules_parts['tabs'][$type] as $value) {
9673  $values = explode(':', $value);
9674 
9675  $reg = array();
9676  if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
9677  if (count($values) == 6) {
9678  // new declaration with permissions:
9679  // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9680  // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9681  if ($values[0] != $type) {
9682  continue;
9683  }
9684 
9685  if (verifCond($values[4])) {
9686  if ($values[3]) {
9687  if ($filterorigmodule) { // If a filter of module origin has been requested
9688  if (strpos($values[3], '@')) { // This is an external module
9689  if ($filterorigmodule != 'external') {
9690  continue;
9691  }
9692  } else { // This looks a core module
9693  if ($filterorigmodule != 'core') {
9694  continue;
9695  }
9696  }
9697  }
9698  $langs->load($values[3]);
9699  }
9700  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9701  // If label is "SUBSTITUION_..."
9702  $substitutionarray = array();
9703  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9704  $label = make_substitutions($reg[1], $substitutionarray);
9705  } else {
9706  // If label is "Label,Class,File,Method", we call the method to show content inside the badge
9707  $labeltemp = explode(',', $values[2]);
9708  $label = $langs->trans($labeltemp[0]);
9709 
9710  if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
9711  dol_include_once($labeltemp[2]);
9712  $classtoload = $labeltemp[1];
9713  if (class_exists($classtoload)) {
9714  $obj = new $classtoload($db);
9715  $function = $labeltemp[3];
9716  if ($obj && $function && method_exists($obj, $function)) {
9717  $nbrec = $obj->$function($object->id, $obj);
9718  if (!empty($nbrec)) {
9719  $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
9720  }
9721  }
9722  }
9723  }
9724  }
9725 
9726  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
9727  $head[$h][1] = $label;
9728  $head[$h][2] = str_replace('+', '', $values[1]);
9729  $h++;
9730  }
9731  } elseif (count($values) == 5) { // case deprecated
9732  dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
9733 
9734  if ($values[0] != $type) {
9735  continue;
9736  }
9737  if ($values[3]) {
9738  if ($filterorigmodule) { // If a filter of module origin has been requested
9739  if (strpos($values[3], '@')) { // This is an external module
9740  if ($filterorigmodule != 'external') {
9741  continue;
9742  }
9743  } else { // This looks a core module
9744  if ($filterorigmodule != 'core') {
9745  continue;
9746  }
9747  }
9748  }
9749  $langs->load($values[3]);
9750  }
9751  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9752  $substitutionarray = array();
9753  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9754  $label = make_substitutions($reg[1], $substitutionarray);
9755  } else {
9756  $label = $langs->trans($values[2]);
9757  }
9758 
9759  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
9760  $head[$h][1] = $label;
9761  $head[$h][2] = str_replace('+', '', $values[1]);
9762  $h++;
9763  }
9764  } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
9765  if ($values[0] != $type) {
9766  continue;
9767  }
9768  $tabname = str_replace('-', '', $values[1]);
9769  foreach ($head as $key => $val) {
9770  $condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
9771  //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
9772  if ($head[$key][2] == $tabname && $condition) {
9773  unset($head[$key]);
9774  break;
9775  }
9776  }
9777  }
9778  }
9779  }
9780 
9781  // No need to make a return $head. Var is modified as a reference
9782  if (!empty($hookmanager)) {
9783  $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
9784  $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters);
9785  if ($reshook > 0) { // Hook ask to replace completely the array
9786  $head = $hookmanager->resArray;
9787  } else { // Hook
9788  $head = array_merge($head, $hookmanager->resArray);
9789  }
9790  $h = count($head);
9791  }
9792 }
9793 
9805 function printCommonFooter($zone = 'private')
9806 {
9807  global $conf, $hookmanager, $user, $debugbar;
9808  global $action;
9809  global $micro_start_time;
9810 
9811  if ($zone == 'private') {
9812  print "\n".'<!-- Common footer for private page -->'."\n";
9813  } else {
9814  print "\n".'<!-- Common footer for public page -->'."\n";
9815  }
9816 
9817  // A div to store page_y POST parameter so we can read it using javascript
9818  print "\n<!-- A div to store page_y POST parameter -->\n";
9819  print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
9820 
9821  $parameters = array();
9822  $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
9823  if (empty($reshook)) {
9824  if (!empty($conf->global->MAIN_HTML_FOOTER)) {
9825  print $conf->global->MAIN_HTML_FOOTER."\n";
9826  }
9827 
9828  print "\n";
9829  if (!empty($conf->use_javascript_ajax)) {
9830  print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and madatory fields, tuning info, ... -->\n";
9831  print '<script>'."\n";
9832  print 'jQuery(document).ready(function() {'."\n";
9833 
9834  if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
9835  print "\n";
9836  print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
9837  print 'jQuery("li.menuhider").click(function(event) {';
9838  print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
9839  print ' console.log("We click on .menuhider");'."\n";
9840  print ' $("body").toggleClass("sidebar-collapse")'."\n";
9841  print '});'."\n";
9842  }
9843 
9844  // Management of focus and mandatory for fields
9845  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"])))) {
9846  print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
9847  $relativepathstring = $_SERVER["PHP_SELF"];
9848  // Clean $relativepathstring
9849  if (constant('DOL_URL_ROOT')) {
9850  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
9851  }
9852  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
9853  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
9854  //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
9855  if (!empty($user->default_values[$relativepathstring]['focus'])) {
9856  foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
9857  $qualified = 0;
9858  if ($defkey != '_noquery_') {
9859  $tmpqueryarraytohave = explode('&', $defkey);
9860  $foundintru = 0;
9861  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9862  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9863  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9864  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9865  $foundintru = 1;
9866  }
9867  }
9868  if (!$foundintru) {
9869  $qualified = 1;
9870  }
9871  //var_dump($defkey.'-'.$qualified);
9872  } else {
9873  $qualified = 1;
9874  }
9875 
9876  if ($qualified) {
9877  foreach ($defval as $paramkey => $paramval) {
9878  // Set focus on field
9879  print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
9880  print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
9881  print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
9882  }
9883  }
9884  }
9885  }
9886  if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
9887  foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
9888  $qualified = 0;
9889  if ($defkey != '_noquery_') {
9890  $tmpqueryarraytohave = explode('&', $defkey);
9891  $foundintru = 0;
9892  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9893  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9894  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9895  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9896  $foundintru = 1;
9897  }
9898  }
9899  if (!$foundintru) {
9900  $qualified = 1;
9901  }
9902  //var_dump($defkey.'-'.$qualified);
9903  } else {
9904  $qualified = 1;
9905  }
9906 
9907  if ($qualified) {
9908  foreach ($defval as $paramkey => $paramval) {
9909  // Add property 'required' on input
9910  print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9911  print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9912  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";
9913  print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9914  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
9915  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
9916 
9917  // Add 'field required' class on closest td for all input elements : input, textarea and select
9918  print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
9919  }
9920  }
9921  }
9922  }
9923  }
9924 
9925  print '});'."\n";
9926 
9927  // End of tuning
9928  if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) {
9929  print "\n";
9930  print "/* JS CODE TO ENABLE to add memory info */\n";
9931  print 'window.console && console.log("';
9932  if (!empty($conf->global->MEMCACHED_SERVER)) {
9933  print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
9934  }
9935  print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off');
9936  if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
9937  $micro_end_time = microtime(true);
9938  print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
9939  }
9940 
9941  if (function_exists("memory_get_usage")) {
9942  print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
9943  }
9944  if (function_exists("memory_get_peak_usage")) {
9945  print ' - Real mem peak: '.memory_get_peak_usage(true);
9946  }
9947  if (function_exists("zend_loader_file_encoded")) {
9948  print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
9949  }
9950  print '");'."\n";
9951  }
9952 
9953  print "\n".'</script>'."\n";
9954 
9955  // Google Analytics
9956  // TODO Add a hook here
9957  if (isModEnabled('google') && !empty($conf->global->MAIN_GOOGLE_AN_ID)) {
9958  $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID);
9959  foreach ($tmptagarray as $tmptag) {
9960  print "\n";
9961  print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
9962  print '
9963  <!-- Global site tag (gtag.js) - Google Analytics -->
9964  <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
9965  <script>
9966  window.dataLayer = window.dataLayer || [];
9967  function gtag(){dataLayer.push(arguments);}
9968  gtag(\'js\', new Date());
9969 
9970  gtag(\'config\', \''.trim($tmptag).'\');
9971  </script>';
9972  print "\n";
9973  }
9974  }
9975  }
9976 
9977  // Add Xdebug coverage of code
9978  if (defined('XDEBUGCOVERAGE')) {
9979  print_r(xdebug_get_code_coverage());
9980  }
9981 
9982  // Add DebugBar data
9983  if (!empty($user->rights->debugbar->read) && is_object($debugbar)) {
9984  $debugbar['time']->stopMeasure('pageaftermaster');
9985  print '<!-- Output debugbar data -->'."\n";
9986  $renderer = $debugbar->getRenderer();
9987  print $debugbar->getRenderer()->render();
9988  } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
9989  print "\n";
9990  print "<!-- Start of log output\n";
9991  //print '<div class="hidden">'."\n";
9992  foreach ($conf->logbuffer as $logline) {
9993  print $logline."<br>\n";
9994  }
9995  //print '</div>'."\n";
9996  print "End of log output -->\n";
9997  }
9998  }
9999 }
10000 
10010 function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
10011 {
10012  if (is_null($string)) {
10013  return array();
10014  }
10015 
10016  if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
10017  // This is a regex string
10018  $newdelimiter = $delimiter;
10019  } else {
10020  // This is a simple string
10021  $newdelimiter = preg_quote($delimiter, '/');
10022  }
10023 
10024  if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
10025  $ka = array();
10026  foreach ($a as $s) { // each part
10027  if ($s) {
10028  if ($pos = strpos($s, $kv)) { // key/value delimiter
10029  $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
10030  } else { // key delimiter not found
10031  $ka[] = trim($s);
10032  }
10033  }
10034  }
10035  return $ka;
10036  }
10037 
10038  return array();
10039 }
10040 
10041 
10048 function dol_set_focus($selector)
10049 {
10050  print "\n".'<!-- Set focus onto a specific field -->'."\n";
10051  print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
10052 }
10053 
10054 
10062 function dol_getmypid()
10063 {
10064  if (!function_exists('getmypid')) {
10065  return mt_rand(99900000, 99965535);
10066  } else {
10067  return getmypid(); // May be a number on 64 bits (depending on OS)
10068  }
10069 }
10070 
10071 
10089 function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
10090 {
10091  global $db, $langs;
10092 
10093  $value = trim($value);
10094 
10095  if ($mode == 0) {
10096  $value = preg_replace('/\*/', '%', $value); // Replace * with %
10097  }
10098  if ($mode == 1) {
10099  $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
10100  }
10101 
10102  $value = preg_replace('/\s*\|\s*/', '|', $value);
10103 
10104  $crits = explode(' ', $value);
10105  $res = '';
10106  if (!is_array($fields)) {
10107  $fields = array($fields);
10108  }
10109 
10110  $i1 = 0; // count the nb of and criteria added (all fields / criterias)
10111  foreach ($crits as $crit) { // Loop on each AND criteria
10112  $crit = trim($crit);
10113  $i2 = 0; // count the nb of valid criteria added for this this first criteria
10114  $newres = '';
10115  foreach ($fields as $field) {
10116  if ($mode == 1) {
10117  $tmpcrits = explode('|', $crit);
10118  $i3 = 0; // count the nb of valid criteria added for this current field
10119  foreach ($tmpcrits as $tmpcrit) {
10120  if ($tmpcrit !== '0' && empty($tmpcrit)) {
10121  continue;
10122  }
10123  $tmpcrit = trim($tmpcrit);
10124 
10125  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
10126 
10127  $operator = '=';
10128  $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
10129 
10130  $reg = array();
10131  preg_match('/([!<>=]+)/', $tmpcrit, $reg);
10132  if (!empty($reg[1])) {
10133  $operator = $reg[1];
10134  }
10135  if ($newcrit != '') {
10136  $numnewcrit = price2num($newcrit);
10137  if (is_numeric($numnewcrit)) {
10138  $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
10139  } else {
10140  $newres .= '1 = 2'; // force false, we received a corrupted data
10141  }
10142  $i3++; // a criteria was added to string
10143  }
10144  }
10145  $i2++; // a criteria for 1 more field was added to string
10146  } elseif ($mode == 2 || $mode == -2) {
10147  $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
10148  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
10149  $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
10150  if ($mode == -2) {
10151  $newres .= ' OR '.$field.' IS NULL';
10152  }
10153  $i2++; // a criteria for 1 more field was added to string
10154  } elseif ($mode == 3 || $mode == -3) {
10155  $tmparray = explode(',', $crit);
10156  if (count($tmparray)) {
10157  $listofcodes = '';
10158  foreach ($tmparray as $val) {
10159  $val = trim($val);
10160  if ($val) {
10161  $listofcodes .= ($listofcodes ? ',' : '');
10162  $listofcodes .= "'".$db->escape($val)."'";
10163  }
10164  }
10165  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
10166  $i2++; // a criteria for 1 more field was added to string
10167  }
10168  if ($mode == -3) {
10169  $newres .= ' OR '.$field.' IS NULL';
10170  }
10171  } elseif ($mode == 4) {
10172  $tmparray = explode(',', $crit);
10173  if (count($tmparray)) {
10174  $listofcodes = '';
10175  foreach ($tmparray as $val) {
10176  $val = trim($val);
10177  if ($val) {
10178  $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
10179  $newres .= ' OR '.$field." = '".$db->escape($val)."'";
10180  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
10181  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
10182  $newres .= ')';
10183  $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)
10184  }
10185  }
10186  }
10187  } else { // $mode=0
10188  $tmpcrits = explode('|', $crit);
10189  $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
10190  foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
10191  if ($tmpcrit !== '0' && empty($tmpcrit)) {
10192  continue;
10193  }
10194  $tmpcrit = trim($tmpcrit);
10195 
10196  if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
10197  $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
10198  } else {
10199  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
10200  }
10201 
10202  if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
10203  $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
10204  } else {
10205  $tmpcrit2 = $tmpcrit;
10206  $tmpbefore = '%';
10207  $tmpafter = '%';
10208  $tmps = '';
10209 
10210  if (preg_match('/^!/', $tmpcrit)) {
10211  $tmps .= $field." NOT LIKE "; // ! as exclude character
10212  $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
10213  } else {
10214  $tmps .= $field." LIKE ";
10215  }
10216  $tmps .= "'";
10217 
10218  if (preg_match('/^[\^\$]/', $tmpcrit)) {
10219  $tmpbefore = '';
10220  $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
10221  }
10222  if (preg_match('/[\^\$]$/', $tmpcrit)) {
10223  $tmpafter = '';
10224  $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
10225  }
10226 
10227  if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
10228  $tmps = "(".$tmps;
10229  }
10230  $newres .= $tmps;
10231  $newres .= $tmpbefore;
10232  $newres .= $db->escape($tmpcrit2);
10233  $newres .= $tmpafter;
10234  $newres .= "'";
10235  if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
10236  $newres .= " OR ".$field." IS NULL)";
10237  }
10238  }
10239 
10240  $i3++;
10241  }
10242 
10243  $i2++; // a criteria for 1 more field was added to string
10244  }
10245  }
10246 
10247  if ($newres) {
10248  $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
10249  }
10250  $i1++;
10251  }
10252  $res = ($nofirstand ? "" : " AND ")."(".$res.")";
10253 
10254  return $res;
10255 }
10256 
10263 function showDirectDownloadLink($object)
10264 {
10265  global $conf, $langs;
10266 
10267  $out = '';
10268  $url = $object->getLastMainDocLink($object->element);
10269 
10270  $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
10271  if ($url) {
10272  $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
10273  $out .= ajax_autoselect("directdownloadlink", 0);
10274  } else {
10275  $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
10276  }
10277 
10278  return $out;
10279 }
10280 
10289 function getImageFileNameForSize($file, $extName, $extImgTarget = '')
10290 {
10291  $dirName = dirname($file);
10292  if ($dirName == '.') {
10293  $dirName = '';
10294  }
10295 
10296  $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
10297  $fileName = basename($fileName);
10298 
10299  if (empty($extImgTarget)) {
10300  $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
10301  }
10302  if (empty($extImgTarget)) {
10303  $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
10304  }
10305  if (empty($extImgTarget)) {
10306  $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
10307  }
10308  if (empty($extImgTarget)) {
10309  $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
10310  }
10311  if (empty($extImgTarget)) {
10312  $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
10313  }
10314  if (empty($extImgTarget)) {
10315  $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
10316  }
10317 
10318  if (!$extImgTarget) {
10319  return $file;
10320  }
10321 
10322  $subdir = '';
10323  if ($extName) {
10324  $subdir = 'thumbs/';
10325  }
10326 
10327  return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
10328 }
10329 
10330 
10340 function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
10341 {
10342  global $conf, $langs;
10343 
10344  if (empty($conf->use_javascript_ajax)) {
10345  return '';
10346  }
10347 
10348  $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
10349 
10350  if ($alldata == 1) {
10351  if ($isAllowedForPreview) {
10352  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));
10353  } else {
10354  return array();
10355  }
10356  }
10357 
10358  // old behavior, return a string
10359  if ($isAllowedForPreview) {
10360  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')).'\')';
10361  } else {
10362  return '';
10363  }
10364 }
10365 
10366 
10375 function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
10376 {
10377  global $langs;
10378  $out = '<script nonce="'.getNonce().'">
10379  jQuery(document).ready(function () {
10380  jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
10381  });
10382  </script>';
10383  if ($addlink) {
10384  if ($textonlink === 'image') {
10385  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
10386  } else {
10387  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
10388  }
10389  }
10390  return $out;
10391 }
10392 
10400 function dolIsAllowedForPreview($file)
10401 {
10402  global $conf;
10403 
10404  // Check .noexe extension in filename
10405  if (preg_match('/\.noexe$/i', $file)) {
10406  return 0;
10407  }
10408 
10409  // Check mime types
10410  $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
10411  if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) {
10412  $mime_preview[] = 'svg+xml';
10413  }
10414  //$mime_preview[]='vnd.oasis.opendocument.presentation';
10415  //$mime_preview[]='archive';
10416  $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
10417  if ($num_mime !== false) {
10418  return 1;
10419  }
10420 
10421  // By default, not allowed for preview
10422  return 0;
10423 }
10424 
10425 
10435 function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
10436 {
10437  $mime = $default;
10438  $imgmime = 'other.png';
10439  $famime = 'file-o';
10440  $srclang = '';
10441 
10442  $tmpfile = preg_replace('/\.noexe$/', '', $file);
10443 
10444  // Plain text files
10445  if (preg_match('/\.txt$/i', $tmpfile)) {
10446  $mime = 'text/plain';
10447  $imgmime = 'text.png';
10448  $famime = 'file-text-o';
10449  }
10450  if (preg_match('/\.rtx$/i', $tmpfile)) {
10451  $mime = 'text/richtext';
10452  $imgmime = 'text.png';
10453  $famime = 'file-text-o';
10454  }
10455  if (preg_match('/\.csv$/i', $tmpfile)) {
10456  $mime = 'text/csv';
10457  $imgmime = 'text.png';
10458  $famime = 'file-text-o';
10459  }
10460  if (preg_match('/\.tsv$/i', $tmpfile)) {
10461  $mime = 'text/tab-separated-values';
10462  $imgmime = 'text.png';
10463  $famime = 'file-text-o';
10464  }
10465  if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
10466  $mime = 'text/plain';
10467  $imgmime = 'text.png';
10468  $famime = 'file-text-o';
10469  }
10470  if (preg_match('/\.ini$/i', $tmpfile)) {
10471  $mime = 'text/plain';
10472  $imgmime = 'text.png';
10473  $srclang = 'ini';
10474  $famime = 'file-text-o';
10475  }
10476  if (preg_match('/\.md$/i', $tmpfile)) {
10477  $mime = 'text/plain';
10478  $imgmime = 'text.png';
10479  $srclang = 'md';
10480  $famime = 'file-text-o';
10481  }
10482  if (preg_match('/\.css$/i', $tmpfile)) {
10483  $mime = 'text/css';
10484  $imgmime = 'css.png';
10485  $srclang = 'css';
10486  $famime = 'file-text-o';
10487  }
10488  if (preg_match('/\.lang$/i', $tmpfile)) {
10489  $mime = 'text/plain';
10490  $imgmime = 'text.png';
10491  $srclang = 'lang';
10492  $famime = 'file-text-o';
10493  }
10494  // Certificate files
10495  if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) {
10496  $mime = 'text/plain';
10497  $imgmime = 'text.png';
10498  $famime = 'file-text-o';
10499  }
10500  // XML based (HTML/XML/XAML)
10501  if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) {
10502  $mime = 'text/html';
10503  $imgmime = 'html.png';
10504  $srclang = 'html';
10505  $famime = 'file-text-o';
10506  }
10507  if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
10508  $mime = 'text/xml';
10509  $imgmime = 'other.png';
10510  $srclang = 'xml';
10511  $famime = 'file-text-o';
10512  }
10513  if (preg_match('/\.xaml$/i', $tmpfile)) {
10514  $mime = 'text/xml';
10515  $imgmime = 'other.png';
10516  $srclang = 'xaml';
10517  $famime = 'file-text-o';
10518  }
10519  // Languages
10520  if (preg_match('/\.bas$/i', $tmpfile)) {
10521  $mime = 'text/plain';
10522  $imgmime = 'text.png';
10523  $srclang = 'bas';
10524  $famime = 'file-code-o';
10525  }
10526  if (preg_match('/\.(c)$/i', $tmpfile)) {
10527  $mime = 'text/plain';
10528  $imgmime = 'text.png';
10529  $srclang = 'c';
10530  $famime = 'file-code-o';
10531  }
10532  if (preg_match('/\.(cpp)$/i', $tmpfile)) {
10533  $mime = 'text/plain';
10534  $imgmime = 'text.png';
10535  $srclang = 'cpp';
10536  $famime = 'file-code-o';
10537  }
10538  if (preg_match('/\.cs$/i', $tmpfile)) {
10539  $mime = 'text/plain';
10540  $imgmime = 'text.png';
10541  $srclang = 'cs';
10542  $famime = 'file-code-o';
10543  }
10544  if (preg_match('/\.(h)$/i', $tmpfile)) {
10545  $mime = 'text/plain';
10546  $imgmime = 'text.png';
10547  $srclang = 'h';
10548  $famime = 'file-code-o';
10549  }
10550  if (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
10551  $mime = 'text/plain';
10552  $imgmime = 'text.png';
10553  $srclang = 'java';
10554  $famime = 'file-code-o';
10555  }
10556  if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
10557  $mime = 'text/plain';
10558  $imgmime = 'php.png';
10559  $srclang = 'php';
10560  $famime = 'file-code-o';
10561  }
10562  if (preg_match('/\.phtml$/i', $tmpfile)) {
10563  $mime = 'text/plain';
10564  $imgmime = 'php.png';
10565  $srclang = 'php';
10566  $famime = 'file-code-o';
10567  }
10568  if (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
10569  $mime = 'text/plain';
10570  $imgmime = 'pl.png';
10571  $srclang = 'perl';
10572  $famime = 'file-code-o';
10573  }
10574  if (preg_match('/\.sql$/i', $tmpfile)) {
10575  $mime = 'text/plain';
10576  $imgmime = 'text.png';
10577  $srclang = 'sql';
10578  $famime = 'file-code-o';
10579  }
10580  if (preg_match('/\.js$/i', $tmpfile)) {
10581  $mime = 'text/x-javascript';
10582  $imgmime = 'jscript.png';
10583  $srclang = 'js';
10584  $famime = 'file-code-o';
10585  }
10586  // Open office
10587  if (preg_match('/\.odp$/i', $tmpfile)) {
10588  $mime = 'application/vnd.oasis.opendocument.presentation';
10589  $imgmime = 'ooffice.png';
10590  $famime = 'file-powerpoint-o';
10591  }
10592  if (preg_match('/\.ods$/i', $tmpfile)) {
10593  $mime = 'application/vnd.oasis.opendocument.spreadsheet';
10594  $imgmime = 'ooffice.png';
10595  $famime = 'file-excel-o';
10596  }
10597  if (preg_match('/\.odt$/i', $tmpfile)) {
10598  $mime = 'application/vnd.oasis.opendocument.text';
10599  $imgmime = 'ooffice.png';
10600  $famime = 'file-word-o';
10601  }
10602  // MS Office
10603  if (preg_match('/\.mdb$/i', $tmpfile)) {
10604  $mime = 'application/msaccess';
10605  $imgmime = 'mdb.png';
10606  $famime = 'file-o';
10607  }
10608  if (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
10609  $mime = 'application/msword';
10610  $imgmime = 'doc.png';
10611  $famime = 'file-word-o';
10612  }
10613  if (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
10614  $mime = 'application/msword';
10615  $imgmime = 'doc.png';
10616  $famime = 'file-word-o';
10617  }
10618  if (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
10619  $mime = 'application/vnd.ms-excel';
10620  $imgmime = 'xls.png';
10621  $famime = 'file-excel-o';
10622  }
10623  if (preg_match('/\.xla(m)?$/i', $tmpfile)) {
10624  $mime = 'application/vnd.ms-excel';
10625  $imgmime = 'xls.png';
10626  $famime = 'file-excel-o';
10627  }
10628  if (preg_match('/\.xls$/i', $tmpfile)) {
10629  $mime = 'application/vnd.ms-excel';
10630  $imgmime = 'xls.png';
10631  $famime = 'file-excel-o';
10632  }
10633  if (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
10634  $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
10635  $imgmime = 'xls.png';
10636  $famime = 'file-excel-o';
10637  }
10638  if (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
10639  $mime = 'application/vnd.ms-powerpoint';
10640  $imgmime = 'ppt.png';
10641  $famime = 'file-powerpoint-o';
10642  }
10643  if (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
10644  $mime = 'application/x-mspowerpoint';
10645  $imgmime = 'ppt.png';
10646  $famime = 'file-powerpoint-o';
10647  }
10648  // Other
10649  if (preg_match('/\.pdf$/i', $tmpfile)) {
10650  $mime = 'application/pdf';
10651  $imgmime = 'pdf.png';
10652  $famime = 'file-pdf-o';
10653  }
10654  // Scripts
10655  if (preg_match('/\.bat$/i', $tmpfile)) {
10656  $mime = 'text/x-bat';
10657  $imgmime = 'script.png';
10658  $srclang = 'dos';
10659  $famime = 'file-code-o';
10660  }
10661  if (preg_match('/\.sh$/i', $tmpfile)) {
10662  $mime = 'text/x-sh';
10663  $imgmime = 'script.png';
10664  $srclang = 'bash';
10665  $famime = 'file-code-o';
10666  }
10667  if (preg_match('/\.ksh$/i', $tmpfile)) {
10668  $mime = 'text/x-ksh';
10669  $imgmime = 'script.png';
10670  $srclang = 'bash';
10671  $famime = 'file-code-o';
10672  }
10673  if (preg_match('/\.bash$/i', $tmpfile)) {
10674  $mime = 'text/x-bash';
10675  $imgmime = 'script.png';
10676  $srclang = 'bash';
10677  $famime = 'file-code-o';
10678  }
10679  // Images
10680  if (preg_match('/\.ico$/i', $tmpfile)) {
10681  $mime = 'image/x-icon';
10682  $imgmime = 'image.png';
10683  $famime = 'file-image-o';
10684  }
10685  if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
10686  $mime = 'image/jpeg';
10687  $imgmime = 'image.png';
10688  $famime = 'file-image-o';
10689  }
10690  if (preg_match('/\.png$/i', $tmpfile)) {
10691  $mime = 'image/png';
10692  $imgmime = 'image.png';
10693  $famime = 'file-image-o';
10694  }
10695  if (preg_match('/\.gif$/i', $tmpfile)) {
10696  $mime = 'image/gif';
10697  $imgmime = 'image.png';
10698  $famime = 'file-image-o';
10699  }
10700  if (preg_match('/\.bmp$/i', $tmpfile)) {
10701  $mime = 'image/bmp';
10702  $imgmime = 'image.png';
10703  $famime = 'file-image-o';
10704  }
10705  if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
10706  $mime = 'image/tiff';
10707  $imgmime = 'image.png';
10708  $famime = 'file-image-o';
10709  }
10710  if (preg_match('/\.svg$/i', $tmpfile)) {
10711  $mime = 'image/svg+xml';
10712  $imgmime = 'image.png';
10713  $famime = 'file-image-o';
10714  }
10715  if (preg_match('/\.webp$/i', $tmpfile)) {
10716  $mime = 'image/webp';
10717  $imgmime = 'image.png';
10718  $famime = 'file-image-o';
10719  }
10720  // Calendar
10721  if (preg_match('/\.vcs$/i', $tmpfile)) {
10722  $mime = 'text/calendar';
10723  $imgmime = 'other.png';
10724  $famime = 'file-text-o';
10725  }
10726  if (preg_match('/\.ics$/i', $tmpfile)) {
10727  $mime = 'text/calendar';
10728  $imgmime = 'other.png';
10729  $famime = 'file-text-o';
10730  }
10731  // Other
10732  if (preg_match('/\.torrent$/i', $tmpfile)) {
10733  $mime = 'application/x-bittorrent';
10734  $imgmime = 'other.png';
10735  $famime = 'file-o';
10736  }
10737  // Audio
10738  if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) {
10739  $mime = 'audio';
10740  $imgmime = 'audio.png';
10741  $famime = 'file-audio-o';
10742  }
10743  // Video
10744  if (preg_match('/\.mp4$/i', $tmpfile)) {
10745  $mime = 'video/mp4';
10746  $imgmime = 'video.png';
10747  $famime = 'file-video-o';
10748  }
10749  if (preg_match('/\.ogv$/i', $tmpfile)) {
10750  $mime = 'video/ogg';
10751  $imgmime = 'video.png';
10752  $famime = 'file-video-o';
10753  }
10754  if (preg_match('/\.webm$/i', $tmpfile)) {
10755  $mime = 'video/webm';
10756  $imgmime = 'video.png';
10757  $famime = 'file-video-o';
10758  }
10759  if (preg_match('/\.avi$/i', $tmpfile)) {
10760  $mime = 'video/x-msvideo';
10761  $imgmime = 'video.png';
10762  $famime = 'file-video-o';
10763  }
10764  if (preg_match('/\.divx$/i', $tmpfile)) {
10765  $mime = 'video/divx';
10766  $imgmime = 'video.png';
10767  $famime = 'file-video-o';
10768  }
10769  if (preg_match('/\.xvid$/i', $tmpfile)) {
10770  $mime = 'video/xvid';
10771  $imgmime = 'video.png';
10772  $famime = 'file-video-o';
10773  }
10774  if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
10775  $mime = 'video';
10776  $imgmime = 'video.png';
10777  $famime = 'file-video-o';
10778  }
10779  // Archive
10780  if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) {
10781  $mime = 'archive';
10782  $imgmime = 'archive.png';
10783  $famime = 'file-archive-o';
10784  } // application/xxx where zzz is zip, ...
10785  // Exe
10786  if (preg_match('/\.(exe|com)$/i', $tmpfile)) {
10787  $mime = 'application/octet-stream';
10788  $imgmime = 'other.png';
10789  $famime = 'file-o';
10790  }
10791  // Lib
10792  if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) {
10793  $mime = 'library';
10794  $imgmime = 'library.png';
10795  $famime = 'file-o';
10796  }
10797  // Err
10798  if (preg_match('/\.err$/i', $tmpfile)) {
10799  $mime = 'error';
10800  $imgmime = 'error.png';
10801  $famime = 'file-text-o';
10802  }
10803 
10804  // Return string
10805  if ($mode == 1) {
10806  $tmp = explode('/', $mime);
10807  return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
10808  }
10809  if ($mode == 2) {
10810  return $imgmime;
10811  }
10812  if ($mode == 3) {
10813  return $srclang;
10814  }
10815  if ($mode == 4) {
10816  return $famime;
10817  }
10818  return $mime;
10819 }
10820 
10832 function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
10833 {
10834  global $conf, $db;
10835 
10836  $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
10837 
10838  $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
10839 
10840  if (is_null($dictvalues)) {
10841  $dictvalues = array();
10842 
10843  $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
10844  if ($checkentity) {
10845  $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
10846  }
10847 
10848  $resql = $db->query($sql);
10849  if ($resql) {
10850  while ($obj = $db->fetch_object($resql)) {
10851  $dictvalues[$obj->{$rowidfield}] = $obj; // $obj is stdClass
10852  }
10853  } else {
10854  dol_print_error($db);
10855  }
10856 
10857  $conf->cache['dictvalues_'.$tablename] = $dictvalues;
10858  }
10859 
10860  if (!empty($dictvalues[$id])) {
10861  // Found
10862  $tmp = $dictvalues[$id];
10863  return (property_exists($tmp, $field) ? $tmp->$field : '');
10864  } else {
10865  // Not found
10866  return '';
10867  }
10868 }
10869 
10876 function colorIsLight($stringcolor)
10877 {
10878  $stringcolor = str_replace('#', '', $stringcolor);
10879  $res = -1;
10880  if (!empty($stringcolor)) {
10881  $res = 0;
10882  $tmp = explode(',', $stringcolor);
10883  if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
10884  $r = $tmp[0];
10885  $g = $tmp[1];
10886  $b = $tmp[2];
10887  } else {
10888  $hexr = $stringcolor[0].$stringcolor[1];
10889  $hexg = $stringcolor[2].$stringcolor[3];
10890  $hexb = $stringcolor[4].$stringcolor[5];
10891  $r = hexdec($hexr);
10892  $g = hexdec($hexg);
10893  $b = hexdec($hexb);
10894  }
10895  $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
10896  if ($bright > 0.6) {
10897  $res = 1;
10898  }
10899  }
10900  return $res;
10901 }
10902 
10911 function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
10912 {
10913  global $conf;
10914 
10915  //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
10916  //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
10917  if (empty($menuentry['enabled'])) {
10918  return 0; // Entry disabled by condition
10919  }
10920  if ($type_user && $menuentry['module']) {
10921  $tmploops = explode('|', $menuentry['module']);
10922  $found = 0;
10923  foreach ($tmploops as $tmploop) {
10924  if (in_array($tmploop, $listofmodulesforexternal)) {
10925  $found++;
10926  break;
10927  }
10928  }
10929  if (!$found) {
10930  return 0; // Entry is for menus all excluded to external users
10931  }
10932  }
10933  if (!$menuentry['perms'] && $type_user) {
10934  return 0; // No permissions and user is external
10935  }
10936  if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) {
10937  return 0; // No permissions and option to hide when not allowed, even for internal user, is on
10938  }
10939  if (!$menuentry['perms']) {
10940  return 2; // No permissions and user is external
10941  }
10942  return 1;
10943 }
10944 
10952 function roundUpToNextMultiple($n, $x = 5)
10953 {
10954  return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
10955 }
10956 
10968 function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
10969 {
10970  $attr = array(
10971  'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css'])
10972  );
10973 
10974  if (empty($html)) {
10975  $html = $label;
10976  }
10977 
10978  if (!empty($url)) {
10979  $attr['href'] = $url;
10980  }
10981 
10982  if ($mode === 'dot') {
10983  $attr['class'] .= ' classfortooltip';
10984  $attr['title'] = $html;
10985  $attr['aria-label'] = $label;
10986  $html = '';
10987  }
10988 
10989  // Override attr
10990  if (!empty($params['attr']) && is_array($params['attr'])) {
10991  foreach ($params['attr'] as $key => $value) {
10992  if ($key == 'class') {
10993  $attr['class'] .= ' '.$value;
10994  } elseif ($key == 'classOverride') {
10995  $attr['class'] = $value;
10996  } else {
10997  $attr[$key] = $value;
10998  }
10999  }
11000  }
11001 
11002  // TODO: add hook
11003 
11004  // escape all attribute
11005  $attr = array_map('dol_escape_htmltag', $attr);
11006 
11007  $TCompiledAttr = array();
11008  foreach ($attr as $key => $value) {
11009  $TCompiledAttr[] = $key.'="'.$value.'"';
11010  }
11011 
11012  $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
11013 
11014  $tag = !empty($url) ? 'a' : 'span';
11015 
11016  return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
11017 }
11018 
11019 
11032 function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
11033 {
11034  global $conf;
11035 
11036  $return = '';
11037  $dolGetBadgeParams = array();
11038 
11039  if (!empty($params['badgeParams'])) {
11040  $dolGetBadgeParams = $params['badgeParams'];
11041  }
11042 
11043  // TODO : add a hook
11044  if ($displayMode == 0) {
11045  $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
11046  } elseif ($displayMode == 1) {
11047  $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
11048  } elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) {
11049  // Use status with images (for backward compatibility)
11050  $return = '';
11051  $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
11052  $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>' : '');
11053 
11054  // For small screen, we always use the short label instead of long label.
11055  if (!empty($conf->dol_optimize_smallscreen)) {
11056  if ($displayMode == 0) {
11057  $displayMode = 1;
11058  } elseif ($displayMode == 4) {
11059  $displayMode = 2;
11060  } elseif ($displayMode == 6) {
11061  $displayMode = 5;
11062  }
11063  }
11064 
11065  // For backward compatibility. Image's filename are still in French, so we use this array to convert
11066  $statusImg = array(
11067  'status0' => 'statut0',
11068  'status1' => 'statut1',
11069  'status2' => 'statut2',
11070  'status3' => 'statut3',
11071  'status4' => 'statut4',
11072  'status5' => 'statut5',
11073  'status6' => 'statut6',
11074  'status7' => 'statut7',
11075  'status8' => 'statut8',
11076  'status9' => 'statut9'
11077  );
11078 
11079  if (!empty($statusImg[$statusType])) {
11080  $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
11081  } else {
11082  $htmlImg = img_picto($statusLabel, $statusType);
11083  }
11084 
11085  if ($displayMode === 2) {
11086  $return = $htmlImg.' '.$htmlLabelShort;
11087  } elseif ($displayMode === 3) {
11088  $return = $htmlImg;
11089  } elseif ($displayMode === 4) {
11090  $return = $htmlImg.' '.$htmlLabel;
11091  } elseif ($displayMode === 5) {
11092  $return = $htmlLabelShort.' '.$htmlImg;
11093  } else { // $displayMode >= 6
11094  $return = $htmlLabel.' '.$htmlImg;
11095  }
11096  } elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) {
11097  // Use new badge
11098  $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
11099 
11100  $dolGetBadgeParams['attr']['class'] = 'badge-status';
11101  $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
11102 
11103  if ($displayMode == 3) {
11104  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
11105  } elseif ($displayMode === 5) {
11106  $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
11107  } else {
11108  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
11109  }
11110  }
11111 
11112  return $return;
11113 }
11114 
11115 
11150 function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
11151 {
11152  global $hookmanager, $action, $object, $langs;
11153 
11154  // If $url is an array, we must build a dropdown button
11155  if (is_array($url)) {
11156  $out = '<div class="dropdown inline-block dropdown-holder">';
11157  $out .= '<a style="margin-right: auto;" class="dropdown-toggle butAction" data-toggle="dropdown">'.$label.'</a>';
11158  $out .= '<div class="dropdown-content">';
11159  foreach ($url as $subbutton) {
11160  if ($subbutton['enabled'] && $subbutton['perm']) {
11161  if (!empty($subbutton['lang'])) {
11162  $langs->load($subbutton['lang']);
11163  }
11164  $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', DOL_URL_ROOT.$subbutton['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage'])), '', 1, array('isDropDown' => true));
11165  }
11166  }
11167  $out .= "</div>";
11168  $out .= "</div>";
11169 
11170  return $out;
11171  }
11172 
11173  // If $url is a simple link
11174  if (!empty($params['isDropdown']))
11175  $class = "dropdown-item";
11176  else {
11177  $class = 'butAction';
11178  if ($actionType == 'danger' || $actionType == 'delete') {
11179  $class = 'butActionDelete';
11180  if (!empty($url) && strpos($url, 'token=') === false) $url .= '&token='.newToken();
11181  }
11182  }
11183  $attr = array(
11184  'class' => $class,
11185  'href' => empty($url) ? '' : $url,
11186  'title' => $label
11187  );
11188 
11189  if (empty($text)) {
11190  $text = $label;
11191  $attr['title'] = ''; // if html not set, leave label on title is redundant
11192  } else {
11193  $attr['title'] = $label;
11194  $attr['aria-label'] = $label;
11195  }
11196 
11197  if (empty($userRight)) {
11198  $attr['class'] = 'butActionRefused';
11199  $attr['href'] = '';
11200  $attr['title'] = (($label && $text && $label != $text) ? $label : $langs->trans('NotEnoughPermissions'));
11201  }
11202 
11203  if (!empty($id)) {
11204  $attr['id'] = $id;
11205  }
11206 
11207  // Override attr
11208  if (!empty($params['attr']) && is_array($params['attr'])) {
11209  foreach ($params['attr'] as $key => $value) {
11210  if ($key == 'class') {
11211  $attr['class'] .= ' '.$value;
11212  } elseif ($key == 'classOverride') {
11213  $attr['class'] = $value;
11214  } else {
11215  $attr[$key] = $value;
11216  }
11217  }
11218  }
11219 
11220  // automatic add tooltip when title is detected
11221  if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
11222  $attr['class'].= ' classfortooltip';
11223  }
11224 
11225  // Js Confirm button
11226  if ($userRight && !empty($params['confirm'])) {
11227  if (!is_array($params['confirm'])) {
11228  $params['confirm'] = array();
11229  }
11230 
11231  if (empty($params['confirm']['url'])) {
11232  $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
11233  }
11234 
11235  // for js desabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
11236  $attr['data-confirm-url'] = $params['confirm']['url'];
11237  $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
11238  $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
11239  $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
11240  $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
11241  $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
11242  $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
11243 
11244  $attr['class'].= ' butActionConfirm';
11245  }
11246 
11247  if (isset($attr['href']) && empty($attr['href'])) {
11248  unset($attr['href']);
11249  }
11250 
11251  // escape all attribute
11252  $attr = array_map('dol_escape_htmltag', $attr);
11253 
11254  $TCompiledAttr = array();
11255  foreach ($attr as $key => $value) {
11256  $TCompiledAttr[] = $key.'= "'.$value.'"';
11257  }
11258 
11259  $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
11260 
11261  $tag = !empty($attr['href']) ? 'a' : 'span';
11262 
11263 
11264  $parameters = array(
11265  'TCompiledAttr' => $TCompiledAttr, // array
11266  'compiledAttributes' => $compiledAttributes, // string
11267  'attr' => $attr,
11268  'tag' => $tag,
11269  'label' => $label,
11270  'html' => $text,
11271  'actionType' => $actionType,
11272  'url' => $url,
11273  'id' => $id,
11274  'userRight' => $userRight,
11275  'params' => $params
11276  );
11277 
11278  $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
11279  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
11280 
11281  if (empty($reshook)) {
11282  if (dol_textishtml($text)) { // If content already HTML encoded
11283  return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
11284  } else {
11285  return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
11286  }
11287  } else {
11288  return $hookmanager->resPrint;
11289  }
11290 }
11291 
11298 function dolGetButtonTitleSeparator($moreClass = "")
11299 {
11300  return '<span class="button-title-separator '.$moreClass.'" ></span>';
11301 }
11302 
11309 function getFieldErrorIcon($fieldValidationErrorMsg)
11310 {
11311  $out = '';
11312  if (!empty($fieldValidationErrorMsg)) {
11313  $out.= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
11314  $out.= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
11315  $out.= '</span>';
11316  }
11317 
11318  return $out;
11319 }
11320 
11333 function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
11334 {
11335  global $langs, $conf, $user;
11336 
11337  // Actually this conf is used in css too for external module compatibility and smooth transition to this function
11338  if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) {
11339  return '';
11340  }
11341 
11342  $class = 'btnTitle';
11343  if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
11344  $class .= ' btnTitlePlus';
11345  }
11346  $useclassfortooltip = 1;
11347 
11348  if (!empty($params['morecss'])) {
11349  $class .= ' '.$params['morecss'];
11350  }
11351 
11352  $attr = array(
11353  'class' => $class,
11354  'href' => empty($url) ? '' : $url
11355  );
11356 
11357  if (!empty($helpText)) {
11358  $attr['title'] = dol_escape_htmltag($helpText);
11359  } elseif (empty($attr['title']) && $label) {
11360  $attr['title'] = $label;
11361  $useclassfortooltip = 0;
11362  }
11363 
11364  if ($status == 2) {
11365  $attr['class'] .= ' btnTitleSelected';
11366  } elseif ($status <= 0) {
11367  $attr['class'] .= ' refused';
11368 
11369  $attr['href'] = '';
11370 
11371  if ($status == -1) { // disable
11372  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
11373  } elseif ($status == 0) { // Not enough permissions
11374  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
11375  }
11376  }
11377 
11378  if (!empty($attr['title']) && $useclassfortooltip) {
11379  $attr['class'] .= ' classfortooltip';
11380  }
11381 
11382  if (!empty($id)) {
11383  $attr['id'] = $id;
11384  }
11385 
11386  // Override attr
11387  if (!empty($params['attr']) && is_array($params['attr'])) {
11388  foreach ($params['attr'] as $key => $value) {
11389  if ($key == 'class') {
11390  $attr['class'] .= ' '.$value;
11391  } elseif ($key == 'classOverride') {
11392  $attr['class'] = $value;
11393  } else {
11394  $attr[$key] = $value;
11395  }
11396  }
11397  }
11398 
11399  if (isset($attr['href']) && empty($attr['href'])) {
11400  unset($attr['href']);
11401  }
11402 
11403  // TODO : add a hook
11404 
11405  // escape all attribute
11406  $attr = array_map('dol_escape_htmltag', $attr);
11407 
11408  $TCompiledAttr = array();
11409  foreach ($attr as $key => $value) {
11410  $TCompiledAttr[] = $key.'="'.$value.'"';
11411  }
11412 
11413  $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
11414 
11415  $tag = (empty($attr['href']) ? 'span' : 'a');
11416 
11417  $button = '<'.$tag.' '.$compiledAttributes.'>';
11418  $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
11419  if (!empty($params['forcenohideoftext'])) {
11420  $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
11421  }
11422  $button .= '</'.$tag.'>';
11423 
11424  return $button;
11425 }
11426 
11437 function getElementProperties($element_type)
11438 {
11439  global $conf;
11440 
11441  $regs = array();
11442 
11443  $classfile = $classname = $classpath = $subdir = $dir_output = '';
11444 
11445  // Parse element/subelement
11446  $module = $element_type;
11447  $element = $element_type;
11448  $subelement = $element_type;
11449 
11450  // If we ask a resource form external module (instead of default path)
11451  if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) { // 'myobject@mymodule'
11452  $element = $subelement = $regs[1];
11453  $module = $regs[2];
11454  }
11455 
11456  // If we ask a resource for a string with an element and a subelement
11457  // Example 'project_task'
11458  if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
11459  $module = $element = $regs[1];
11460  $subelement = $regs[2];
11461  }
11462 
11463  // For compat and To work with non standard path
11464  if ($element_type == "action") {
11465  $classpath = 'comm/action/class';
11466  $subelement = 'Actioncomm';
11467  $module = 'agenda';
11468  } elseif ($element_type == 'cronjob') {
11469  $classpath = 'cron/class';
11470  $module = 'cron';
11471  } elseif ($element_type == 'adherent_type') {
11472  $classpath = 'adherents/class';
11473  $classfile = 'adherent_type';
11474  $module = 'adherent';
11475  $subelement = 'adherent_type';
11476  $classname = 'AdherentType';
11477  } elseif ($element_type == 'bank_account') {
11478  $classpath = 'compta/bank/class';
11479  $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
11480  $classfile = 'account';
11481  $classname = 'Account';
11482  } elseif ($element_type == 'category') {
11483  $classpath = 'categories/class';
11484  $module = 'categorie';
11485  $subelement = 'categorie';
11486  } elseif ($element_type == 'contact') {
11487  $classpath = 'contact/class';
11488  $classfile = 'contact';
11489  $module = 'societe';
11490  $subelement = 'contact';
11491  } elseif ($element_type == 'stock') {
11492  $classpath = 'product/stock/class';
11493  $classfile = 'entrepot';
11494  $classname = 'Entrepot';
11495  } elseif ($element_type == 'project') {
11496  $classpath = 'projet/class';
11497  $module = 'projet';
11498  } elseif ($element_type == 'project_task') {
11499  $classpath = 'projet/class';
11500  $module = 'projet';
11501  $subelement = 'task';
11502  } elseif ($element_type == 'facture' || $element_type == 'invoice') {
11503  $classpath = 'compta/facture/class';
11504  $module = 'facture';
11505  $subelement = 'facture';
11506  } elseif ($element_type == 'commande' || $element_type == 'order') {
11507  $classpath = 'commande/class';
11508  $module = 'commande';
11509  $subelement = 'commande';
11510  } elseif ($element_type == 'propal') {
11511  $classpath = 'comm/propal/class';
11512  } elseif ($element_type == 'shipping') {
11513  $classpath = 'expedition/class';
11514  $classfile = 'expedition';
11515  $classname = 'Expedition';
11516  $module = 'expedition';
11517  } elseif ($element_type == 'supplier_proposal') {
11518  $classpath = 'supplier_proposal/class';
11519  $module = 'supplier_proposal';
11520  $element = 'supplierproposal';
11521  $classfile = 'supplier_proposal';
11522  $subelement = 'supplierproposal';
11523  } elseif ($element_type == 'shipping') {
11524  $classpath = 'expedition/class';
11525  $subelement = 'expedition';
11526  $module = 'expedition_bon';
11527  } elseif ($element_type == 'delivery') {
11528  $classpath = 'delivery/class';
11529  $subelement = 'delivery';
11530  $module = 'delivery_note';
11531  } elseif ($element_type == 'contract') {
11532  $classpath = 'contrat/class';
11533  $module = 'contrat';
11534  $subelement = 'contrat';
11535  } elseif ($element_type == 'mailing') {
11536  $classpath = 'comm/mailing/class';
11537  $module = 'mailing';
11538  $classfile = 'mailing';
11539  $classname = 'Mailing';
11540  $subelement = '';
11541  } elseif ($element_type == 'member') {
11542  $classpath = 'adherents/class';
11543  $module = 'adherent';
11544  $subelement = 'adherent';
11545  } elseif ($element_type == 'usergroup') {
11546  $classpath = 'user/class';
11547  $module = 'user';
11548  } elseif ($element_type == 'mo') {
11549  $classpath = 'mrp/class';
11550  $classfile = 'mo';
11551  $classname = 'Mo';
11552  $module = 'mrp';
11553  $subelement = '';
11554  } elseif ($element_type == 'cabinetmed_cons') {
11555  $classpath = 'cabinetmed/class';
11556  $module = 'cabinetmed';
11557  $subelement = 'cabinetmedcons';
11558  } elseif ($element_type == 'fichinter') {
11559  $classpath = 'fichinter/class';
11560  $module = 'ficheinter';
11561  $subelement = 'fichinter';
11562  } elseif ($element_type == 'dolresource' || $element_type == 'resource') {
11563  $classpath = 'resource/class';
11564  $module = 'resource';
11565  $subelement = 'dolresource';
11566  } elseif ($element_type == 'propaldet') {
11567  $classpath = 'comm/propal/class';
11568  $module = 'propal';
11569  $subelement = 'propaleligne';
11570  } elseif ($element_type == 'opensurvey_sondage') {
11571  $classpath = 'opensurvey/class';
11572  $module = 'opensurvey';
11573  $subelement = 'opensurveysondage';
11574  } elseif ($element_type == 'order_supplier') {
11575  $classpath = 'fourn/class';
11576  $module = 'fournisseur';
11577  $classfile = 'fournisseur.commande';
11578  $element = 'order_supplier';
11579  $subelement = '';
11580  $classname = 'CommandeFournisseur';
11581  } elseif ($element_type == 'invoice_supplier') {
11582  $classpath = 'fourn/class';
11583  $module = 'fournisseur';
11584  $classfile = 'fournisseur.facture';
11585  $element = 'invoice_supplier';
11586  $subelement = '';
11587  $classname = 'FactureFournisseur';
11588  } elseif ($element_type == "service") {
11589  $classpath = 'product/class';
11590  $subelement = 'product';
11591  } elseif ($element_type == 'salary') {
11592  $classpath = 'salaries/class';
11593  $module = 'salaries';
11594  } elseif ($element_type == 'productlot') {
11595  $module = 'productbatch';
11596  $classpath = 'product/stock/class';
11597  $classfile = 'productlot';
11598  $classname = 'Productlot';
11599  $element = 'productlot';
11600  $subelement = '';
11601  } elseif ($element_type == 'websitepage') {
11602  $classpath = 'website/class';
11603  $classfile = 'websitepage';
11604  $classname = 'Websitepage';
11605  $module = 'website';
11606  $subelement = 'websitepage';
11607  } elseif ($element_type == 'fiscalyear') {
11608  $classpath = 'core/class';
11609  $module = 'accounting';
11610  $subelement = 'fiscalyear';
11611  } elseif ($element_type == 'chargesociales') {
11612  $classpath = 'compta/sociales/class';
11613  $module = 'tax';
11614  } elseif ($element_type == 'tva') {
11615  $classpath = 'compta/tva/class';
11616  $module = 'tax';
11617  $subdir = '/vat';
11618  }
11619 
11620  if (empty($classfile)) {
11621  $classfile = strtolower($subelement);
11622  }
11623  if (empty($classname)) {
11624  $classname = ucfirst($subelement);
11625  }
11626  if (empty($classpath)) {
11627  $classpath = $module.'/class';
11628  }
11629 
11630  //print 'getElementProperties subdir='.$subdir;
11631 
11632  // Set dir_output
11633  if ($module && isset($conf->$module)) { // The generic case
11634  if (!empty($conf->$module->multidir_output[$conf->entity])) {
11635  $dir_output = $conf->$module->multidir_output[$conf->entity];
11636  } elseif (!empty($conf->$module->output[$conf->entity])) {
11637  $dir_output = $conf->$module->output[$conf->entity];
11638  } elseif (!empty($conf->$module->dir_output)) {
11639  $dir_output = $conf->$module->dir_output;
11640  }
11641  }
11642 
11643  // Overwrite value for special cases
11644  if ($element == 'order_supplier') {
11645  $dir_output = $conf->fournisseur->commande->dir_output;
11646  } elseif ($element == 'invoice_supplier') {
11647  $dir_output = $conf->fournisseur->facture->dir_output;
11648  }
11649  $dir_output .= $subdir;
11650 
11651  $element_properties = array(
11652  'module' => $module,
11653  'element' => $element,
11654  'subelement' => $subelement,
11655  'classpath' => $classpath,
11656  'classfile' => $classfile,
11657  'classname' => $classname,
11658  'dir_output' => $dir_output
11659  );
11660  return $element_properties;
11661 }
11662 
11672 function fetchObjectByElement($element_id, $element_type, $element_ref = '')
11673 {
11674  global $db;
11675 
11676  $ret = 0;
11677 
11678  $element_prop = getElementProperties($element_type);
11679 
11680  if (is_array($element_prop) && isModEnabled($element_prop['module'])) {
11681  dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
11682 
11683  if (class_exists($element_prop['classname'])) {
11684  $classname = $element_prop['classname'];
11685  $objecttmp = new $classname($db);
11686  $ret = $objecttmp->fetch($element_id, $element_ref);
11687  if ($ret >= 0) {
11688  if (empty($objecttmp->module)) {
11689  $objecttmp->module = $element_prop['module'];
11690  }
11691 
11692  return $objecttmp;
11693  }
11694  } else {
11695  return -1;
11696  }
11697  }
11698 
11699  return $ret;
11700 }
11701 
11708 function isAFileWithExecutableContent($filename)
11709 {
11710  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)) {
11711  return true;
11712  }
11713 
11714  return false;
11715 }
11716 
11724 function newToken()
11725 {
11726  return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
11727 }
11728 
11736 function currentToken()
11737 {
11738  return isset($_SESSION['token']) ? $_SESSION['token'] : '';
11739 }
11740 
11746 function getNonce()
11747 {
11748  global $conf;
11749 
11750  if (empty($conf->cache['nonce'])) {
11751  $conf->cache['nonce'] = dolGetRandomBytes(8);
11752  }
11753 
11754  return $conf->cache['nonce'];
11755 }
11756 
11757 
11770 function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1)
11771 {
11772  global $langs;
11773 
11774  print '<div class="div-table-responsive-no-min">';
11775  print '<table class="noborder centpercent">';
11776  print '<tr class="liste_titre">';
11777 
11778  print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">';
11779 
11780  print $langs->trans($header);
11781 
11782  // extra space between the first header and the number
11783  if ($number > -1) {
11784  print ' ';
11785  }
11786 
11787  if (!empty($link)) {
11788  if (!empty($arguments)) {
11789  print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11790  } else {
11791  print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
11792  }
11793  }
11794 
11795  if ($number > -1) {
11796  print '<span class="badge">'.$number.'</span>';
11797  }
11798 
11799  if (!empty($link)) {
11800  print '</a>';
11801  }
11802 
11803  print '</th>';
11804 
11805  if ($number < 0 && !empty($link)) {
11806  print '<th class="right">';
11807 
11808  if (!empty($arguments)) {
11809  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
11810  } else {
11811  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">';
11812  }
11813 
11814  print $langs->trans("FullList");
11815  print '</a>';
11816  print '</th>';
11817  }
11818 
11819  print '</tr>';
11820 }
11821 
11830 function finishSimpleTable($addLineBreak = false)
11831 {
11832  print '</table>';
11833  print '</div>';
11834 
11835  if ($addLineBreak) {
11836  print '<br>';
11837  }
11838 }
11839 
11851 function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
11852 {
11853  global $langs;
11854 
11855  if ($num === 0) {
11856  print '<tr class="oddeven">';
11857  print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>';
11858  print '</tr>';
11859  return;
11860  }
11861 
11862  if ($nbofloop === 0) {
11863  // don't show a summary line
11864  return;
11865  }
11866 
11867  if ($num === 0) {
11868  $colspan = $tableColumnCount;
11869  } elseif ($num > $nbofloop) {
11870  $colspan = $tableColumnCount;
11871  } else {
11872  $colspan = $tableColumnCount - 1;
11873  }
11874 
11875  if ($extraRightColumn) {
11876  $colspan--;
11877  }
11878 
11879  print '<tr class="liste_total">';
11880 
11881  if ($nbofloop > 0 && $num > $nbofloop) {
11882  print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
11883  } else {
11884  print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
11885  print '<td class="right" width="100">'.price($total).'</td>';
11886  }
11887 
11888  if ($extraRightColumn) {
11889  print '<td></td>';
11890  }
11891 
11892  print '</tr>';
11893 }
11894 
11903 function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
11904 {
11905  global $conf;
11906 
11907  if ($method == -1) {
11908  $method = 0;
11909  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) {
11910  $method = 1;
11911  }
11912  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) {
11913  $method = 2;
11914  }
11915  }
11916 
11917  // Be sure we don't have output buffering enabled to have readfile working correctly
11918  while (ob_get_level()) {
11919  ob_end_flush();
11920  }
11921 
11922  // Solution 0
11923  if ($method == 0) {
11924  readfile($fullpath_original_file_osencoded);
11925  } elseif ($method == 1) {
11926  // Solution 1
11927  $handle = fopen($fullpath_original_file_osencoded, "rb");
11928  while (!feof($handle)) {
11929  print fread($handle, 8192);
11930  }
11931  fclose($handle);
11932  } elseif ($method == 2) {
11933  // Solution 2
11934  $handle1 = fopen($fullpath_original_file_osencoded, "rb");
11935  $handle2 = fopen("php://output", "wb");
11936  stream_copy_to_stream($handle1, $handle2);
11937  fclose($handle1);
11938  fclose($handle2);
11939  }
11940 }
11941 
11951 function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
11952 {
11953  /*
11954  global $conf;
11955 
11956  if (!empty($conf->dol_no_mouse_hover)) {
11957  $showonlyonhover = 0;
11958  }*/
11959 
11960  $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
11961  if ($texttoshow === 'none') {
11962  $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>';
11963  } elseif ($texttoshow) {
11964  $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>';
11965  } else {
11966  $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>';
11967  }
11968 
11969  return $result;
11970 }
11971 
11972 
11979 function jsonOrUnserialize($stringtodecode)
11980 {
11981  $result = json_decode($stringtodecode);
11982  if ($result === null) {
11983  $result = unserialize($stringtodecode);
11984  }
11985 
11986  return $result;
11987 }
11988 
11989 
12003 function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
12004 {
12005  if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
12006  $filter = '(' . $filter . ')';
12007  }
12008 
12009  $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'
12010 
12011  if (!dolCheckFilters($filter, $errorstr)) {
12012  if ($noerror) {
12013  return '1 = 2';
12014  } else {
12015  return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
12016  }
12017  }
12018 
12019  // Test the filter syntax
12020  $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
12021  $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
12022  // If the string result contains something else than '()', the syntax was wrong
12023  if (preg_match('/[^\‍(\‍)]/', $t)) {
12024  $errorstr = 'Bad syntax of the search string';
12025  if ($noerror) {
12026  return '1 = 2';
12027  } else {
12028  return 'Filter syntax error - '.$errorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
12029  }
12030  }
12031 
12032  return ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
12033 }
12034 
12042 function dolCheckFilters($sqlfilters, &$error = '')
12043 {
12044  //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
12045  //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
12046  $tmp = $sqlfilters;
12047  $i = 0; $nb = strlen($tmp);
12048  $counter = 0;
12049  while ($i < $nb) {
12050  if ($tmp[$i] == '(') {
12051  $counter++;
12052  }
12053  if ($tmp[$i] == ')') {
12054  $counter--;
12055  }
12056  if ($counter < 0) {
12057  $error = "Wrond balance of parenthesis in sqlfilters=".$sqlfilters;
12058  dol_syslog($error, LOG_WARNING);
12059  return false;
12060  }
12061  $i++;
12062  }
12063  return true;
12064 }
12065 
12074 {
12075  //dol_syslog("Convert matches ".$matches[1]);
12076  if (empty($matches[1])) {
12077  return '';
12078  }
12079  $tmp = explode(':', $matches[1]);
12080  if (count($tmp) < 3) {
12081  return '';
12082  }
12083 
12084  return '()'; // An empty criteria
12085 }
12086 
12095 function dolForgeCriteriaCallback($matches)
12096 {
12097  global $db;
12098 
12099  //dol_syslog("Convert matches ".$matches[1]);
12100  if (empty($matches[1])) {
12101  return '';
12102  }
12103  $tmp = explode(':', $matches[1]);
12104  if (count($tmp) < 3) {
12105  return '';
12106  }
12107 
12108  $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
12109 
12110  $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
12111 
12112  if ($operator == 'NOTLIKE') {
12113  $operator = 'NOT LIKE';
12114  }
12115  if ($operator == 'ISNOT') {
12116  $operator = 'IS NOT';
12117  }
12118  if ($operator == '!=') {
12119  $operator = '<>';
12120  }
12121 
12122  $tmpescaped = $tmp[2];
12123  $regbis = array();
12124 
12125  if ($operator == 'IN') { // IN is allowed for list of ID or code only
12126  //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
12127  $tmpescaped = '('.$db->escape($db->sanitize($tmpescaped, 1, 0)).')';
12128  //} else {
12129  // $tmpescaped = $db->escape($db->sanitize($tmpescaped, 1));
12130  //}
12131  } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
12132  if (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
12133  $tmpescaped = $regbis[1];
12134  }
12135  //$tmpescaped = "'".$db->escapeforlike($db->escape($regbis[1]))."'";
12136  $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the like will works
12137  } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
12138  $tmpescaped = "'".$db->escape($regbis[1])."'";
12139  } else {
12140  if (strtoupper($tmpescaped) == 'NULL') {
12141  $tmpescaped = 'NULL';
12142  } elseif (is_int($tmpescaped)) {
12143  $tmpescaped = (int) $tmpescaped;
12144  } else {
12145  $tmpescaped = (float) $tmpescaped;
12146  }
12147  }
12148 
12149  return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
12150 }
12151 
12152 
12161 function getTimelineIcon($actionstatic, &$histo, $key)
12162 {
12163  global $conf, $langs;
12164  $out = '<!-- timeline icon -->'."\n";
12165  $iconClass = 'fa fa-comments';
12166  $img_picto = '';
12167  $colorClass = '';
12168  $pictoTitle = '';
12169 
12170  if ($histo[$key]['percent'] == -1) {
12171  $colorClass = 'timeline-icon-not-applicble';
12172  $pictoTitle = $langs->trans('StatusNotApplicable');
12173  } elseif ($histo[$key]['percent'] == 0) {
12174  $colorClass = 'timeline-icon-todo';
12175  $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
12176  } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
12177  $colorClass = 'timeline-icon-in-progress';
12178  $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
12179  } elseif ($histo[$key]['percent'] >= 100) {
12180  $colorClass = 'timeline-icon-done';
12181  $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
12182  }
12183 
12184  if ($actionstatic->code == 'AC_TICKET_CREATE') {
12185  $iconClass = 'fa fa-ticket';
12186  } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
12187  $iconClass = 'fa fa-pencilxxx';
12188  } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
12189  $iconClass = 'fa fa-comments';
12190  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
12191  $iconClass = 'fa fa-mask';
12192  } elseif (!empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
12193  if ($actionstatic->type_picto) {
12194  $img_picto = img_picto('', $actionstatic->type_picto);
12195  } else {
12196  if ($actionstatic->type_code == 'AC_RDV') {
12197  $iconClass = 'fa fa-handshake';
12198  } elseif ($actionstatic->type_code == 'AC_TEL') {
12199  $iconClass = 'fa fa-phone';
12200  } elseif ($actionstatic->type_code == 'AC_FAX') {
12201  $iconClass = 'fa fa-fax';
12202  } elseif ($actionstatic->type_code == 'AC_EMAIL') {
12203  $iconClass = 'fa fa-envelope';
12204  } elseif ($actionstatic->type_code == 'AC_INT') {
12205  $iconClass = 'fa fa-shipping-fast';
12206  } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
12207  $iconClass = 'fa fa-robot';
12208  } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
12209  $iconClass = 'fa fa-robot';
12210  }
12211  }
12212  }
12213 
12214  $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
12215  return $out;
12216 }
12217 
12224 function getActionCommEcmList($object)
12225 {
12226  global $conf, $db;
12227 
12228  $documents = array();
12229 
12230  $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
12231  $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
12232  $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
12233  //$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
12234  $sql .= ' ORDER BY ecm.position ASC';
12235 
12236  $resql = $db->query($sql);
12237  if ($resql) {
12238  if ($db->num_rows($resql)) {
12239  while ($obj = $db->fetch_object($resql)) {
12240  $documents[$obj->id] = $obj;
12241  }
12242  }
12243  }
12244 
12245  return $documents;
12246 }
12247 
12248 
12249 
12267 function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = '', $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
12268 {
12269  global $user, $conf;
12270  global $form;
12271 
12272  global $param, $massactionbutton;
12273 
12274  dol_include_once('/comm/action/class/actioncomm.class.php');
12275 
12276  // Check parameters
12277  if (!is_object($filterobj) && !is_object($objcon)) {
12278  dol_print_error('', 'BadParameter');
12279  }
12280 
12281  $histo = array();
12282  $numaction = 0;
12283  $now = dol_now();
12284 
12285  $sortfield_list = explode(',', $sortfield);
12286  $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
12287  $sortfield_new_list = array();
12288  foreach ($sortfield_list as $sortfield_value) {
12289  $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
12290  }
12291  $sortfield_new = implode(',', $sortfield_new_list);
12292 
12293  if (isModEnabled('agenda')) {
12294  // Search histo on actioncomm
12295  if (is_object($objcon) && $objcon->id > 0) {
12296  $sql = "SELECT DISTINCT a.id, a.label as label,";
12297  } else {
12298  $sql = "SELECT a.id, a.label as label,";
12299  }
12300  $sql .= " a.datep as dp,";
12301  $sql .= " a.note as message,";
12302  $sql .= " a.datep2 as dp2,";
12303  $sql .= " a.percent as percent, 'action' as type,";
12304  $sql .= " a.fk_element, a.elementtype,";
12305  $sql .= " a.fk_contact,";
12306  $sql .= " a.email_from as msg_from,";
12307  $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
12308  $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";
12309  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
12310  $sql .= ", sp.lastname, sp.firstname";
12311  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12312  $sql .= ", m.lastname, m.firstname";
12313  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12314  $sql .= ", o.ref";
12315  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12316  $sql .= ", o.ref";
12317  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12318  $sql .= ", o.ref";
12319  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
12320  $sql .= ", o.ref";
12321  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
12322  $sql .= ", o.ref";
12323  }
12324  $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
12325  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
12326  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
12327 
12328  $force_filter_contact = false;
12329  if (is_object($objcon) && $objcon->id > 0) {
12330  $force_filter_contact = true;
12331  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
12332  $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
12333  }
12334 
12335  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
12336  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
12337  } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
12338  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
12339  $sql .= " ON er.resource_type = 'dolresource'";
12340  $sql .= " AND er.element_id = a.id";
12341  $sql .= " AND er.resource_id = ".((int) $filterobj->id);
12342  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12343  $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
12344  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12345  $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
12346  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12347  $sql .= ", ".MAIN_DB_PREFIX."product as o";
12348  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12349  $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
12350  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
12351  $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
12352  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
12353  $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
12354  }
12355 
12356  $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
12357  if ($force_filter_contact === false) {
12358  if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
12359  $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
12360  } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
12361  $sql .= " AND a.fk_project = ".((int) $filterobj->id);
12362  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12363  $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
12364  if ($filterobj->id) {
12365  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12366  }
12367  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12368  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
12369  if ($filterobj->id) {
12370  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12371  }
12372  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12373  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
12374  if ($filterobj->id) {
12375  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12376  }
12377  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12378  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
12379  if ($filterobj->id) {
12380  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12381  }
12382  } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
12383  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
12384  if ($filterobj->id) {
12385  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12386  }
12387  } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
12388  $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
12389  if ($filterobj->id) {
12390  $sql .= " AND a.fk_element = ".((int) $filterobj->id);
12391  }
12392  }
12393  }
12394 
12395  // Condition on actioncode
12396  if (!empty($actioncode)) {
12397  if (empty($conf->global->AGENDA_USE_EVENT_TYPE)) {
12398  if ($actioncode == 'AC_NON_AUTO') {
12399  $sql .= " AND c.type != 'systemauto'";
12400  } elseif ($actioncode == 'AC_ALL_AUTO') {
12401  $sql .= " AND c.type = 'systemauto'";
12402  } else {
12403  if ($actioncode == 'AC_OTH') {
12404  $sql .= " AND c.type != 'systemauto'";
12405  } elseif ($actioncode == 'AC_OTH_AUTO') {
12406  $sql .= " AND c.type = 'systemauto'";
12407  }
12408  }
12409  } else {
12410  if ($actioncode == 'AC_NON_AUTO') {
12411  $sql .= " AND c.type != 'systemauto'";
12412  } elseif ($actioncode == 'AC_ALL_AUTO') {
12413  $sql .= " AND c.type = 'systemauto'";
12414  } else {
12415  $sql .= " AND c.code = '".$db->escape($actioncode)."'";
12416  }
12417  }
12418  }
12419  if ($donetodo == 'todo') {
12420  $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
12421  } elseif ($donetodo == 'done') {
12422  $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
12423  }
12424  if (is_array($filters) && $filters['search_agenda_label']) {
12425  $sql .= natural_search('a.label', $filters['search_agenda_label']);
12426  }
12427  }
12428 
12429  // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
12430  if (isModEnabled('mailing') && !empty($objcon->email)
12431  && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
12432  $langs->load("mails");
12433 
12434  $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";
12435  $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
12436  $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
12437  $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
12438  if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
12439  $sql2 .= ", '' as lastname, '' as firstname";
12440  } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
12441  $sql2 .= ", '' as lastname, '' as firstname";
12442  } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
12443  $sql2 .= ", '' as ref";
12444  } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
12445  $sql2 .= ", '' as ref";
12446  } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
12447  $sql2 .= ", '' as ref";
12448  }
12449  $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
12450  $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
12451  $sql2 .= " AND mc.statut = 1";
12452  $sql2 .= " AND u.rowid = m.fk_user_valid";
12453  $sql2 .= " AND mc.fk_mailing=m.rowid";
12454  }
12455 
12456  if (!empty($sql) && !empty($sql2)) {
12457  $sql = $sql." UNION ".$sql2;
12458  } elseif (empty($sql) && !empty($sql2)) {
12459  $sql = $sql2;
12460  }
12461 
12462  // TODO Add limit in nb of results
12463  if ($sql) { // May not be defined if module Agenda is not enabled and mailing module disabled too
12464  $sql .= $db->order($sortfield_new, $sortorder);
12465 
12466  dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
12467  $resql = $db->query($sql);
12468  if ($resql) {
12469  $i = 0;
12470  $num = $db->num_rows($resql);
12471 
12472  while ($i < $num) {
12473  $obj = $db->fetch_object($resql);
12474 
12475  if ($obj->type == 'action') {
12476  $contactaction = new ActionComm($db);
12477  $contactaction->id = $obj->id;
12478  $result = $contactaction->fetchResources();
12479  if ($result < 0) {
12480  dol_print_error($db);
12481  setEventMessage("actions.lib::show_actions_messaging Error fetch ressource", 'errors');
12482  }
12483 
12484  //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
12485  //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
12486  $tododone = '';
12487  if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
12488  $tododone = 'todo';
12489  }
12490 
12491  $histo[$numaction] = array(
12492  'type'=>$obj->type,
12493  'tododone'=>$tododone,
12494  'id'=>$obj->id,
12495  'datestart'=>$db->jdate($obj->dp),
12496  'dateend'=>$db->jdate($obj->dp2),
12497  'note'=>$obj->label,
12498  'message'=>$obj->message,
12499  'percent'=>$obj->percent,
12500 
12501  'userid'=>$obj->user_id,
12502  'login'=>$obj->user_login,
12503  'userfirstname'=>$obj->user_firstname,
12504  'userlastname'=>$obj->user_lastname,
12505  'userphoto'=>$obj->user_photo,
12506  'msg_from'=>$obj->msg_from,
12507 
12508  'contact_id'=>$obj->fk_contact,
12509  'socpeopleassigned' => $contactaction->socpeopleassigned,
12510  'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
12511  'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
12512  'fk_element'=>$obj->fk_element,
12513  'elementtype'=>$obj->elementtype,
12514  // Type of event
12515  'acode'=>$obj->acode,
12516  'alabel'=>$obj->alabel,
12517  'libelle'=>$obj->alabel, // deprecated
12518  'apicto'=>$obj->apicto
12519  );
12520  } else {
12521  $histo[$numaction] = array(
12522  'type'=>$obj->type,
12523  'tododone'=>'done',
12524  'id'=>$obj->id,
12525  'datestart'=>$db->jdate($obj->dp),
12526  'dateend'=>$db->jdate($obj->dp2),
12527  'note'=>$obj->label,
12528  'message'=>$obj->message,
12529  'percent'=>$obj->percent,
12530  'acode'=>$obj->acode,
12531 
12532  'userid'=>$obj->user_id,
12533  'login'=>$obj->user_login,
12534  'userfirstname'=>$obj->user_firstname,
12535  'userlastname'=>$obj->user_lastname,
12536  'userphoto'=>$obj->user_photo
12537  );
12538  }
12539 
12540  $numaction++;
12541  $i++;
12542  }
12543  } else {
12544  dol_print_error($db);
12545  }
12546  }
12547 
12548  // Set $out to show events
12549  $out = '';
12550 
12551  if (!isModEnabled('agenda')) {
12552  $langs->loadLangs(array("admin", "errors"));
12553  $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
12554  }
12555 
12556  if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
12557  $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
12558 
12559  require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
12560  include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12561  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
12562  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
12563 
12564  $formactions = new FormActions($db);
12565 
12566  $actionstatic = new ActionComm($db);
12567  $userstatic = new User($db);
12568  $contactstatic = new Contact($db);
12569  $userGetNomUrlCache = array();
12570  $contactGetNomUrlCache = array();
12571 
12572  $out .= '<div class="filters-container" >';
12573  $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
12574  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
12575 
12576  if ($objcon && get_class($objcon) == 'Contact' &&
12577  (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
12578  $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
12579  } else {
12580  $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
12581  }
12582  if ($filterobj && get_class($filterobj) == 'Societe') {
12583  $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
12584  }
12585 
12586  $out .= "\n";
12587 
12588  $out .= '<div class="div-table-responsive-no-min">';
12589  $out .= '<table class="noborder borderbottom centpercent">';
12590 
12591  $out .= '<tr class="liste_titre">';
12592 
12593  // Action column
12594  if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
12595  $out .= '<th class="liste_titre width50 middle">';
12596  $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
12597  $out .= $searchpicto;
12598  $out .= '</th>';
12599  }
12600 
12601  $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
12602 
12603  $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
12604  if ($donetodo) {
12605  $out .= '<th class="liste_titre"></th>';
12606  }
12607  $out .= '<th class="liste_titre">';
12608  $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
12609  //$out .= img_picto($langs->trans("Type"), 'type');
12610  $out .= $formactions->select_type_actions($actioncode, "actioncode", '', empty($conf->global->AGENDA_USE_EVENT_TYPE) ? 1 : -1, 0, 0, 1, 'minwidth200imp');
12611  $out .= '</th>';
12612  $out .= '<th class="liste_titre maxwidth100onsmartphone">';
12613  $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
12614  $out .= '</th>';
12615 
12616  // Action column
12617  if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
12618  $out .= '<th class="liste_titre width50 middle">';
12619  $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
12620  $out .= $searchpicto;
12621  $out .= '</th>';
12622  }
12623 
12624  $out .= '</tr>';
12625 
12626 
12627  $out .= '</table>';
12628 
12629  $out .= '</form>';
12630  $out .= '</div>';
12631 
12632  $out .= "\n";
12633 
12634  $out .= '<ul class="timeline">';
12635 
12636  if ($donetodo) {
12637  $tmp = '';
12638  if (get_class($filterobj) == 'Societe') {
12639  $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
12640  }
12641  $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
12642  $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
12643  $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
12644  //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
12645  if (get_class($filterobj) == 'Societe') {
12646  $tmp .= '</a>';
12647  }
12648  $out .= getTitleFieldOfList($tmp);
12649  }
12650 
12651 
12652  //require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
12653  //$caction=new CActionComm($db);
12654  //$arraylist=$caction->liste_array(1, 'code', '', (empty($conf->global->AGENDA_USE_EVENT_TYPE)?1:0), '', 1);
12655 
12656  $actualCycleDate = false;
12657 
12658  // Loop on each event to show it
12659  foreach ($histo as $key => $value) {
12660  $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
12661 
12662  $actionstatic->type_picto = $histo[$key]['apicto'];
12663  $actionstatic->type_code = $histo[$key]['acode'];
12664 
12665  $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
12666 
12667  $tmpa = dol_getdate($histo[$key]['datestart'], false);
12668  if ($actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
12669  $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
12670  $out .= '<!-- timeline time label -->';
12671  $out .= '<li class="time-label">';
12672  $out .= '<span class="timeline-badge-date">';
12673  $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
12674  $out .= '</span>';
12675  $out .= '</li>';
12676  $out .= '<!-- /.timeline-label -->';
12677  }
12678 
12679 
12680  $out .= '<!-- timeline item -->'."\n";
12681  $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
12682 
12683  $out .= getTimelineIcon($actionstatic, $histo, $key);
12684 
12685  $out .= '<div class="timeline-item">'."\n";
12686 
12687  $out .= '<span class="timeline-header-action">';
12688 
12689  if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
12690  $out .= '<a class="timeline-btn" href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12691  $out .= $histo[$key]['id'];
12692  $out .= '</a> ';
12693  } else {
12694  $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
12695  }
12696 
12697  if ($user->hasRight('agenda', 'allactions', 'create') ||
12698  (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
12699  $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>';
12700  }
12701 
12702  $out .= '</span>';
12703  // Date
12704  $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
12705  $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
12706  if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
12707  $tmpa = dol_getdate($histo[$key]['datestart'], true);
12708  $tmpb = dol_getdate($histo[$key]['dateend'], true);
12709  if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
12710  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
12711  } else {
12712  $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
12713  }
12714  }
12715  $late = 0;
12716  if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12717  $late = 1;
12718  }
12719  if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12720  $late = 1;
12721  }
12722  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
12723  $late = 1;
12724  }
12725  if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
12726  $late = 1;
12727  }
12728  if ($late) {
12729  $out .= img_warning($langs->trans("Late")).' ';
12730  }
12731  $out .= "</span></span>\n";
12732 
12733  // Ref
12734  $out .= '<h3 class="timeline-header">';
12735 
12736  // Author of event
12737  $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
12738  if ($histo[$key]['userid'] > 0) {
12739  if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
12740  $userstatic->fetch($histo[$key]['userid']);
12741  $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
12742  }
12743  $out .= $userGetNomUrlCache[$histo[$key]['userid']];
12744  } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
12745  if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
12746  if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
12747  $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
12748  } else {
12749  $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
12750  }
12751  }
12752  $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
12753  }
12754  $out .= '</div>';
12755 
12756  // Title
12757  $libelle = '';
12758  $out .= ' <div class="messaging-title inline-block">';
12759 
12760  if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
12761  $out .= $langs->trans('TicketNewMessage');
12762  } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
12763  $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
12764  } elseif (isset($histo[$key]['type'])) {
12765  if ($histo[$key]['type'] == 'action') {
12766  $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
12767  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
12768  $libelle = $histo[$key]['note'];
12769  $actionstatic->id = $histo[$key]['id'];
12770  $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
12771  } elseif ($histo[$key]['type'] == 'mailing') {
12772  $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
12773  $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
12774  $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
12775  $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
12776  } else {
12777  $libelle .= $histo[$key]['note'];
12778  $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
12779  }
12780  }
12781 
12782  $out .= '</div>';
12783 
12784  $out .= '</h3>';
12785 
12786  if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
12787  && $actionstatic->code != 'AC_TICKET_CREATE'
12788  && $actionstatic->code != 'AC_TICKET_MODIFY'
12789  ) {
12790  $out .= '<div class="timeline-body">';
12791  $out .= $histo[$key]['message'];
12792  $out .= '</div>';
12793  }
12794 
12795  // Timeline footer
12796  $footer = '';
12797 
12798  // Contact for this action
12799  if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
12800  $contactList = '';
12801  foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
12802  $contact = new Contact($db);
12803  $result = $contact->fetch($cid);
12804 
12805  if ($result < 0) {
12806  dol_print_error($db, $contact->error);
12807  }
12808 
12809  if ($result > 0) {
12810  $contactList .= !empty($contactList) ? ', ' : '';
12811  $contactList .= $contact->getNomUrl(1);
12812  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12813  if (!empty($contact->phone_pro)) {
12814  $contactList .= '('.dol_print_phone($contact->phone_pro).')';
12815  }
12816  }
12817  }
12818  }
12819 
12820  $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
12821  } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
12822  $contact = new Contact($db);
12823  $result = $contact->fetch($histo[$key]['contact_id']);
12824 
12825  if ($result < 0) {
12826  dol_print_error($db, $contact->error);
12827  }
12828 
12829  if ($result > 0) {
12830  $footer .= $contact->getNomUrl(1);
12831  if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
12832  if (!empty($contact->phone_pro)) {
12833  $footer .= '('.dol_print_phone($contact->phone_pro).')';
12834  }
12835  }
12836  }
12837  }
12838 
12839  $documents = getActionCommEcmList($actionstatic);
12840  if (!empty($documents)) {
12841  $footer .= '<div class="timeline-documents-container">';
12842  foreach ($documents as $doc) {
12843  $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
12844  $footer .= ' data-id="'.$doc->id.'" ';
12845  $footer .= ' data-path="'.$doc->filepath.'"';
12846  $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
12847  $footer .= '>';
12848 
12849  $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
12850  $mime = dol_mimetype($filePath);
12851  $file = $actionstatic->id.'/'.$doc->filename;
12852  $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
12853  $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
12854  $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
12855 
12856  $mimeAttr = ' mime="'.$mime.'" ';
12857  $class = '';
12858  if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
12859  $class .= ' documentpreview';
12860  }
12861 
12862  $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
12863  $footer .= img_mime($filePath).' '.$doc->filename;
12864  $footer .= '</a>';
12865 
12866  $footer .= '</span>';
12867  }
12868  $footer .= '</div>';
12869  }
12870 
12871  if (!empty($footer)) {
12872  $out .= '<div class="timeline-footer">'.$footer.'</div>';
12873  }
12874 
12875  $out .= '</div>'."\n"; // end timeline-item
12876 
12877  $out .= '</li>';
12878  $out .= '<!-- END timeline item -->';
12879 
12880  $i++;
12881  }
12882 
12883  $out .= "</ul>\n";
12884 
12885  if (empty($histo)) {
12886  $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
12887  }
12888  }
12889 
12890  if ($noprint) {
12891  return $out;
12892  } else {
12893  print $out;
12894  }
12895 }
12896 
12907 function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
12908 {
12909  if ($hourTime === 'getpost') {
12910  $hour = GETPOSTINT($prefix . 'hour');
12911  $minute = GETPOSTINT($prefix . 'minute');
12912  $second = GETPOSTINT($prefix . 'second');
12913  } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
12914  $hour = intval($m[1]);
12915  $minute = intval($m[2]);
12916  $second = intval($m[3]);
12917  } else {
12918  $hour = $minute = $second = 0;
12919  }
12920  // normalize out of range values
12921  $hour = min($hour, 23);
12922  $minute = min($minute, 59);
12923  $second = min($second, 59);
12924  return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
12925 }
12926 
12938 function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
12939 {
12940  if ($timestamp === null) $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
12941  $TParam = array(
12942  $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
12943  $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
12944  $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
12945  );
12946  if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
12947  $TParam = array_merge($TParam, array(
12948  $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
12949  $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
12950  $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
12951  ));
12952  }
12953 
12954  return '&' . http_build_query($TParam);
12955 }
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:496
dol_get_next_day($day, $month, $year)
Return next day.
Definition: date.lib.php:481
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:465
dol_get_next_month($month, $year)
Return next month.
Definition: date.lib.php:515
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:2114
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
Definition: files.lib.php:3448
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 not be considered as HTML encoded even if 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.
dolPrintHTML($s)
Return a string ready to be output on HTML page To use text inside an attribute, use can use only dol...
img_searchclear($titlealt='default', $other='')
Show search logo.
dolPrintLabel($s)
Return a string label ready to be output on HTML content To use text inside an attribute,...
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...
dolPrintHTMLForTextArea($s)
Return a string ready to be output on input textarea To use text inside an attribute,...
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:1510
div float
Buy price without taxes.
Definition: style.css.php:926
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,...