dolibarr 21.0.0-beta
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-2024 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-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
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-2024 Frédéric France <frederic.france@free.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 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
25 * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26 * Copyright (C) 2024 Josep Lluís Amador Teruel <joseplluis@lliuretic.cat>
27 * Copyright (C) 2024 Benoît PASCAL <contact@p-ben.com>
28 *
29 * This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 3 of the License, or
32 * (at your option) any later version.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program. If not, see <https://www.gnu.org/licenses/>.
41 * or see https://www.gnu.org/
42 */
43
50//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
51
52// Function for better PHP x compatibility
53if (!function_exists('utf8_encode')) {
61 function utf8_encode($elements)
62 {
63 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
64 }
65}
66
67if (!function_exists('utf8_decode')) {
75 function utf8_decode($elements)
76 {
77 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
78 }
79}
80if (!function_exists('str_starts_with')) {
89 function str_starts_with($haystack, $needle)
90 {
91 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
92 }
93}
94if (!function_exists('str_ends_with')) {
103 function str_ends_with($haystack, $needle)
104 {
105 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
106 }
107}
108if (!function_exists('str_contains')) {
117 function str_contains($haystack, $needle)
118 {
119 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
120 }
121}
122
123
135function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
136{
137 global $conf;
138
139 if (!is_object($object) && empty($module)) {
140 return null;
141 }
142 if (empty($module) && !empty($object->element)) {
143 $module = $object->element;
144 }
145
146 // Special case for backward compatibility
147 if ($module == 'fichinter') {
148 $module = 'ficheinter';
149 } elseif ($module == 'invoice_supplier') {
150 $module = 'supplier_invoice';
151 } elseif ($module == 'order_supplier') {
152 $module = 'supplier_order';
153 }
154
155 // Get the relative path of directory
156 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
157 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
158 $s = '';
159 if ($mode != 'outputrel') {
160 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
161 }
162 if ($forobject && $object->id > 0) {
163 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
164 }
165 return $s;
166 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
167 $s = '';
168 if ($mode != 'outputrel') {
169 $s = $conf->$module->dir_output;
170 }
171 if ($forobject && $object->id > 0) {
172 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
173 }
174 return $s;
175 } else {
176 return 'error-diroutput-not-defined-for-this-object='.$module;
177 }
178 } elseif ($mode == 'temp') {
179 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
180 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
181 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
182 return $conf->$module->dir_temp;
183 } else {
184 return 'error-dirtemp-not-defined-for-this-object='.$module;
185 }
186 } else {
187 return 'error-bad-value-for-mode';
188 }
189}
190
200function getMultidirTemp($object, $module = '', $forobject = 0)
201{
202 return getMultidirOutput($object, $module, $forobject, 'temp');
203}
204
214function getMultidirVersion($object, $module = '', $forobject = 0)
215{
216 return getMultidirOutput($object, $module, $forobject, 'version');
217}
218
219
228function getDolGlobalString($key, $default = '')
229{
230 global $conf;
231 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
232}
233
243function getDolGlobalInt($key, $default = 0)
244{
245 global $conf;
246 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
247}
248
258function getDolGlobalFloat($key, $default = 0)
259{
260 global $conf;
261 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
262}
263
272function getDolGlobalBool($key, $default = false)
273{
274 global $conf;
275 return (bool) ($conf->global->$key ?? $default);
276}
277
287function getDolUserString($key, $default = '', $tmpuser = null)
288{
289 if (empty($tmpuser)) {
290 global $user;
291 $tmpuser = $user;
292 }
293
294 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
295}
296
305function getDolUserInt($key, $default = 0, $tmpuser = null)
306{
307 if (empty($tmpuser)) {
308 global $user;
309 $tmpuser = $user;
310 }
311
312 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
313}
314
315
325define(
326 'MODULE_MAPPING',
327 array(
328 // Map deprecated names to new names
329 'adherent' => 'member', // Has new directory
330 'member_type' => 'adherent_type', // No directory, but file called adherent_type
331 'banque' => 'bank', // Has new directory
332 'contrat' => 'contract', // Has new directory
333 'entrepot' => 'stock', // Has new directory
334 'projet' => 'project', // Has new directory
335 'categorie' => 'category', // Has old directory
336 'commande' => 'order', // Has old directory
337 'expedition' => 'shipping', // Has old directory
338 'facture' => 'invoice', // Has old directory
339 'fichinter' => 'intervention', // Has old directory
340 'ficheinter' => 'intervention', // Backup for 'fichinter'
341 'propale' => 'propal', // Has old directory
342 'socpeople' => 'contact', // Has old directory
343 'fournisseur' => 'supplier', // Has old directory
344
345 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
346 'product_price' => 'productprice', // NO directory
347 'product_fournisseur_price' => 'productsupplierprice', // NO directory
348 )
349);
350
357function isModEnabled($module)
358{
359 global $conf;
360
361 // Fix old names (map to new names)
362 $arrayconv = MODULE_MAPPING;
363 $arrayconvbis = array_flip(MODULE_MAPPING);
364
365 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
366 // Special cases: both use the same module.
367 $arrayconv['supplier_order'] = 'fournisseur';
368 $arrayconv['supplier_invoice'] = 'fournisseur';
369 }
370 // Special case.
371 // @TODO Replace isModEnabled('delivery_note') with
372 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
373 if ($module == 'delivery_note') {
374 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
375 return false;
376 } else {
377 $module = 'shipping';
378 }
379 }
380
381 $module_alt = $module;
382 if (!empty($arrayconv[$module])) {
383 $module_alt = $arrayconv[$module];
384 }
385 $module_bis = $module;
386 if (!empty($arrayconvbis[$module])) {
387 $module_bis = $arrayconvbis[$module];
388 }
389
390 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
391 //return !empty($conf->$module->enabled);
392}
393
400function isDolTms($timestamp)
401{
402 if ($timestamp === '') {
403 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
404 return false;
405 }
406 if (is_null($timestamp) || !is_numeric($timestamp)) {
407 return false;
408 }
409
410 return true;
411}
412
424function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
425{
426 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
427
428 $class = 'DoliDB'.ucfirst($type);
429 $db = new $class($type, $host, $user, $pass, $name, $port);
430 return $db;
431}
432
450function getEntity($element, $shared = 1, $currentobject = null)
451{
452 global $conf, $mc, $hookmanager, $object, $action, $db;
453
454 if (!is_object($hookmanager)) {
455 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
456 $hookmanager = new HookManager($db);
457 }
458
459 // fix different element names (France to English)
460 switch ($element) {
461 case 'projet':
462 $element = 'project';
463 break;
464 case 'contrat':
465 $element = 'contract';
466 break; // "/contrat/class/contrat.class.php"
467 case 'order_supplier':
468 $element = 'supplier_order';
469 break; // "/fourn/class/fournisseur.commande.class.php"
470 case 'invoice_supplier':
471 $element = 'supplier_invoice';
472 break; // "/fourn/class/fournisseur.facture.class.php"
473 }
474
475 if (is_object($mc)) {
476 $out = $mc->getEntity($element, $shared, $currentobject);
477 } else {
478 $out = '';
479 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
480 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
481 $addzero[] = 'c_holiday_types';
482 }
483 if (in_array($element, $addzero)) {
484 $out .= '0,';
485 }
486 $out .= ((int) $conf->entity);
487 }
488
489 // Manipulate entities to query on the fly
490 $parameters = array(
491 'element' => $element,
492 'shared' => $shared,
493 'object' => $object,
494 'currentobject' => $currentobject,
495 'out' => $out
496 );
497 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
498
499 if (is_numeric($reshook)) {
500 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
501 $out .= ','.$hookmanager->resPrint; // add
502 } elseif ($reshook == 1) {
503 $out = $hookmanager->resPrint; // replace
504 }
505 }
506
507 return $out;
508}
509
516function setEntity($currentobject)
517{
518 global $conf, $mc;
519
520 if (is_object($mc) && method_exists($mc, 'setEntity')) {
521 return $mc->setEntity($currentobject);
522 } else {
523 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
524 }
525}
526
533function isASecretKey($keyname)
534{
535 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
536}
537
538
545function num2Alpha($n)
546{
547 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
548 $r = chr($n % 26 + 0x41) . $r;
549 }
550 return $r;
551}
552
553
570function getBrowserInfo($user_agent)
571{
572 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
573
574 $name = 'unknown';
575 $version = '';
576 $os = 'unknown';
577 $phone = '';
578
579 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
580
581 $detectmobile = new Mobile_Detect(null, $user_agent);
582 $tablet = $detectmobile->isTablet();
583
584 if ($detectmobile->isMobile()) {
585 $phone = 'unknown';
586
587 // If phone/smartphone, we set phone os name.
588 if ($detectmobile->is('AndroidOS')) {
589 $os = $phone = 'android';
590 } elseif ($detectmobile->is('BlackBerryOS')) {
591 $os = $phone = 'blackberry';
592 } elseif ($detectmobile->is('iOS')) {
593 $os = 'ios';
594 $phone = 'iphone';
595 } elseif ($detectmobile->is('PalmOS')) {
596 $os = $phone = 'palm';
597 } elseif ($detectmobile->is('SymbianOS')) {
598 $os = 'symbian';
599 } elseif ($detectmobile->is('webOS')) {
600 $os = 'webos';
601 } elseif ($detectmobile->is('MaemoOS')) {
602 $os = 'maemo';
603 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
604 $os = 'windows';
605 }
606 }
607
608 // OS
609 if (preg_match('/linux/i', $user_agent)) {
610 $os = 'linux';
611 } elseif (preg_match('/macintosh/i', $user_agent)) {
612 $os = 'macintosh';
613 } elseif (preg_match('/windows/i', $user_agent)) {
614 $os = 'windows';
615 }
616
617 // Name
618 $reg = array();
619 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
620 $name = 'firefox';
621 $version = empty($reg[2]) ? '' : $reg[2];
622 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
623 $name = 'edge';
624 $version = empty($reg[2]) ? '' : $reg[2];
625 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
626 $name = 'chrome';
627 $version = empty($reg[2]) ? '' : $reg[2];
628 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
629 // we can have 'chrome (Mozilla...) chrome x.y' in one string
630 $name = 'chrome';
631 } elseif (preg_match('/iceweasel/i', $user_agent)) {
632 $name = 'iceweasel';
633 } elseif (preg_match('/epiphany/i', $user_agent)) {
634 $name = 'epiphany';
635 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
636 $name = 'safari';
637 $version = empty($reg[2]) ? '' : $reg[2];
638 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
639 // Safari is often present in string for mobile but its not.
640 $name = 'opera';
641 $version = empty($reg[2]) ? '' : $reg[2];
642 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
643 $name = 'ie';
644 $version = end($reg);
645 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
646 // MS products at end
647 $name = 'ie';
648 $version = end($reg);
649 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
650 // MS products at end
651 $name = 'textbrowser';
652 $version = empty($reg[3]) ? '' : $reg[3];
653 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
654 // MS products at end
655 $name = 'textbrowser';
656 $version = empty($reg[1]) ? '' : $reg[1];
657 }
658
659 if ($tablet) {
660 $layout = 'tablet';
661 } elseif ($phone) {
662 $layout = 'phone';
663 } else {
664 $layout = 'classic';
665 }
666
667 return array(
668 'browsername' => $name,
669 'browserversion' => $version,
670 'browseros' => $os,
671 'browserua' => $user_agent,
672 'layout' => $layout, // tablet, phone, classic
673 'phone' => $phone, // deprecated
674 'tablet' => $tablet // deprecated
675 );
676}
677
683function dol_shutdown()
684{
685 global $db;
686 $disconnectdone = false;
687 $depth = 0;
688 if (is_object($db) && !empty($db->connected)) {
689 $depth = $db->transaction_opened;
690 $disconnectdone = $db->close();
691 }
692 dol_syslog("--- End access to ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]).(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
693}
694
704function GETPOSTISSET($paramname)
705{
706 $isset = false;
707
708 $relativepathstring = $_SERVER["PHP_SELF"];
709 // Clean $relativepathstring
710 if (constant('DOL_URL_ROOT')) {
711 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
712 }
713 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
714 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
715 //var_dump($relativepathstring);
716 //var_dump($user->default_values);
717
718 // Code for search criteria persistence.
719 // Retrieve values if restore_lastsearch_values
720 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
721 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
722 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
723 if (is_array($tmp)) {
724 foreach ($tmp as $key => $val) {
725 if ($key == $paramname) { // We are on the requested parameter
726 $isset = true;
727 break;
728 }
729 }
730 }
731 }
732 // If there is saved contextpage, limit, page or mode
733 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
734 $isset = true;
735 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
736 $isset = true;
737 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
738 $isset = true;
739 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
740 $isset = true;
741 }
742 } else {
743 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
744 }
745
746 return $isset;
747}
748
757function GETPOSTISARRAY($paramname, $method = 0)
758{
759 // for $method test need return the same $val as GETPOST
760 if (empty($method)) {
761 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
762 } elseif ($method == 1) {
763 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
764 } elseif ($method == 2) {
765 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
766 } elseif ($method == 3) {
767 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
768 } else {
769 $val = 'BadFirstParameterForGETPOST';
770 }
771
772 return is_array($val);
773}
774
805function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
806{
807 global $mysoc, $user, $conf;
808
809 if (empty($paramname)) { // Explicit test for null for phan.
810 return 'BadFirstParameterForGETPOST';
811 }
812 if (empty($check)) {
813 dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and a 2nd param that is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
814 // Enable this line to know who call the GETPOST with '' $check parameter.
815 //var_dump(debug_backtrace()[0]);
816 }
817
818 if (empty($method)) {
819 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
820 } elseif ($method == 1) {
821 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
822 } elseif ($method == 2) {
823 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
824 } elseif ($method == 3) {
825 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
826 } else {
827 return 'BadThirdParameterForGETPOST';
828 }
829
830 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
831
832 if (empty($method) || $method == 3 || $method == 4) {
833 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
834 // Clean $relativepathstring
835 if (constant('DOL_URL_ROOT')) {
836 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
837 }
838 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
839 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
840 //var_dump($relativepathstring);
841 //var_dump($user->default_values);
842
843 // Code for search criteria persistence.
844 // Retrieve saved values if restore_lastsearch_values is set
845 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
846 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
847 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
848 if (is_array($tmp)) {
849 foreach ($tmp as $key => $val) {
850 if ($key == $paramname) { // We are on the requested parameter
851 $out = $val;
852 break;
853 }
854 }
855 }
856 }
857 // If there is saved contextpage, page or limit
858 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
859 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
860 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
861 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
862 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
863 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
864 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
865 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
866 }
867 } elseif (!isset($_GET['sortfield'])) {
868 // Else, retrieve default values if we are not doing a sort
869 // 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
870 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
871 // Search default value from $object->field
872 global $object;
873 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
874 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
875 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
876 $out = $object->fields[$paramname]['default'];
877 }
878 }
879 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
880 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
881 // Now search in setup to overwrite default values
882 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
883 if (isset($user->default_values[$relativepathstring]['createform'])) {
884 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
885 $qualified = 0;
886 if ($defkey != '_noquery_') {
887 $tmpqueryarraytohave = explode('&', $defkey);
888 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
889 $foundintru = 0;
890 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
891 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
892 $foundintru = 1;
893 }
894 }
895 if (!$foundintru) {
896 $qualified = 1;
897 }
898 //var_dump($defkey.'-'.$qualified);
899 } else {
900 $qualified = 1;
901 }
902
903 if ($qualified) {
904 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
905 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
906 break;
907 }
908 }
909 }
910 }
911 }
912 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
913 // Management of default search_filters and sort order
914 if (!empty($user->default_values)) {
915 // $user->default_values defined from menu 'Setup - Default values'
916 //var_dump($user->default_values[$relativepathstring]);
917 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
918 // Sorted on which fields ? ASC or DESC ?
919 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
920 // Even if paramname is sortfield, data are stored into ['sortorder...']
921 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
922 $qualified = 0;
923 if ($defkey != '_noquery_') {
924 $tmpqueryarraytohave = explode('&', $defkey);
925 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
926 $foundintru = 0;
927 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
928 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
929 $foundintru = 1;
930 }
931 }
932 if (!$foundintru) {
933 $qualified = 1;
934 }
935 //var_dump($defkey.'-'.$qualified);
936 } else {
937 $qualified = 1;
938 }
939
940 if ($qualified) {
941 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
942 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
943 if ($out) {
944 $out .= ', ';
945 }
946 if ($paramname == 'sortfield') {
947 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
948 }
949 if ($paramname == 'sortorder') {
950 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
951 }
952 }
953 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
954 }
955 }
956 }
957 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
958 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
959 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
960 continue;
961 }
962 $qualified = 0;
963 if ($defkey != '_noquery_') {
964 $tmpqueryarraytohave = explode('&', $defkey);
965 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
966 $foundintru = 0;
967 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
968 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
969 $foundintru = 1;
970 }
971 }
972 if (!$foundintru) {
973 $qualified = 1;
974 }
975 //var_dump($defkey.'-'.$qualified);
976 } else {
977 $qualified = 1;
978 }
979
980 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
981 // We must keep $_POST and $_GET here
982 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
983 // We made a search from quick search menu, do we still use default filter ?
984 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
985 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
986 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
987 }
988 } else {
989 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
990 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
991 }
992 break;
993 }
994 }
995 }
996 }
997 }
998 }
999 }
1000 }
1001
1002 // Substitution variables for GETPOST (used to get final url with variable parameters or final default value, when using variable parameters __XXX__ in the GET URL)
1003 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1004 // 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.
1005 '@phan-var-force string $paramname';
1006 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1007 $reg = array();
1008 $maxloop = 20;
1009 $loopnb = 0; // Protection against infinite loop
1010
1011 while (preg_match('/__([A-Z0-9]+(?:_[A-Z0-9]+){0,3})__/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.
1012 $loopnb++;
1013 $newout = '';
1014
1015 if ($reg[1] == 'DAY') {
1016 $tmp = dol_getdate(dol_now(), true);
1017 $newout = $tmp['mday'];
1018 } elseif ($reg[1] == 'MONTH') {
1019 $tmp = dol_getdate(dol_now(), true);
1020 $newout = $tmp['mon'];
1021 } elseif ($reg[1] == 'YEAR') {
1022 $tmp = dol_getdate(dol_now(), true);
1023 $newout = $tmp['year'];
1024 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1025 $tmp = dol_getdate(dol_now(), true);
1026 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1027 $newout = $tmp2['day'];
1028 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1029 $tmp = dol_getdate(dol_now(), true);
1030 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1031 $newout = $tmp2['month'];
1032 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1033 $tmp = dol_getdate(dol_now(), true);
1034 $newout = ($tmp['year'] - 1);
1035 } elseif ($reg[1] == 'NEXT_DAY') {
1036 $tmp = dol_getdate(dol_now(), true);
1037 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1038 $newout = $tmp2['day'];
1039 } elseif ($reg[1] == 'NEXT_MONTH') {
1040 $tmp = dol_getdate(dol_now(), true);
1041 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1042 $newout = $tmp2['month'];
1043 } elseif ($reg[1] == 'NEXT_YEAR') {
1044 $tmp = dol_getdate(dol_now(), true);
1045 $newout = ($tmp['year'] + 1);
1046 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1047 $newout = $mysoc->country_id;
1048 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1049 $newout = $user->id;
1050 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1051 $newout = $user->fk_user;
1052 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1053 $newout = $conf->entity;
1054 } elseif ($reg[1] == 'ID') {
1055 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1056 } else {
1057 $newout = ''; // Key not found, we replace with empty string
1058 }
1059 //var_dump('__'.$reg[1].'__ -> '.$newout);
1060 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1061 }
1062 }
1063
1064 // Check type of variable and make sanitization according to this
1065 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1066 if (!is_array($out) || empty($out)) {
1067 $out = array();
1068 } else {
1069 $tmparray = explode(':', $check);
1070 if (!empty($tmparray[1])) {
1071 $tmpcheck = $tmparray[1];
1072 } else {
1073 $tmpcheck = 'alphanohtml';
1074 }
1075 foreach ($out as $outkey => $outval) {
1076 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1077 }
1078 }
1079 } else {
1080 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1081 // 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
1082 if (strpos($paramname, 'search_') === 0) {
1083 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1084 }
1085
1086 // @phan-suppress-next-line UnknownSanitizeType
1087 $out = sanitizeVal($out, $check, $filter, $options);
1088 }
1089
1090 // Sanitizing for special parameters.
1091 // Note: There is no reason to allow the backtopage/backtopageforcancel/backtopagejs, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
1092 // @TODO Merge backtopage with backtourl
1093 // @TODO Rename backtolist into backtopagelist
1094 if (preg_match('/^backto/i', $paramname)) {
1095 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1096 $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retrieve it after other replacements.
1097 do {
1098 $oldstringtoclean = $out;
1099 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1100 $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'
1101 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1102 } while ($oldstringtoclean != $out);
1103 }
1104
1105 // Code for search criteria persistence.
1106 // Save data into session if key start with 'search_'
1107 if (empty($method) || $method == 3 || $method == 4) {
1108 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1109 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1110
1111 // We save search key only if $out not empty that means:
1112 // - posted value not empty, or
1113 // - 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).
1114
1115 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1116 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1117 }
1118 }
1119 }
1120
1121 return $out;
1122}
1123
1133function GETPOSTINT($paramname, $method = 0)
1134{
1135 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1136}
1137
1138
1147function GETPOSTFLOAT($paramname, $rounding = '')
1148{
1149 // price2num() is used to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.)
1150 return (float) price2num(GETPOST($paramname), $rounding, 2);
1151}
1152
1153
1167function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
1168{
1169 $m = array();
1170 if ($hourTime === 'getpost') {
1171 $hour = GETPOSTINT($prefix . 'hour');
1172 $minute = GETPOSTINT($prefix . 'minute');
1173 $second = GETPOSTINT($prefix . 'second');
1174 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1175 $hour = intval($m[1]);
1176 $minute = intval($m[2]);
1177 $second = intval($m[3]);
1178 } else {
1179 $hour = $minute = $second = 0;
1180 }
1181 // normalize out of range values
1182 $hour = (int) min($hour, 23);
1183 $minute = (int) min($minute, 59);
1184 $second = (int) min($second, 59);
1185
1186 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
1187}
1188
1189
1200function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1201{
1202 return sanitizeVal($out, $check, $filter, $options);
1203}
1204
1214function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1215{
1216 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1217 // Check is done after replacement
1218 switch ($check) {
1219 case 'none':
1220 case 'password':
1221 break;
1222 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1223 if (!is_numeric($out)) {
1224 $out = '';
1225 }
1226 break;
1227 case 'intcomma':
1228 if (is_array($out)) {
1229 $out = implode(',', $out);
1230 }
1231 if (preg_match('/[^0-9,-]+/i', $out)) {
1232 $out = '';
1233 }
1234 break;
1235 case 'san_alpha':
1236 $out = filter_var($out, FILTER_SANITIZE_STRING);
1237 break;
1238 case 'email':
1239 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1240 break;
1241 case 'aZ':
1242 if (!is_array($out)) {
1243 $out = trim($out);
1244 if (preg_match('/[^a-z]+/i', $out)) {
1245 $out = '';
1246 }
1247 }
1248 break;
1249 case 'aZ09':
1250 if (!is_array($out)) {
1251 $out = trim($out);
1252 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1253 $out = '';
1254 }
1255 }
1256 break;
1257 case 'aZ09arobase': // great to sanitize $objecttype parameter
1258 if (!is_array($out)) {
1259 $out = trim($out);
1260 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1261 $out = '';
1262 }
1263 }
1264 break;
1265 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1266 if (!is_array($out)) {
1267 $out = trim($out);
1268 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1269 $out = '';
1270 }
1271 }
1272 break;
1273 case 'alpha': // No html and no ../ and "
1274 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1275 if (!is_array($out)) {
1276 $out = trim($out);
1277 do {
1278 $oldstringtoclean = $out;
1279 // Remove html tags
1280 $out = dol_string_nohtmltag($out, 0);
1281 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1282 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1283 // Remove also other dangerous string sequences
1284 // '../' or '..\' is dangerous because it allows dir transversals
1285 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1286 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1287 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1288 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1289 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1290 } while ($oldstringtoclean != $out);
1291 // keep lines feed
1292 }
1293 break;
1294 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email@domain.com>". Less secured than 'alphanohtml'
1295 if (!is_array($out)) {
1296 $out = trim($out);
1297 do {
1298 $oldstringtoclean = $out;
1299 // Decode html entities
1300 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1301 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1302 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1303 // Remove also other dangerous string sequences
1304 // '../' or '..\' is dangerous because it allows dir transversals
1305 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1306 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1307 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1308 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1309 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1310 } while ($oldstringtoclean != $out);
1311 }
1312 break;
1313 case 'nohtml': // No html
1314 $out = dol_string_nohtmltag($out, 0);
1315 break;
1316 case 'restricthtmlnolink':
1317 case 'restricthtml': // Recommended for most html textarea
1318 case 'restricthtmlallowclass':
1319 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1320 case 'restricthtmlallowunvalid':
1321 $out = dol_htmlwithnojs($out, 1, $check);
1322 break;
1323 case 'custom':
1324 if (!empty($out)) {
1325 if (empty($filter)) {
1326 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1327 }
1328 if (is_null($options)) {
1329 $options = 0;
1330 }
1331 $out = filter_var($out, $filter, $options);
1332 }
1333 break;
1334 default:
1335 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1336 $out = GETPOST($out, 'alphanohtml');
1337 break;
1338 }
1339
1340 return $out;
1341}
1342
1343
1344if (!function_exists('dol_getprefix')) {
1355 function dol_getprefix($mode = '')
1356 {
1357 // If prefix is for email (we need to have $conf already loaded for this case)
1358 if ($mode == 'email') {
1359 global $conf;
1360
1361 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1362 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1363 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1364 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1365 return $_SERVER["SERVER_NAME"];
1366 }
1367 }
1368
1369 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1370 if (!empty($conf->file->instance_unique_id)) {
1371 return sha1('dolibarr'.$conf->file->instance_unique_id);
1372 }
1373
1374 // For backward compatibility when instance_unique_id is not set
1375 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1376 }
1377
1378 // If prefix is for session (no need to have $conf loaded)
1379 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1380 $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
1381
1382 // The recommended value (may be not defined for old versions)
1383 if (!empty($tmp_instance_unique_id)) {
1384 return sha1('dolibarr'.$tmp_instance_unique_id);
1385 }
1386
1387 // For backward compatibility when instance_unique_id is not set
1388 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1389 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1390 } else {
1391 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1392 }
1393 }
1394}
1395
1406function dol_include_once($relpath, $classname = '')
1407{
1408 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']
1409
1410 $fullpath = dol_buildpath($relpath);
1411
1412 if (!file_exists($fullpath)) {
1413 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1414 return false;
1415 }
1416
1417 if (!empty($classname) && !class_exists($classname)) {
1418 return include $fullpath;
1419 } else {
1420 return include_once $fullpath;
1421 }
1422}
1423
1424
1438function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1439{
1440 global $conf;
1441
1442 $path = preg_replace('/^\//', '', $path);
1443
1444 if (empty($type)) { // For a filesystem path
1445 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1446 if (is_array($conf->file->dol_document_root)) {
1447 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1448 if ($key == 'main') {
1449 continue;
1450 }
1451 // if (@file_exists($dirroot.'/'.$path)) {
1452 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1453 $res = $dirroot.'/'.$path;
1454 return $res;
1455 }
1456 }
1457 }
1458 if ($returnemptyifnotfound) {
1459 // Not found into alternate dir
1460 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1461 return '';
1462 }
1463 }
1464 } else {
1465 // For an url path
1466 // We try to get local path of file on filesystem from url
1467 // Note that trying to know if a file on disk exist by forging path on disk from url
1468 // works only for some web server and some setup. This is bugged when
1469 // using proxy, rewriting, virtual path, etc...
1470 $res = '';
1471 if ($type == 1) {
1472 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1473 }
1474 if ($type == 2) {
1475 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1476 }
1477 if ($type == 3) {
1478 $res = DOL_URL_ROOT.'/'.$path;
1479 }
1480
1481 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1482 if ($key == 'main') {
1483 if ($type == 3) {
1484 /*global $dolibarr_main_url_root;*/
1485
1486 // Define $urlwithroot
1487 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1488 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1489 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1490
1491 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1492 }
1493 continue;
1494 }
1495 $regs = array();
1496 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1497 if (!empty($regs[1])) {
1498 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1499 //if (file_exists($dirroot.'/'.$regs[1])) {
1500 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1501 if ($type == 1) {
1502 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1503 } elseif ($type == 2) {
1504 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1505 } elseif ($type == 3) {
1506 /*global $dolibarr_main_url_root;*/
1507
1508 // Define $urlwithroot
1509 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1510 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1511 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1512
1513 $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
1514 }
1515 break;
1516 }
1517 }
1518 }
1519 }
1520
1521 return $res;
1522}
1523
1534function dol_get_object_properties($obj, $properties = [])
1535{
1536 // Get real properties using get_object_vars() if $properties is empty
1537 if (empty($properties)) {
1538 return get_object_vars($obj);
1539 }
1540
1541 $existingProperties = [];
1542 $realProperties = get_object_vars($obj);
1543
1544 // Get the real or magic property values
1545 foreach ($properties as $property) {
1546 if (array_key_exists($property, $realProperties)) {
1547 // Real property, add the value
1548 $existingProperties[$property] = $obj->{$property};
1549 } elseif (property_exists($obj, $property)) {
1550 // Magic property
1551 $existingProperties[$property] = $obj->{$property};
1552 }
1553 }
1554
1555 return $existingProperties;
1556}
1557
1558
1574function dol_clone($object, $native = 2)
1575{
1576 if ($native == 0) {
1577 // deprecated method, use the method with native = 2 instead
1578 $tmpsavdb = null;
1579 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1580 $tmpsavdb = $object->db;
1581 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1582 }
1583
1584 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1585
1586 if (!empty($tmpsavdb)) {
1587 $object->db = $tmpsavdb;
1588 }
1589 } elseif ($native == 2) {
1590 // recommended method to have a full isolated cloned object
1591 $myclone = new stdClass();
1592 $tmparray = get_object_vars($object); // return only public properties
1593
1594 if (is_array($tmparray)) {
1595 foreach ($tmparray as $propertykey => $propertyval) {
1596 if (is_scalar($propertyval) || is_array($propertyval)) {
1597 $myclone->$propertykey = $propertyval;
1598 }
1599 }
1600 }
1601 } else {
1602 $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (referring to the same target/variable)
1603 }
1604
1605 return $myclone;
1606}
1607
1617function dol_size($size, $type = '')
1618{
1619 global $conf;
1620 if (empty($conf->dol_optimize_smallscreen)) {
1621 return $size;
1622 }
1623 if ($type == 'width' && $size > 250) {
1624 return 250;
1625 } else {
1626 return 10;
1627 }
1628}
1629
1630
1642function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1643{
1644 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1645 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1646 // Char '/' and '\' are file delimiters.
1647 // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1648 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1649 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1650 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1651 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1652 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1653 $tmp = str_replace('..', '', $tmp);
1654 return $tmp;
1655}
1656
1657
1669function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1670{
1671 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1672 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1673 // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1674 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1675
1676 $tmp = $str;
1677 if ($unaccent) {
1678 $tmp = dol_string_unaccent($tmp);
1679 }
1680 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1681 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1682 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1683 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1684 $tmp = str_replace('..', '', $tmp);
1685 return $tmp;
1686}
1687
1695function dol_sanitizeUrl($stringtoclean, $type = 1)
1696{
1697 // 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)
1698 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1699 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1700 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1701 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1702
1703 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1704 if ($type == 1) {
1705 // removing : should disable links to external url like http:aaa)
1706 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1707 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1708 }
1709
1710 do {
1711 $oldstringtoclean = $stringtoclean;
1712 // removing '&colon' should disable links to external url like http:aaa)
1713 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1714 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1715 } while ($oldstringtoclean != $stringtoclean);
1716
1717 if ($type == 1) {
1718 // removing '//' should disable links to external url like //aaa or http//)
1719 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1720 }
1721
1722 return $stringtoclean;
1723}
1724
1731function dol_sanitizeEmail($stringtoclean)
1732{
1733 do {
1734 $oldstringtoclean = $stringtoclean;
1735 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1736 } while ($oldstringtoclean != $stringtoclean);
1737
1738 return $stringtoclean;
1739}
1740
1749function dol_sanitizeKeyCode($str)
1750{
1751 return preg_replace('/[^\w]+/', '', $str);
1752}
1753
1754
1763function dol_string_unaccent($str)
1764{
1765 if (is_null($str)) {
1766 return '';
1767 }
1768
1769 if (utf8_check($str)) {
1770 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1771 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1772 return $transliterator->transliterate($str);
1773 }
1774 // See http://www.utf8-chartable.de/
1775 $string = rawurlencode($str);
1776 $replacements = array(
1777 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1778 '%C3%87' => 'C',
1779 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1780 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1781 '%C3%91' => 'N',
1782 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1783 '%C5%A0' => 'S',
1784 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1785 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1786 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1787 '%C3%A7' => 'c',
1788 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1789 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1790 '%C3%B1' => 'n',
1791 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1792 '%C5%A1' => 's',
1793 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1794 '%C3%BD' => 'y', '%C3%BF' => 'y'
1795 );
1796 $string = strtr($string, $replacements);
1797 return rawurldecode($string);
1798 } else {
1799 // See http://www.ascii-code.com/
1800 $string = strtr(
1801 $str,
1802 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1803 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1804 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1805 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1806 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1807 \xF9\xFA\xFB\xFC\xFD\xFF",
1808 "AAAAAAC
1809 EEEEIIIIDN
1810 OOOOOUUUY
1811 aaaaaaceeee
1812 iiiidnooooo
1813 uuuuyy"
1814 );
1815 $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"));
1816 return $string;
1817 }
1818}
1819
1833function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1834{
1835 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1836 if (empty($keepspaces)) {
1837 $forbidden_chars_to_replace[] = " ";
1838 }
1839 $forbidden_chars_to_remove = array();
1840 //$forbidden_chars_to_remove=array("(",")");
1841
1842 if (is_array($badcharstoreplace)) {
1843 $forbidden_chars_to_replace = $badcharstoreplace;
1844 }
1845 if (is_array($badcharstoremove)) {
1846 $forbidden_chars_to_remove = $badcharstoremove;
1847 }
1848
1849 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1850 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1851}
1852
1853
1867function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1868{
1869 if ($removetabcrlf) {
1870 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1871 } else {
1872 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
1873 }
1874}
1875
1884function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1885{
1886 if (is_null($stringtoescape)) {
1887 return '';
1888 }
1889
1890 // escape quotes and backslashes, newlines, etc.
1891 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1892 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1893 if (empty($noescapebackslashn)) {
1894 $substitjs["\n"] = '\\n';
1895 $substitjs['\\'] = '\\\\';
1896 }
1897 if (empty($mode)) {
1898 $substitjs["'"] = "\\'";
1899 $substitjs['"'] = "\\'";
1900 } elseif ($mode == 1) {
1901 $substitjs["'"] = "\\'";
1902 } elseif ($mode == 2) {
1903 $substitjs['"'] = '\\"';
1904 } elseif ($mode == 3) {
1905 $substitjs["'"] = "\\'";
1906 $substitjs['"'] = "\\\"";
1907 }
1908 return strtr($stringtoescape, $substitjs);
1909}
1910
1920function dol_escape_uri($stringtoescape)
1921{
1922 return rawurlencode($stringtoescape);
1923}
1924
1931function dol_escape_json($stringtoescape)
1932{
1933 return str_replace('"', '\"', $stringtoescape);
1934}
1935
1943function dol_escape_php($stringtoescape, $stringforquotes = 2)
1944{
1945 if (is_null($stringtoescape)) {
1946 return '';
1947 }
1948
1949 if ($stringforquotes == 2) {
1950 return str_replace('"', "'", $stringtoescape);
1951 } elseif ($stringforquotes == 1) {
1952 // We remove the \ char.
1953 // If we allow the \ char, we can have $stringtoescape =
1954 // abc\';phpcodedanger; so the escapement will become
1955 // abc\\';phpcodedanger; and injecting this into
1956 // $a='...' will give $ac='abc\\';phpcodedanger;
1957 $stringtoescape = str_replace('\\', '', $stringtoescape);
1958 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1959 }
1960
1961 return 'Bad parameter for stringforquotes in dol_escape_php';
1962}
1963
1970function dol_escape_all($stringtoescape)
1971{
1972 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1973}
1974
1981function dol_escape_xml($stringtoescape)
1982{
1983 return $stringtoescape;
1984}
1985
1993function dolPrintLabel($s)
1994{
1995 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1996}
1997
2005function dolPrintText($s)
2006{
2007 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2008}
2009
2019function dolPrintHTML($s, $allowiframe = 0)
2020{
2021 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2022}
2023
2031function dolPrintHTMLForAttribute($s)
2032{
2033 // The dol_htmlentitiesbr will convert simple text into html
2034 // The dol_escape_htmltag will escape html chars.
2035 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
2036}
2037
2047function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2048{
2049 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2050}
2051
2058function dolPrintPassword($s)
2059{
2060 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
2061}
2062
2063
2080function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2081{
2082 if ($noescapetags == 'common') {
2083 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
2084 // Add also html5 tags
2085 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2086 }
2087 if ($cleanalsojavascript) {
2088 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2089 }
2090
2091 // escape quotes and backslashes, newlines, etc.
2092 if ($escapeonlyhtmltags) {
2093 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2094 } else {
2095 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
2096 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
2097 }
2098 if (!$keepb) {
2099 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2100 }
2101 if (!$keepn) {
2102 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2103 } elseif ($keepn == -1) {
2104 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2105 }
2106
2107 if ($escapeonlyhtmltags) {
2108 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2109 } else {
2110 // Escape tags to keep
2111 $tmparrayoftags = array();
2112 if ($noescapetags) {
2113 $tmparrayoftags = explode(',', $noescapetags);
2114 }
2115 if (count($tmparrayoftags)) {
2116 $reg = array();
2117 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2118
2119 foreach ($tmparrayoftags as $tagtoreplace) {
2120 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2121 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2122 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2123
2124 // For case of tag with attribute
2125 do {
2126 $tmpold = $tmp;
2127
2128 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', $tmp, $reg)) {
2129 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must never have [ ] inside the attribute string
2130 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes);
2131 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2132 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2133 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2134 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2135 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2136 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2137 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2138 }
2139 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)\s+\/>/', $tmp, $reg)) {
2140 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
2141 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2142 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2143 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2144 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2145 }
2146
2147 $diff = strcmp($tmpold, $tmp);
2148 } while ($diff);
2149 }
2150 }
2151
2152 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2153
2154 //print $result;
2155
2156 if (count($tmparrayoftags)) {
2157 foreach ($tmparrayoftags as $tagtoreplace) {
2158 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2159 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2160 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2161 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2162 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2163 }
2164
2165 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2166 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2167 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2168 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2169 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2170 }
2171
2172 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2173
2174 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2175
2176 return $result;
2177 }
2178}
2179
2187function dol_strtolower($string, $encoding = "UTF-8")
2188{
2189 if (function_exists('mb_strtolower')) {
2190 return mb_strtolower($string, $encoding);
2191 } else {
2192 return strtolower($string);
2193 }
2194}
2195
2204function dol_strtoupper($string, $encoding = "UTF-8")
2205{
2206 if (function_exists('mb_strtoupper')) {
2207 return mb_strtoupper($string, $encoding);
2208 } else {
2209 return strtoupper($string);
2210 }
2211}
2212
2221function dol_ucfirst($string, $encoding = "UTF-8")
2222{
2223 if (function_exists('mb_substr')) {
2224 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2225 } else {
2226 return ucfirst($string);
2227 }
2228}
2229
2238function dol_ucwords($string, $encoding = "UTF-8")
2239{
2240 if (function_exists('mb_convert_case')) {
2241 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2242 } else {
2243 return ucwords($string);
2244 }
2245}
2246
2247
2253function getCallerInfoString()
2254{
2255 $backtrace = debug_backtrace();
2256 $msg = "";
2257 if (count($backtrace) >= 2) {
2258 $trace = $backtrace[1];
2259 if (isset($trace['file'], $trace['line'])) {
2260 $msg = " From {$trace['file']}:{$trace['line']}.";
2261 }
2262 }
2263 return $msg;
2264}
2265
2288function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2289{
2290 global $conf, $user, $debugbar;
2291
2292 // If syslog module enabled
2293 if (!isModEnabled('syslog')) {
2294 return;
2295 }
2296
2297 // Check if we are into execution of code of a website
2298 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2299 global $website, $websitekey;
2300 if (is_object($website) && !empty($website->ref)) {
2301 $suffixinfilename .= '_website_'.$website->ref;
2302 } elseif (!empty($websitekey)) {
2303 $suffixinfilename .= '_website_'.$websitekey;
2304 }
2305 }
2306
2307 // Check if we have a forced suffix
2308 if (defined('USESUFFIXINLOG')) {
2309 $suffixinfilename .= constant('USESUFFIXINLOG');
2310 }
2311
2312 if ($ident < 0) {
2313 foreach ($conf->loghandlers as $loghandlerinstance) {
2314 $loghandlerinstance->setIdent($ident);
2315 }
2316 }
2317
2318 if (!empty($message)) {
2319 // Test log level @phan-suppress-next-line PhanPluginDuplicateArrayKey
2320 $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');
2321 if (!array_key_exists($level, $logLevels)) {
2322 throw new Exception('Incorrect log level');
2323 }
2324 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2325 return;
2326 }
2327
2328 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2329 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2330 }
2331
2332 // If adding log inside HTML page is required
2333 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2334 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2335 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2336 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2337
2338 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2339 }
2340
2341 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2342 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2343 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2344 print "\n\n<!-- Log start\n";
2345 print dol_escape_htmltag($message)."\n";
2346 print "Log end -->\n";
2347 }
2348
2349 $data = array(
2350 'message' => $message,
2351 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2352 'level' => $level,
2353 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2354 'ip' => false,
2355 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2356 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2357 );
2358
2359 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2360 if (!empty($remoteip)) {
2361 $data['ip'] = $remoteip;
2362 // This is when server run behind a reverse proxy
2363 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2364 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2365 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2366 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2367 }
2368 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2369 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2370 $data['ip'] = $_SERVER['SERVER_ADDR'];
2371 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2372 // This is when PHP session is ran outside a web server, like from Windows command line (Not always defined, but useful if OS defines it).
2373 $data['ip'] = $_SERVER['COMPUTERNAME'];
2374 } else {
2375 $data['ip'] = '???';
2376 }
2377
2378 if (!empty($_SERVER['USERNAME'])) {
2379 // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2380 $data['osuser'] = $_SERVER['USERNAME'];
2381 } elseif (!empty($_SERVER['LOGNAME'])) {
2382 // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2383 $data['osuser'] = $_SERVER['LOGNAME'];
2384 }
2385
2386 // Loop on each log handler and send output
2387 foreach ($conf->loghandlers as $loghandlerinstance) {
2388 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2389 continue;
2390 }
2391 $loghandlerinstance->export($data, $suffixinfilename);
2392 }
2393 unset($data);
2394 }
2395
2396 if ($ident > 0) {
2397 foreach ($conf->loghandlers as $loghandlerinstance) {
2398 $loghandlerinstance->setIdent($ident);
2399 }
2400 }
2401}
2402
2414function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2415{
2416 global $langs, $db;
2417
2418 $form = new Form($db);
2419
2420 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2421 if (empty($templatenameforexport)) {
2422 $templatenameforexport = 'website_'.$website->ref;
2423 }
2424
2425 $out = '';
2426 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2427
2428 // for generate popup
2429 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2430 $out .= 'jQuery(document).ready(function () {';
2431 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2432 $out .= ' var dialogHtml = \'';
2433
2434 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2435 $dialogcontent .= ' <div style="margin-top: 20px;">';
2436 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2437 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2438 $dialogcontent .= ' </div>';
2439 $dialogcontent .= ' <br>';
2440 $dialogcontent .= ' <div style="margin-top: 20px;">';
2441 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2442 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2443 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2444 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2445 $dialogcontent .= ' <input type="text" autofocus name="export_path" id="export-path-'.$name.'" placeholder="'.$langs->trans('ExportPath').'" style="width:400px " value="'.dol_escape_htmltag($templatenameforexport).'"/><br>';
2446 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2447 $dialogcontent .= ' </form>';
2448 $dialogcontent .= ' </div>';
2449 $dialogcontent .= ' </div>';
2450
2451 $out .= dol_escape_js($dialogcontent);
2452
2453 $out .= '\';';
2454
2455
2456 // Add the content of the dialog to the body of the page
2457 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2458 $out .= ' if ($dialog.length > 0) {
2459 $dialog.remove();
2460 }
2461 jQuery("body").append(dialogHtml);';
2462
2463 // Configuration of popup
2464 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2465 $out .= ' autoOpen: false,';
2466 $out .= ' modal: true,';
2467 $out .= ' height: 290,';
2468 $out .= ' width: "40%",';
2469 $out .= ' title: "' . dol_escape_js($label) . '",';
2470 $out .= ' });';
2471
2472 // Simulate a click on the original "submit" input to export the site.
2473 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2474 $out .= ' console.log("Clic on exportsite.");';
2475 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2476 $out .= ' console.log("element founded:", target.length > 0);';
2477 $out .= ' if (target.length > 0) { target.click(); }';
2478 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2479 $out .= ' });';
2480
2481 // open popup
2482 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2483 $out .= ' return false;';
2484 $out .= ' });';
2485 $out .= '});';
2486 $out .= '</script>';
2487
2488 return $out;
2489}
2490
2491
2508function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2509{
2510 global $conf;
2511
2512 if (strpos($url, '?') > 0) {
2513 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2514 } else {
2515 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2516 }
2517
2518 $out = '';
2519
2520 $backtopagejsfieldsid = '';
2521 $backtopagejsfieldslabel = '';
2522 if ($backtopagejsfields) {
2523 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2524 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2525 $backtopagejsfields = $name.":".$backtopagejsfields;
2526 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2527 } else {
2528 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2529 }
2530 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2531 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2532 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2533 }
2534
2535 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2536 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2537 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2538 if (empty($conf->use_javascript_ajax)) {
2539 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2540 } elseif ($jsonopen) {
2541 $out .= ' href="#" onclick="'.$jsonopen.'"';
2542 } else {
2543 $out .= ' href="#"';
2544 }
2545 $out .= '>'.$buttonstring.'</a>';
2546
2547 if (!empty($conf->use_javascript_ajax)) {
2548 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2549 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2550 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2551 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2552 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2553
2554 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2555 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2556 jQuery(document).ready(function () {
2557 jQuery(".button_'.$name.'").click(function () {
2558 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2559 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2560 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2561 $tmpdialog.dialog({
2562 autoOpen: false,
2563 modal: true,
2564 height: (window.innerHeight - 150),
2565 width: \'80%\',
2566 title: \''.dol_escape_js($label).'\',
2567 open: function (event, ui) {
2568 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2569 },
2570 close: function (event, ui) {
2571 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2572 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2573 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2574 if (returnedid != "" && returnedid != "div for returned id") {
2575 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2576 }
2577 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2578 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2579 }
2580 }
2581 });
2582
2583 $tmpdialog.dialog(\'open\');
2584 return false;
2585 });
2586 });
2587 </script>';
2588 }
2589 return $out;
2590}
2591
2608function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2609{
2610 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2611}
2612
2629function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2630{
2631 global $conf, $langs, $hookmanager;
2632
2633 // Show title
2634 $showtitle = 1;
2635 if (!empty($conf->dol_optimize_smallscreen)) {
2636 $showtitle = 0;
2637 }
2638
2639 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2640
2641 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2642 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2643 }
2644
2645 // Show right part
2646 if ($morehtmlright) {
2647 $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.
2648 }
2649
2650 // Show title
2651 /*
2652 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2653 $limittitle = 30;
2654 $out .= '<a class="tabTitle">';
2655 if ($picto) {
2656 $noprefix = $pictoisfullpath;
2657 if (strpos($picto, 'fontawesome_') !== false) {
2658 $noprefix = 1;
2659 }
2660 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2661 }
2662 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2663 $out .= '</a>';
2664 }
2665 */
2666
2667 // Show tabs
2668
2669 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2670 $maxkey = -1;
2671 if (is_array($links) && !empty($links)) {
2672 $keys = array_keys($links);
2673 if (count($keys)) {
2674 $maxkey = max($keys);
2675 }
2676 }
2677
2678 // Show tabs
2679 // if =0 we don't use the feature
2680 if (empty($limittoshow)) {
2681 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2682 }
2683 if (!empty($conf->dol_optimize_smallscreen)) {
2684 $limittoshow = 2;
2685 }
2686
2687 $displaytab = 0;
2688 $nbintab = 0;
2689 $popuptab = 0;
2690 $outmore = '';
2691 for ($i = 0; $i <= $maxkey; $i++) {
2692 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2693 // If active tab is already present
2694 if ($i >= $limittoshow) {
2695 $limittoshow--;
2696 }
2697 }
2698 }
2699
2700 for ($i = 0; $i <= $maxkey; $i++) {
2701 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2702 $isactive = true;
2703 } else {
2704 $isactive = false;
2705 }
2706
2707 if ($i < $limittoshow || $isactive) {
2708 // Output entry with a visible tab
2709 $out .= '<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((!$isactive && getDolGlobalString('MAIN_HIDE_INACTIVETAB_ON_PRINT')) ? ' hideonprint' : '').'"><!-- id tab = '.(empty($links[$i][2]) ? '' : dol_escape_htmltag($links[$i][2])).' -->';
2710
2711 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2712 if (!empty($links[$i][0])) {
2713 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2714 } else {
2715 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2716 }
2717 } elseif (!empty($links[$i][1])) {
2718 //print "x $i $active ".$links[$i][2]." z";
2719 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2720
2721 if (!empty($links[$i][0])) {
2722 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2723 $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).'">';
2724 }
2725
2726 if ($displaytab == 0 && $picto) {
2727 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2728 }
2729
2730 $out .= $links[$i][1];
2731 if (!empty($links[$i][0])) {
2732 $out .= '</a>'."\n";
2733 }
2734 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2735 $out .= '</div>';
2736 }
2737
2738 $out .= '</div>';
2739 } else {
2740 // Add entry into the combo popup with the other tabs
2741 if (!$popuptab) {
2742 $popuptab = 1;
2743 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2744 }
2745 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2746 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2747 if (!empty($links[$i][0])) {
2748 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2749 } else {
2750 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2751 }
2752 } elseif (!empty($links[$i][1])) {
2753 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2754 $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.
2755 $outmore .= '</a>'."\n";
2756 }
2757 $outmore .= '</div>';
2758
2759 $nbintab++;
2760 }
2761
2762 $displaytab = $i + 1;
2763 }
2764 if ($popuptab) {
2765 $outmore .= '</div>';
2766 }
2767
2768 if ($popuptab) { // If there is some tabs not shown
2769 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2770 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2771 $widthofpopup = 200;
2772
2773 $tabsname = $moretabssuffix;
2774 if (empty($tabsname)) {
2775 $tabsname = str_replace("@", "", $picto);
2776 }
2777 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2778 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2779 $out .= '<div class="tab valignmiddle"><a href="#" class="tab moretab inline-block tabunactive valignmiddle"><span class="hideonsmartphone">'.$langs->trans("More").'</span>... ('.$nbintab.')</a></div>'; // Do not use "reposition" class in the "More".
2780 }
2781 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2782 $out .= $outmore;
2783 $out .= '</div>';
2784 $out .= '<div></div>';
2785 $out .= "</div>\n";
2786
2787 $out .= '<script nonce="'.getNonce().'">';
2788 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2789 var x = this.offsetLeft, y = this.offsetTop;
2790 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2791 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2792 $('#moretabsList".$tabsname."').css('".$right."','8px');
2793 }
2794 $('#moretabsList".$tabsname."').css('".$left."','auto');
2795 });
2796 ";
2797 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2798 $out .= "</script>";
2799 }
2800
2801 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2802 $out .= "</div>\n";
2803 }
2804
2805 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
2806 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
2807 $out .= '">'."\n";
2808 }
2809 if (!empty($dragdropfile)) {
2810 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2811 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2812 }
2813 $parameters = array('tabname' => $active, 'out' => $out);
2814 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2815 if ($reshook > 0) {
2816 $out = $hookmanager->resPrint;
2817 }
2818
2819 return $out;
2820}
2821
2829function dol_fiche_end($notab = 0)
2830{
2831 print dol_get_fiche_end($notab);
2832}
2833
2840function dol_get_fiche_end($notab = 0)
2841{
2842 if (!$notab || $notab == -1) {
2843 return "\n</div>\n";
2844 } else {
2845 return '';
2846 }
2847}
2848
2868function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2869{
2870 global $conf, $form, $user, $langs, $hookmanager, $action;
2871
2872 $error = 0;
2873
2874 $maxvisiblephotos = 1;
2875 $showimage = 1;
2876 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2877 // @phan-suppress-next-line PhanUndeclaredMethod
2878 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2879 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2880 $showbarcode = 0;
2881 }
2882 $modulepart = 'unknown';
2883
2884 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2885 $modulepart = $object->element;
2886 } elseif ($object->element == 'member') {
2887 $modulepart = 'memberphoto';
2888 } elseif ($object->element == 'user') {
2889 $modulepart = 'userphoto';
2890 }
2891
2892 if (class_exists("Imagick")) {
2893 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2894 $modulepart = $object->element;
2895 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2896 $modulepart = 'ficheinter';
2897 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2898 $modulepart = 'contract';
2899 } elseif ($object->element == 'order_supplier') {
2900 $modulepart = 'supplier_order';
2901 } elseif ($object->element == 'invoice_supplier') {
2902 $modulepart = 'supplier_invoice';
2903 }
2904 }
2905
2906 if ($object->element == 'product') {
2908 '@phan-var-force Product $object';
2909 $width = 80;
2910 $cssclass = 'photowithmargin photoref';
2911 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2912 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2913 if ($conf->browser->layout == 'phone') {
2914 $maxvisiblephotos = 1;
2915 }
2916 if ($showimage) {
2917 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity], 1, $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2918 } else {
2919 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2920 $nophoto = '';
2921 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2922 } else { // Show no photo link
2923 $nophoto = '/public/theme/common/nophoto.png';
2924 $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>';
2925 }
2926 }
2927 } elseif ($object->element == 'category') {
2929 '@phan-var-force Categorie $object';
2930 $width = 80;
2931 $cssclass = 'photowithmargin photoref';
2932 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2933 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2934 if ($conf->browser->layout == 'phone') {
2935 $maxvisiblephotos = 1;
2936 }
2937 if ($showimage) {
2938 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('category', $conf->categorie->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2939 } else {
2940 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2941 $nophoto = '';
2942 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2943 } else { // Show no photo link
2944 $nophoto = '/public/theme/common/nophoto.png';
2945 $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>';
2946 }
2947 }
2948 } elseif ($object->element == 'bom') {
2950 '@phan-var-force Bom $object';
2951 $width = 80;
2952 $cssclass = 'photowithmargin photoref';
2953 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2954 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2955 if ($conf->browser->layout == 'phone') {
2956 $maxvisiblephotos = 1;
2957 }
2958 if ($showimage) {
2959 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('bom', $conf->bom->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2960 } else {
2961 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2962 $nophoto = '';
2963 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2964 } else { // Show no photo link
2965 $nophoto = '/public/theme/common/nophoto.png';
2966 $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>';
2967 }
2968 }
2969 } elseif ($object->element == 'ticket') {
2970 $width = 80;
2971 $cssclass = 'photoref';
2973 '@phan-var-force Ticket $object';
2974 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2975 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2976 if ($conf->browser->layout == 'phone') {
2977 $maxvisiblephotos = 1;
2978 }
2979
2980 if ($showimage) {
2981 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2982 if ($object->nbphoto > 0) {
2983 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2984 } else {
2985 $showimage = 0;
2986 }
2987 }
2988 if (!$showimage) {
2989 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2990 $nophoto = '';
2991 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2992 } else { // Show no photo link
2993 $nophoto = img_picto('No photo', 'object_ticket');
2994 $morehtmlleft .= '<!-- No photo to show -->';
2995 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2996 $morehtmlleft .= $nophoto;
2997 $morehtmlleft .= '</div></div>';
2998 }
2999 }
3000 } else {
3001 if ($showimage) {
3002 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3003 $phototoshow = '';
3004 // Check if a preview file is available
3005 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3006 $objectref = dol_sanitizeFileName($object->ref);
3007 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
3008 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3009 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3010 $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
3011 } else {
3012 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3013 }
3014 if (empty($subdir)) {
3015 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3016 }
3017
3018 $filepath = $dir_output.$subdir."/";
3019
3020 $filepdf = $filepath.$objectref.".pdf";
3021 $relativepath = $subdir.'/'.$objectref.'.pdf';
3022
3023 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3024 $fileimage = $filepdf.'_preview.png';
3025 $relativepathimage = $relativepath.'_preview.png';
3026
3027 $pdfexists = file_exists($filepdf);
3028
3029 // If PDF file exists
3030 if ($pdfexists) {
3031 // Conversion du PDF en image png si fichier png non existent
3032 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3033 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3034 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3035 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3036 if ($ret < 0) {
3037 $error++;
3038 }
3039 }
3040 }
3041 }
3042
3043 if ($pdfexists && !$error) {
3044 $heightforphotref = 80;
3045 if (!empty($conf->dol_optimize_smallscreen)) {
3046 $heightforphotref = 60;
3047 }
3048 // If the preview file is found
3049 if (file_exists($fileimage)) {
3050 $phototoshow = '<div class="photoref">';
3051 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3052 $phototoshow .= '</div>';
3053 }
3054 }
3055 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3056 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
3057 }
3058
3059 if ($phototoshow) {
3060 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3061 $morehtmlleft .= $phototoshow;
3062 $morehtmlleft .= '</div>';
3063 }
3064 }
3065
3066 if (empty($phototoshow)) { // Show No photo link (picto of object)
3067 if ($object->element == 'action') {
3068 $width = 80;
3069 $cssclass = 'photorefcenter';
3070 $nophoto = img_picto('No photo', 'title_agenda');
3071 } else {
3072 $width = 14;
3073 $cssclass = 'photorefcenter';
3074 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3075 $prefix = 'object_';
3076 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3077 $picto = 'project'; // instead of projectpub
3078 }
3079 if (strpos($picto, 'fontawesome_') !== false) {
3080 $prefix = '';
3081 }
3082 $nophoto = img_picto('No photo', $prefix.$picto);
3083 }
3084 $morehtmlleft .= '<!-- No photo to show -->';
3085 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3086 $morehtmlleft .= $nophoto;
3087 $morehtmlleft .= '</div></div>';
3088 }
3089 }
3090 }
3091
3092 // Show barcode
3093 if ($showbarcode) {
3094 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3095 }
3096
3097 if ($object->element == 'societe') {
3098 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3099 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3100 } else {
3101 $morehtmlstatus .= $object->getLibStatut(6);
3102 }
3103 } elseif ($object->element == 'product') {
3104 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3105 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3106 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3107 } else {
3108 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3109 }
3110 $morehtmlstatus .= ' &nbsp; ';
3111 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3112 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3113 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3114 } else {
3115 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3116 }
3117 } elseif (in_array($object->element, array('salary'))) {
3118 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3119 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3120 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3121 }
3122 $morehtmlstatus .= $tmptxt;
3123 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3124 $totalallpayments = $object->getSommePaiement(0);
3125 $totalallpayments += $object->getSumCreditNotesUsed(0);
3126 $totalallpayments += $object->getSumDepositsUsed(0);
3127 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3128 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3129 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3130 }
3131 $morehtmlstatus .= $tmptxt;
3132 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3133 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3134 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3135 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3136 }
3137 $morehtmlstatus .= $tmptxt;
3138 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3139 if ($object->statut == 0) {
3140 $morehtmlstatus .= $object->getLibStatut(5);
3141 } else {
3142 $morehtmlstatus .= $object->getLibStatut(4);
3143 }
3144 } elseif ($object->element == 'facturerec') {
3145 '@phan-var-force FactureRec $object';
3146 if ($object->frequency == 0) {
3147 $morehtmlstatus .= $object->getLibStatut(2);
3148 } else {
3149 $morehtmlstatus .= $object->getLibStatut(5);
3150 }
3151 } elseif ($object->element == 'project_task') {
3152 $object->fk_statut = 1;
3153 $object->status = 1;
3154 if ($object->progress > 0) {
3155 $object->fk_statut = 2;
3156 $object->status = 2;
3157 }
3158 if ($object->progress >= 100) {
3159 $object->fk_statut = 3;
3160 $object->status = 3;
3161 }
3162 $tmptxt = $object->getLibStatut(5);
3163 $morehtmlstatus .= $tmptxt; // No status on task
3164 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3165 $tmptxt = $object->getLibStatut(6);
3166 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3167 $tmptxt = $object->getLibStatut(5);
3168 }
3169 $morehtmlstatus .= $tmptxt;
3170 }
3171
3172 // Add if object was dispatched "into accountancy"
3173 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3174 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3175 if (method_exists($object, 'getVentilExportCompta')) {
3176 $accounted = $object->getVentilExportCompta();
3177 $langs->load("accountancy");
3178 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3179 }
3180 }
3181
3182 // Add alias for thirdparty
3183 if (!empty($object->name_alias)) {
3184 '@phan-var-force Societe $object';
3185 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3186 }
3187
3188 // Add label
3189 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3190 if (!empty($object->label)) {
3191 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3192 }
3193 }
3194 // Show address and email
3195 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3196 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3197 if ($moreaddress) {
3198 $morehtmlref .= '<div class="refidno refaddress">';
3199 $morehtmlref .= $moreaddress;
3200 $morehtmlref .= '</div>';
3201 }
3202 }
3203 if (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id)) {
3204 $morehtmlref .= '<div style="clear: both;"></div>';
3205 $morehtmlref .= '<div class="refidno opacitymedium">';
3206 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3207 $morehtmlref .= '</div>';
3208 }
3209
3210 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3211 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3212 if ($reshook < 0) {
3213 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3214 } elseif (empty($reshook)) {
3215 $morehtmlref .= $hookmanager->resPrint;
3216 } elseif ($reshook > 0) {
3217 $morehtmlref = $hookmanager->resPrint;
3218 }
3219
3220 // $morehtml is the right part (link "Back to list")
3221 // $morehtmlleft is the picto or photo of banner
3222 // $morehtmlstatus is part under the status
3223 // $morehtmlright is part of htmlright
3224
3225 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3226 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3227 print '</div>';
3228 print '<div class="underrefbanner clearboth"></div>';
3229}
3230
3240function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3241{
3242 global $langs;
3243 $ret = '';
3244 if ($fieldrequired) {
3245 $ret .= '<span class="fieldrequired">';
3246 }
3247 $ret .= '<label for="'.$fieldkey.'">';
3248 $ret .= $langs->trans($langkey);
3249 $ret .= '</label>';
3250 if ($fieldrequired) {
3251 $ret .= '</span>';
3252 }
3253 return $ret;
3254}
3255
3269function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3270{
3271 global $langs, $hookmanager;
3272
3273 $ret = '';
3274 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3275
3276 // See format of addresses on https://en.wikipedia.org/wiki/Address
3277 // Address
3278 if (empty($mode)) {
3279 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3280 }
3281 // Zip/Town/State
3282 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3283 // US: title firstname name \n address lines \n town, state, zip \n country
3284 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3285 $ret .= (($ret && $town) ? $sep : '').$town;
3286
3287 if (!empty($object->state)) {
3288 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3289 }
3290 if (!empty($object->zip)) {
3291 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3292 }
3293 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3294 // UK: title firstname name \n address lines \n town state \n zip \n country
3295 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3296 $ret .= ($ret ? $sep : '').$town;
3297 if (!empty($object->state)) {
3298 $ret .= ($ret ? ", " : '').$object->state;
3299 }
3300 if (!empty($object->zip)) {
3301 $ret .= ($ret ? $sep : '').$object->zip;
3302 }
3303 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3304 // ES: title firstname name \n address lines \n zip town \n state \n country
3305 $ret .= ($ret ? $sep : '').$object->zip;
3306 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3307 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3308 if (!empty($object->state)) {
3309 $ret .= $sep.$object->state;
3310 }
3311 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3312 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3313 // See https://www.sljfaq.org/afaq/addresses.html
3314 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3315 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3316 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3317 // IT: title firstname name\n address lines \n zip town state_code \n country
3318 $ret .= ($ret ? $sep : '').$object->zip;
3319 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3320 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3321 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3322 } else {
3323 // Other: title firstname name \n address lines \n zip town[, state] \n country
3324 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3325 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3326 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3327 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3328 $ret .= ($ret ? ", " : '').$object->state;
3329 }
3330 }
3331
3332 if (!is_object($outputlangs)) {
3333 $outputlangs = $langs;
3334 }
3335 if ($withcountry) {
3336 $langs->load("dict");
3337 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3338 }
3339 if ($hookmanager) {
3340 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3341 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3342 if ($reshook > 0) {
3343 $ret = '';
3344 }
3345 $ret .= $hookmanager->resPrint;
3346 }
3347
3348 return $ret;
3349}
3350
3351
3352
3362function dol_strftime($fmt, $ts = false, $is_gmt = false)
3363{
3364 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3365 return dol_print_date($ts, $fmt, $is_gmt);
3366 } else {
3367 return 'Error date outside supported range';
3368 }
3369}
3370
3392function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3393{
3394 global $conf, $langs;
3395
3396 // If date undefined or "", we return ""
3397 if (dol_strlen($time) == 0) {
3398 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3399 }
3400
3401 if ($tzoutput === 'auto') {
3402 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3403 }
3404
3405 // Clean parameters
3406 $to_gmt = false;
3407 $offsettz = $offsetdst = 0;
3408 if ($tzoutput) {
3409 $to_gmt = true; // For backward compatibility
3410 if (is_string($tzoutput)) {
3411 if ($tzoutput == 'tzserver') {
3412 $to_gmt = false;
3413 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3414 // @phan-suppress-next-line PhanPluginRedundantAssignment
3415 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3416 // @phan-suppress-next-line PhanPluginRedundantAssignment
3417 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3418 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3419 $to_gmt = true;
3420 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3421
3422 if (class_exists('DateTimeZone')) {
3423 $user_date_tz = new DateTimeZone($offsettzstring);
3424 $user_dt = new DateTime();
3425 $user_dt->setTimezone($user_date_tz);
3426 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3427 $offsettz = $user_dt->getOffset(); // should include dst ?
3428 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3429 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3430 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3431 }
3432 }
3433 }
3434 }
3435 if (!is_object($outputlangs)) {
3436 $outputlangs = $langs;
3437 }
3438 if (!$format) {
3439 $format = 'daytextshort';
3440 }
3441
3442 // Do we have to reduce the length of date (year on 2 chars) to save space.
3443 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3444 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3445 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3446 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3447 if ($formatwithoutreduce != $format) {
3448 $format = $formatwithoutreduce;
3449 $reduceformat = 1;
3450 } // so format 'dayreduceformat' is processed like day
3451
3452 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3453 // TODO Add format daysmallyear and dayhoursmallyear
3454 if ($format == 'day') {
3455 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3456 } elseif ($format == 'hour') {
3457 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3458 } elseif ($format == 'hourduration') {
3459 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3460 } elseif ($format == 'daytext') {
3461 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3462 } elseif ($format == 'daytextshort') {
3463 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3464 } elseif ($format == 'dayhour') {
3465 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3466 } elseif ($format == 'dayhoursec') {
3467 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3468 } elseif ($format == 'dayhourtext') {
3469 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3470 } elseif ($format == 'dayhourtextshort') {
3471 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3472 } elseif ($format == 'dayhourlog') {
3473 // Format not sensitive to language
3474 $format = '%Y%m%d%H%M%S';
3475 } elseif ($format == 'dayhourlogsmall') {
3476 // Format not sensitive to language
3477 $format = '%y%m%d%H%M';
3478 } elseif ($format == 'dayhourldap') {
3479 $format = '%Y%m%d%H%M%SZ';
3480 } elseif ($format == 'dayhourxcard') {
3481 $format = '%Y%m%dT%H%M%SZ';
3482 } elseif ($format == 'dayxcard') {
3483 $format = '%Y%m%d';
3484 } elseif ($format == 'dayrfc') {
3485 $format = '%Y-%m-%d'; // DATE_RFC3339
3486 } elseif ($format == 'dayhourrfc') {
3487 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3488 } elseif ($format == 'standard') {
3489 $format = '%Y-%m-%d %H:%M:%S';
3490 }
3491
3492 if ($reduceformat) {
3493 $format = str_replace('%Y', '%y', $format);
3494 $format = str_replace('yyyy', 'yy', $format);
3495 }
3496
3497 // Clean format
3498 if (preg_match('/%b/i', $format)) { // There is some text to translate
3499 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3500 $format = str_replace('%b', '__b__', $format);
3501 $format = str_replace('%B', '__B__', $format);
3502 }
3503 if (preg_match('/%a/i', $format)) { // There is some text to translate
3504 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3505 $format = str_replace('%a', '__a__', $format);
3506 $format = str_replace('%A', '__A__', $format);
3507 }
3508
3509 // Analyze date
3510 $reg = array();
3511 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', (string) $time, $reg)) { // Deprecated. Ex: 1970-01-01, 1970-01-01 01:00:00, 19700101010000
3512 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]));
3513 return '';
3514 } elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', (string) $time, $reg)) { // Still available to solve problems in extrafields of type date
3515 // This part of code should not be used anymore.
3516 dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]), LOG_WARNING);
3517 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3518 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3519 $syear = (!empty($reg[1]) ? $reg[1] : '');
3520 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3521 $sday = (!empty($reg[3]) ? $reg[3] : '');
3522 $shour = (!empty($reg[4]) ? $reg[4] : '');
3523 $smin = (!empty($reg[5]) ? $reg[5] : '');
3524 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3525
3526 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3527
3528 if ($to_gmt) {
3529 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3530 } else {
3531 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3532 }
3533 $dtts = new DateTime();
3534 $dtts->setTimestamp($time);
3535 $dtts->setTimezone($tzo);
3536 $newformat = str_replace(
3537 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3538 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3539 $format
3540 );
3541 $ret = $dtts->format($newformat);
3542 $ret = str_replace(
3543 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3544 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3545 $ret
3546 );
3547 } else {
3548 // Date is a timestamps
3549 if ($time < 100000000000) { // Protection against bad date values
3550 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3551
3552 if ($to_gmt) {
3553 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3554 } else {
3555 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3556 }
3557 $dtts = new DateTime();
3558 $dtts->setTimestamp($timetouse);
3559 $dtts->setTimezone($tzo);
3560 $newformat = str_replace(
3561 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3562 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3563 $format
3564 );
3565 $ret = $dtts->format($newformat);
3566 $ret = str_replace(
3567 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3568 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3569 $ret
3570 );
3571 //var_dump($ret);exit;
3572 } else {
3573 $ret = 'Bad value '.$time.' for date';
3574 }
3575 }
3576
3577 if (preg_match('/__b__/i', $format)) {
3578 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3579
3580 if ($to_gmt) {
3581 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3582 } else {
3583 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3584 }
3585 $dtts = new DateTime();
3586 $dtts->setTimestamp($timetouse);
3587 $dtts->setTimezone($tzo);
3588 $month = (int) $dtts->format("m");
3589 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3590 if ($encodetooutput) {
3591 $monthtext = $outputlangs->transnoentities('Month'.$month);
3592 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3593 } else {
3594 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3595 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3596 }
3597 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3598 $ret = str_replace('__b__', $monthtextshort, $ret);
3599 $ret = str_replace('__B__', $monthtext, $ret);
3600 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3601 //return $ret;
3602 }
3603 if (preg_match('/__a__/i', $format)) {
3604 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3605 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3606
3607 if ($to_gmt) {
3608 $tzo = new DateTimeZone('UTC');
3609 } else {
3610 $tzo = new DateTimeZone(date_default_timezone_get());
3611 }
3612 $dtts = new DateTime();
3613 $dtts->setTimestamp($timetouse);
3614 $dtts->setTimezone($tzo);
3615 $w = $dtts->format("w");
3616 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3617
3618 $ret = str_replace('__A__', $dayweek, $ret);
3619 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3620 }
3621
3622 return $ret;
3623}
3624
3625
3646function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3647{
3648 if ($timestamp === '') {
3649 return array();
3650 }
3651
3652 $datetimeobj = new DateTime();
3653 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3654 if ($forcetimezone) {
3655 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3656 }
3657 $arrayinfo = array(
3658 'year' => ((int) date_format($datetimeobj, 'Y')),
3659 'mon' => ((int) date_format($datetimeobj, 'm')),
3660 'mday' => ((int) date_format($datetimeobj, 'd')),
3661 'wday' => ((int) date_format($datetimeobj, 'w')),
3662 'yday' => ((int) date_format($datetimeobj, 'z')),
3663 'hours' => ((int) date_format($datetimeobj, 'H')),
3664 'minutes' => ((int) date_format($datetimeobj, 'i')),
3665 'seconds' => ((int) date_format($datetimeobj, 's')),
3666 '0' => $timestamp
3667 );
3668
3669 return $arrayinfo;
3670}
3671
3693function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3694{
3695 global $conf;
3696 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3697
3698 if ($gm === 'auto') {
3699 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3700 }
3701 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3702
3703 // Clean parameters
3704 if ($hour == -1 || empty($hour)) {
3705 $hour = 0;
3706 }
3707 if ($minute == -1 || empty($minute)) {
3708 $minute = 0;
3709 }
3710 if ($second == -1 || empty($second)) {
3711 $second = 0;
3712 }
3713
3714 // Check parameters
3715 if ($check) {
3716 if (!$month || !$day) {
3717 return '';
3718 }
3719 if ($day > 31) {
3720 return '';
3721 }
3722 if ($month > 12) {
3723 return '';
3724 }
3725 if ($hour < 0 || $hour > 24) {
3726 return '';
3727 }
3728 if ($minute < 0 || $minute > 60) {
3729 return '';
3730 }
3731 if ($second < 0 || $second > 60) {
3732 return '';
3733 }
3734 }
3735
3736 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3737 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3738 $localtz = new DateTimeZone($default_timezone);
3739 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3740 // We use dol_tz_string first because it is more reliable.
3741 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3742 try {
3743 $localtz = new DateTimeZone($default_timezone);
3744 } catch (Exception $e) {
3745 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3746 $default_timezone = @date_default_timezone_get();
3747 }
3748 } elseif (strrpos($gm, "tz,") !== false) {
3749 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3750 try {
3751 $localtz = new DateTimeZone($timezone);
3752 } catch (Exception $e) {
3753 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3754 }
3755 }
3756
3757 if (empty($localtz)) {
3758 $localtz = new DateTimeZone('UTC');
3759 }
3760 //var_dump($localtz);
3761 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3762 $dt = new DateTime('now', $localtz);
3763 $dt->setDate((int) $year, (int) $month, (int) $day);
3764 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3765 $date = $dt->getTimestamp(); // should include daylight saving time
3766 //var_dump($date);
3767 return $date;
3768}
3769
3770
3781function dol_now($mode = 'auto')
3782{
3783 $ret = 0;
3784
3785 if ($mode === 'auto') {
3786 $mode = 'gmt';
3787 }
3788
3789 if ($mode == 'gmt') {
3790 $ret = time(); // Time for now at greenwich.
3791 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3792 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3793 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3794 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3795 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3796 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3797 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3798 // $ret=dol_now('gmt')+($tzsecond*3600);
3799 //}
3800 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3801 // Time for now with user timezone added
3802 //print 'time: '.time();
3803 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3804 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3805 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3806 }
3807
3808 return $ret;
3809}
3810
3811
3820function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3821{
3822 global $conf, $langs;
3823 $level = 1024;
3824
3825 if (!empty($conf->dol_optimize_smallscreen)) {
3826 $shortunit = 1;
3827 }
3828
3829 // Set value text
3830 if (empty($shortvalue) || $size < ($level * 10)) {
3831 $ret = $size;
3832 $textunitshort = $langs->trans("b");
3833 $textunitlong = $langs->trans("Bytes");
3834 } else {
3835 $ret = round($size / $level, 0);
3836 $textunitshort = $langs->trans("Kb");
3837 $textunitlong = $langs->trans("KiloBytes");
3838 }
3839 // Use long or short text unit
3840 if (empty($shortunit)) {
3841 $ret .= ' '.$textunitlong;
3842 } else {
3843 $ret .= ' '.$textunitshort;
3844 }
3845
3846 return $ret;
3847}
3848
3859function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3860{
3861 global $langs;
3862
3863 if (empty($url)) {
3864 return '';
3865 }
3866
3867 $linkstart = '<a href="';
3868 if (!preg_match('/^http/i', $url)) {
3869 $linkstart .= 'http://';
3870 }
3871 $linkstart .= $url;
3872 $linkstart .= '"';
3873 if ($target) {
3874 $linkstart .= ' target="'.$target.'"';
3875 }
3876 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3877 $linkstart .= '>';
3878
3879 $link = '';
3880 if (!preg_match('/^http/i', $url)) {
3881 $link .= 'http://';
3882 }
3883 $link .= dol_trunc($url, $max);
3884
3885 $linkend = '</a>';
3886
3887 if ($morecss == 'float') { // deprecated
3888 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3889 } else {
3890 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3891 }
3892}
3893
3907function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
3908{
3909 global $user, $langs, $hookmanager;
3910
3911 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3912 //$showinvalid = 1; $email = 'rrrrr';
3913
3914 $newemail = dol_escape_htmltag($email);
3915
3916 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3917 $withpicto = 0;
3918 }
3919
3920 if (empty($email)) {
3921 return '&nbsp;';
3922 }
3923
3924 if ($addlink == 1) {
3925 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
3926 if (!preg_match('/^mailto:/i', $email)) {
3927 $newemail .= 'mailto:';
3928 }
3929 $newemail .= $email;
3930 $newemail .= '" target="_blank">';
3931
3932 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3933
3934 if ($max > 0) {
3935 $newemail .= dol_trunc($email, $max);
3936 } else {
3937 $newemail .= $email;
3938 }
3939 $newemail .= '</a>';
3940 if ($showinvalid && !isValidEmail($email)) {
3941 $langs->load("errors");
3942 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3943 }
3944
3945 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3946 $type = 'AC_EMAIL';
3947 $linktoaddaction = '';
3948 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3949 $linktoaddaction = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.urlencode($type).'&amp;contactid='.((int) $cid).'&amp;socid='.((int) $socid).'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3950 }
3951 if ($linktoaddaction) {
3952 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3953 }
3954 }
3955 } elseif ($addlink === 'thirdparty') {
3956 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
3957 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3958 if ($withpicto == 1) {
3959 $tmpnewemail .= $newemail;
3960 }
3961 $tmpnewemail .= '</a>';
3962
3963 $newemail = $tmpnewemail;
3964 } else {
3965 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3966
3967 if ($showinvalid && !isValidEmail($email)) {
3968 $langs->load("errors");
3969 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3970 }
3971 }
3972
3973 //$rep = '<div class="nospan" style="margin-right: 10px">';
3974 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3975 //$rep .= '</div>';
3976 $rep = $newemail;
3977
3978 if ($hookmanager) {
3979 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3980
3981 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3982 if ($reshook > 0) {
3983 $rep = '';
3984 }
3985 $rep .= $hookmanager->resPrint;
3986 }
3987
3988 return $rep;
3989}
3990
3996function getArrayOfSocialNetworks()
3997{
3998 global $conf, $db;
3999
4000 $socialnetworks = array();
4001 // Enable caching of array
4002 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
4003 $cachekey = 'socialnetworks_' . $conf->entity;
4004 $dataretrieved = dol_getcache($cachekey);
4005 if (!is_null($dataretrieved)) {
4006 $socialnetworks = $dataretrieved;
4007 } else {
4008 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
4009 $sql .= " WHERE entity=".$conf->entity;
4010 $resql = $db->query($sql);
4011 if ($resql) {
4012 while ($obj = $db->fetch_object($resql)) {
4013 $socialnetworks[$obj->code] = array(
4014 'rowid' => $obj->rowid,
4015 'label' => $obj->label,
4016 'url' => $obj->url,
4017 'icon' => $obj->icon,
4018 'active' => $obj->active,
4019 );
4020 }
4021 }
4022 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4023 }
4024 return $socialnetworks;
4025}
4026
4037function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
4038{
4039 global $hookmanager, $langs, $user;
4040
4041 $htmllink = $value;
4042
4043 if (empty($value)) {
4044 return '&nbsp;';
4045 }
4046
4047 if (!empty($type)) {
4048 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4049 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4050 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
4051 if ($type == 'skype') {
4052 $htmllink .= dol_escape_htmltag($value);
4053 $htmllink .= '&nbsp; <a href="skype:';
4054 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4055 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
4056 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
4057 $htmllink .= '</a><a href="skype:';
4058 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4059 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
4060 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
4061 $htmllink .= '</a>';
4062 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4063 $addlink = 'AC_SKYPE';
4064 $link = '';
4065 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4066 $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>';
4067 }
4068 $htmllink .= ($link ? ' '.$link : '');
4069 }
4070 } else {
4071 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
4072 if (getDolGlobalString($networkconstname)) {
4073 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
4074 if (preg_match('/^https?:\/\//i', $link)) {
4075 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4076 } elseif ($link) {
4077 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4078 }
4079 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
4080 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4081 if ($tmpvirginurl) {
4082 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4083 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4084
4085 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4086 if ($tmpvirginurl3) {
4087 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4088 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4089 }
4090
4091 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4092 if ($tmpvirginurl2) {
4093 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4094 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4095 }
4096 }
4097 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4098 if (preg_match('/^https?:\/\//i', $link)) {
4099 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4100 } else {
4101 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4102 }
4103 } else {
4104 $htmllink .= dol_escape_htmltag($value);
4105 }
4106 }
4107 $htmllink .= '</div>';
4108 } else {
4109 $langs->load("errors");
4110 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4111 }
4112
4113 if ($hookmanager) {
4114 $parameters = array(
4115 'value' => $value,
4116 'cid' => $cid,
4117 'socid' => $socid,
4118 'type' => $type,
4119 'dictsocialnetworks' => $dictsocialnetworks,
4120 );
4121
4122 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4123 if ($reshook > 0) {
4124 $htmllink = '';
4125 }
4126 $htmllink .= $hookmanager->resPrint;
4127 }
4128
4129 return $htmllink;
4130}
4131
4141function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4142{
4143 global $mysoc;
4144
4145 if (empty($profID) || empty($profIDtype)) {
4146 return '';
4147 }
4148 if (empty($countrycode)) {
4149 $countrycode = $mysoc->country_code;
4150 }
4151 $newProfID = $profID;
4152 $id = substr($profIDtype, -1);
4153 $ret = '';
4154 if (strtoupper($countrycode) == 'FR') {
4155 // France
4156 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4157
4158 if ($id == 1 && dol_strlen($newProfID) == 9) {
4159 // SIREN (ex: 123 123 123)
4160 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4161 }
4162 if ($id == 2 && dol_strlen($newProfID) == 14) {
4163 // SIRET (ex: 123 123 123 12345)
4164 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4165 }
4166 if ($id == 3 && dol_strlen($newProfID) == 5) {
4167 // NAF/APE (ex: 69.20Z)
4168 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4169 }
4170 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4171 // TVA intracommunautaire (ex: FR12 123 123 123)
4172 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4173 }
4174 }
4175 if (!empty($addcpButton)) {
4176 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4177 } else {
4178 $ret = $newProfID;
4179 }
4180 return $ret;
4181}
4182
4198function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4199{
4200 global $conf, $user, $langs, $mysoc, $hookmanager;
4201
4202 // Clean phone parameter
4203 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4204 if (empty($phone)) {
4205 return '';
4206 }
4207 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4208 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4209 }
4210 if (empty($countrycode) && is_object($mysoc)) {
4211 $countrycode = $mysoc->country_code;
4212 }
4213
4214 // Short format for small screens
4215 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4216 $separ = '';
4217 }
4218
4219 $newphone = $phone;
4220 $newphonewa = $phone;
4221 if (strtoupper($countrycode) == "FR") {
4222 // France
4223 if (dol_strlen($phone) == 10) {
4224 $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);
4225 } elseif (dol_strlen($phone) == 7) {
4226 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4227 } elseif (dol_strlen($phone) == 9) {
4228 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4229 } elseif (dol_strlen($phone) == 11) {
4230 $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);
4231 } elseif (dol_strlen($phone) == 12) {
4232 $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);
4233 } elseif (dol_strlen($phone) == 13) {
4234 $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);
4235 }
4236 } elseif (strtoupper($countrycode) == "CA") {
4237 if (dol_strlen($phone) == 10) {
4238 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4239 }
4240 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4241 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4242 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4243 }
4244 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4245 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4246 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4247 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4248 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4249 }
4250 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4251 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4252 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4253 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4254 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4255 }
4256 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4257 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4258 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4259 }
4260 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4261 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4262 $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);
4263 }
4264 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4265 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4266 $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);
4267 }
4268 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4269 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4270 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4271 }
4272 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4273 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4274 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4275 }
4276 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4277 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4278 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4279 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4280 $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);
4281 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4282 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4283 }
4284 } elseif (strtoupper($countrycode) == "ML") {//Mali
4285 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4286 $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);
4287 }
4288 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4289 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4290 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4291 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4292 $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);
4293 }
4294 } elseif (strtoupper($countrycode) == "MU") {
4295 //Maurice
4296 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4297 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4298 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4299 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4300 }
4301 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4302 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4303 $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);
4304 }
4305 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4306 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4307 $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);
4308 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4309 $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);
4310 }
4311 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4312 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4313 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4314 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4315 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4316 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4317 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4318 }
4319 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4320 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4321 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4322 }
4323 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4324 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4325 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4326 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4327 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4328 }
4329 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4330 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4331 $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);
4332 }
4333 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4334 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4335 $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);
4336 }
4337 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4338 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4339 $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);
4340 }
4341 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4342 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4343 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4344 }
4345 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4346 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4347 $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);
4348 }
4349 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4350 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4351 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4352 }
4353 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4354 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4355 $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);
4356 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4357 $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);
4358 }
4359 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4360 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4361 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4362 }
4363 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4364 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4365 $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);
4366 }
4367 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4368 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4369 $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);
4370 }
4371 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4372 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4373 $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);
4374 }
4375 } elseif (strtoupper($countrycode) == "IT") {//Italie
4376 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4377 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4378 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4379 $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);
4380 }
4381 } elseif (strtoupper($countrycode) == "AU") {
4382 //Australie
4383 if (dol_strlen($phone) == 12) {
4384 //ex: +61_A_BCDE_FGHI
4385 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4386 }
4387 } elseif (strtoupper($countrycode) == "LU") {
4388 // Luxembourg
4389 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4390 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4391 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4392 $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);
4393 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4394 $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);
4395 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4396 $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);
4397 }
4398 } elseif (strtoupper($countrycode) == "PE") {
4399 // Peru
4400 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4401 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4402 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4403 $newphonewa = '+51'.$newphone;
4404 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4405 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4406 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4407 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4408 $newphonewa = $newphone;
4409 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3).$separ.substr($newphone, 14, 3);
4410 }
4411 }
4412
4413 $newphoneastart = $newphoneaend = '';
4414 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4415 if ($addlink == 'tel' || $conf->browser->layout == 'phone' || (isModEnabled('clicktodial') && getDolGlobalString('CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS'))) { // If phone or option for, we use link of phone
4416 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4417 $newphoneaend .= '</a>';
4418 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4419 if (empty($user->clicktodial_loaded)) {
4420 $user->fetch_clicktodial();
4421 }
4422
4423 // Define urlmask
4424 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4425 if (!empty($user->clicktodial_url)) {
4426 $urlmask = $user->clicktodial_url;
4427 }
4428
4429 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4430 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4431 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4432 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4433 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4434 // Those lines are for substitution
4435 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4436 '__PHONETO__' => urlencode($phone),
4437 '__LOGIN__' => $clicktodial_login,
4438 '__PASS__' => $clicktodial_password);
4439 $url = make_substitutions($url, $substitarray);
4440 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4441 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4442 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4443 $newphoneaend = '</a>';
4444 } else {
4445 // Old method
4446 $newphoneastart = '<a href="'.$url.'"';
4447 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4448 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4449 }
4450 $newphoneastart .= '>';
4451 $newphoneaend .= '</a>';
4452 }
4453 }
4454
4455 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4456 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4457 $type = 'AC_TEL';
4458 $addlinktoagenda = '';
4459 if ($addlink == 'AC_FAX') {
4460 $type = 'AC_FAX';
4461 }
4462 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4463 $addlinktoagenda = '<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>';
4464 }
4465 if ($addlinktoagenda) {
4466 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4467 }
4468 }
4469 }
4470
4471 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4472 // Link to Whatsapp
4473 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4474 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4475 }
4476
4477 if (empty($titlealt)) {
4478 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4479 }
4480 $rep = '';
4481
4482 if ($hookmanager) {
4483 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4484 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4485 $rep .= $hookmanager->resPrint;
4486 }
4487 if (empty($reshook)) {
4488 $picto = '';
4489 if ($withpicto) {
4490 if ($withpicto == 'fax') {
4491 $picto = 'phoning_fax';
4492 } elseif ($withpicto == 'phone') {
4493 $picto = 'phoning';
4494 } elseif ($withpicto == 'mobile') {
4495 $picto = 'phoning_mobile';
4496 } else {
4497 $picto = '';
4498 }
4499 }
4500 if ($adddivfloat == 1) {
4501 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4502 } elseif (empty($adddivfloat)) {
4503 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4504 }
4505
4506 $rep .= $newphoneastart;
4507 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4508 if ($separ != 'hidenum') {
4509 $rep .= ($withpicto ? ' ' : '').$newphone;
4510 }
4511 $rep .= $newphoneaend;
4512
4513 if ($adddivfloat == 1) {
4514 $rep .= '</div>';
4515 } elseif (empty($adddivfloat)) {
4516 $rep .= '</span>';
4517 }
4518 }
4519
4520 return $rep;
4521}
4522
4530function dol_print_ip($ip, $mode = 0)
4531{
4532 global $langs;
4533
4534 $ret = '';
4535
4536 if (empty($mode)) {
4537 $ret .= $ip;
4538 }
4539
4540 if ($mode != 2) {
4541 $countrycode = dolGetCountryCodeFromIp($ip);
4542 if ($countrycode) { // If success, countrycode is us, fr, ...
4543 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4544 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4545 } else {
4546 $ret .= ' ('.$countrycode.')';
4547 }
4548 } else {
4549 // Nothing
4550 }
4551 }
4552
4553 return $ret;
4554}
4555
4564function getUserRemoteIP()
4565{
4566 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4567 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4568 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4569 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4570 } else {
4571 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4572 }
4573 } else {
4574 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4575 }
4576 } else {
4577 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4578 }
4579 return $ip;
4580}
4581
4590function isHTTPS()
4591{
4592 $isSecure = false;
4593 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4594 $isSecure = true;
4595 } 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') {
4596 $isSecure = true;
4597 }
4598 return $isSecure;
4599}
4600
4607function dolGetCountryCodeFromIp($ip)
4608{
4609 global $conf;
4610
4611 $countrycode = '';
4612
4613 if (isModEnabled('geoipmaxmind')) {
4614 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4615 //$ip='24.24.24.24';
4616 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4617 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4618 $geoip = new DolGeoIP('country', $datafile);
4619 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4620 $countrycode = $geoip->getCountryCodeFromIP($ip);
4621 }
4622
4623 return $countrycode;
4624}
4625
4626
4633function dol_user_country()
4634{
4635 global $conf, $langs, $user;
4636
4637 //$ret=$user->xxx;
4638 $ret = '';
4639 if (isModEnabled('geoipmaxmind')) {
4640 $ip = getUserRemoteIP();
4641 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4642 //$ip='24.24.24.24';
4643 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4644 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4645 $geoip = new DolGeoIP('country', $datafile);
4646 $countrycode = $geoip->getCountryCodeFromIP($ip);
4647 $ret = $countrycode;
4648 }
4649 return $ret;
4650}
4651
4664function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4665{
4666 global $conf, $user, $langs, $hookmanager;
4667
4668 $out = '';
4669
4670 if ($address) {
4671 if ($hookmanager) {
4672 $parameters = array('element' => $element, 'id' => $id);
4673 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4674 $out .= $hookmanager->resPrint;
4675 }
4676 if (empty($reshook)) {
4677 if (empty($charfornl)) {
4678 $out .= nl2br($address);
4679 } else {
4680 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4681 }
4682
4683 // TODO Remove this block, we can add this using the hook now
4684 $showgmap = $showomap = 0;
4685 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4686 $showgmap = 1;
4687 }
4688 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4689 $showgmap = 1;
4690 }
4691 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4692 $showgmap = 1;
4693 }
4694 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4695 $showgmap = 1;
4696 }
4697 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4698 $showomap = 1;
4699 }
4700 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4701 $showomap = 1;
4702 }
4703 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4704 $showomap = 1;
4705 }
4706 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4707 $showomap = 1;
4708 }
4709 if ($showgmap) {
4710 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4711 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4712 }
4713 if ($showomap) {
4714 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4715 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4716 }
4717 }
4718 }
4719 if ($noprint) {
4720 return $out;
4721 } else {
4722 print $out;
4723 return null;
4724 }
4725}
4726
4727
4737function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4738{
4739 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4740 return true;
4741 }
4742 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4743 return true;
4744 }
4745 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4746 return true;
4747 }
4748
4749 return false;
4750}
4751
4761function isValidMXRecord($domain)
4762{
4763 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4764 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4765 return 0;
4766 }
4767 if (function_exists('getmxrr')) {
4768 $mxhosts = array();
4769 $weight = array();
4770 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4771 if (count($mxhosts) > 1) {
4772 return 1;
4773 }
4774 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4775 return 1;
4776 }
4777
4778 return 0;
4779 }
4780 }
4781
4782 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4783 return -1;
4784}
4785
4793function isValidPhone($phone)
4794{
4795 return true;
4796}
4797
4798
4808function dolGetFirstLetters($s, $nbofchar = 1)
4809{
4810 $ret = '';
4811 $tmparray = explode(' ', $s);
4812 foreach ($tmparray as $tmps) {
4813 $ret .= dol_substr($tmps, 0, $nbofchar);
4814 }
4815
4816 return $ret;
4817}
4818
4819
4827function dol_strlen($string, $stringencoding = 'UTF-8')
4828{
4829 if (is_null($string)) {
4830 return 0;
4831 }
4832
4833 if (function_exists('mb_strlen')) {
4834 return mb_strlen($string, $stringencoding);
4835 } else {
4836 return strlen($string);
4837 }
4838}
4839
4850function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4851{
4852 global $langs;
4853
4854 if (empty($stringencoding)) {
4855 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4856 }
4857
4858 $ret = '';
4859 if (empty($trunconbytes)) {
4860 if (function_exists('mb_substr')) {
4861 $ret = mb_substr($string, $start, $length, $stringencoding);
4862 } else {
4863 $ret = substr($string, $start, $length);
4864 }
4865 } else {
4866 if (function_exists('mb_strcut')) {
4867 $ret = mb_strcut($string, $start, $length, $stringencoding);
4868 } else {
4869 $ret = substr($string, $start, $length);
4870 }
4871 }
4872 return $ret;
4873}
4874
4875
4889function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4890{
4891 global $conf;
4892
4893 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4894 return $string;
4895 }
4896
4897 if (empty($stringencoding)) {
4898 $stringencoding = 'UTF-8';
4899 }
4900 // reduce for small screen
4901 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4902 $size = round($size / 3);
4903 }
4904
4905 // We go always here
4906 if ($trunc == 'right') {
4907 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4908 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4909 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4910 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4911 } else {
4912 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4913 return $string;
4914 }
4915 } elseif ($trunc == 'middle') {
4916 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4917 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4918 $size1 = (int) round($size / 2);
4919 $size2 = (int) round($size / 2);
4920 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4921 } else {
4922 return $string;
4923 }
4924 } elseif ($trunc == 'left') {
4925 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4926 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4927 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4928 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4929 } else {
4930 return $string;
4931 }
4932 } elseif ($trunc == 'wrap') {
4933 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4934 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4935 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4936 } else {
4937 return $string;
4938 }
4939 } else {
4940 return 'BadParam3CallingDolTrunc';
4941 }
4942}
4943
4951function getPictoForType($key, $morecss = '')
4952{
4953 // Set array with type -> picto
4954 $type2picto = array(
4955 'varchar' => 'font',
4956 'text' => 'font',
4957 'html' => 'code',
4958 'int' => 'sort-numeric-down',
4959 'double' => 'sort-numeric-down',
4960 'price' => 'currency',
4961 'pricecy' => 'multicurrency',
4962 'password' => 'key',
4963 'boolean' => 'check-square',
4964 'date' => 'calendar',
4965 'datetime' => 'calendar',
4966 'duration' => 'hourglass',
4967 'phone' => 'phone',
4968 'mail' => 'email',
4969 'url' => 'url',
4970 'ip' => 'country',
4971 'select' => 'list',
4972 'sellist' => 'list',
4973 'stars' => 'fontawesome_star_fas',
4974 'radio' => 'check-circle',
4975 'checkbox' => 'list',
4976 'chkbxlst' => 'list',
4977 'link' => 'link',
4978 'icon' => "question",
4979 'point' => "country",
4980 'multipts' => 'country',
4981 'linestrg' => "country",
4982 'polygon' => "country",
4983 'separate' => 'minus'
4984 );
4985
4986 if (!empty($type2picto[$key])) {
4987 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4988 }
4989
4990 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4991}
4992
4993
5015function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
5016{
5017 global $conf;
5018
5019 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5020 $url = DOL_URL_ROOT;
5021 $theme = isset($conf->theme) ? $conf->theme : null;
5022 $path = 'theme/'.$theme;
5023 if (empty($picto)) {
5024 $picto = 'generic';
5025 }
5026
5027 // Define fullpathpicto to use into src
5028 if ($pictoisfullpath) {
5029 // Clean parameters
5030 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5031 $picto .= '.png';
5032 }
5033 $fullpathpicto = $picto;
5034 $reg = array();
5035 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5036 $morecss .= ($morecss ? ' ' : '').$reg[1];
5037 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5038 }
5039 } else {
5040 // $picto can not be null since replaced with 'generic' in that case
5041 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5042 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5043 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5044 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5045
5046 // Fix some values of $pictowithouttext
5047 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5048 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5049 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5050 }
5051
5052 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5053 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5054 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5055 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5056
5057 // Compatibility with old fontawesome versions
5058 if ($pictowithouttext == 'file-o') {
5059 $pictowithouttext = 'file';
5060 }
5061
5062 $pictowithouttextarray = explode('_', $pictowithouttext);
5063 $marginleftonlyshort = 0;
5064
5065 if (!empty($pictowithouttextarray[1])) {
5066 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5067 $fakey = 'fa-'.$pictowithouttextarray[0];
5068 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5069 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5070 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5071 } else {
5072 $fakey = 'fa-'.$pictowithouttext;
5073 $faprefix = 'fas';
5074 $facolor = '';
5075 $fasize = '';
5076 }
5077
5078 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5079 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5080 $morestyle = '';
5081 $reg = array();
5082 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5083 $morecss .= ($morecss ? ' ' : '').$reg[1];
5084 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5085 }
5086 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5087 $morestyle = $reg[1];
5088 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5089 }
5090 $moreatt = trim($moreatt);
5091
5092 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5093 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5094 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5095 $enabledisablehtml .= $titlealt;
5096 }*/
5097 $enabledisablehtml .= '</span>';
5098
5099 return $enabledisablehtml;
5100 }
5101
5102 if (empty($srconly) && in_array($pictowithouttext, array(
5103 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5104 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5105 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5106 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5107 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5108 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5109 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
5110 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5111 'commercial', 'companies',
5112 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5113 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5114 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5115 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5116 'hands-helping', 'help', 'holiday',
5117 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5118 'key', 'knowledgemanagement',
5119 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5120 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5121 'off', 'on', 'order',
5122 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5123 'stock', 'resize', 'service', 'stats',
5124 'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_grey', 'switch_on_warning', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
5125 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
5126 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5127 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5128 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5129 'technic', 'ticket',
5130 'error', 'warning',
5131 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
5132 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5133 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5134 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5135 'conferenceorbooth', 'eventorganization',
5136 'stamp', 'signature',
5137 'webportal'
5138 ))) {
5139 $fakey = $pictowithouttext;
5140 $facolor = '';
5141 $fasize = '';
5142 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5143 if (in_array($pictowithouttext, array('card', 'bell', 'clock', 'establishment', 'file', 'file-o', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
5144 $fa = 'far';
5145 }
5146 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
5147 $fa = 'fab';
5148 }
5149
5150 $arrayconvpictotofa = array(
5151 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5152 'asset' => 'money-check-alt', 'autofill' => 'fill',
5153 'back' => 'arrow-left', 'bank_account' => 'university',
5154 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5155 'bookcal' => 'calendar-check',
5156 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5157 'bom' => 'shapes',
5158 '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',
5159 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5160 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5161 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5162 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5163 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5164 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5165 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5166 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5167 'generic' => 'file', 'holiday' => 'umbrella-beach',
5168 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5169 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5170 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5171 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5172 'sign-out' => 'sign-out-alt',
5173 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_grey' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
5174 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5175 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5176 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5177 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5178 'other' => 'square',
5179 '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',
5180 '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',
5181 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5182 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5183 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5184 'service' => 'concierge-bell',
5185 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5186 'status' => 'stop-circle',
5187 'stripe' => 'stripe-s', 'supplier' => 'building',
5188 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5189 'title_agenda' => 'calendar-alt',
5190 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5191 'jabber' => 'comment-o',
5192 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5193 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5194 'webportal' => 'door-open'
5195 );
5196 if ($conf->currency == 'EUR') {
5197 $arrayconvpictotofa['currency'] = 'euro-sign';
5198 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5199 } else {
5200 $arrayconvpictotofa['currency'] = 'dollar-sign';
5201 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5202 }
5203 if ($pictowithouttext == 'off') {
5204 $fakey = 'fa-square';
5205 $fasize = '1.3em';
5206 } elseif ($pictowithouttext == 'on') {
5207 $fakey = 'fa-check-square';
5208 $fasize = '1.3em';
5209 } elseif ($pictowithouttext == 'listlight') {
5210 $fakey = 'fa-download';
5211 $marginleftonlyshort = 1;
5212 } elseif ($pictowithouttext == 'printer') {
5213 $fakey = 'fa-print';
5214 $fasize = '1.2em';
5215 } elseif ($pictowithouttext == 'note') {
5216 $fakey = 'fa-sticky-note';
5217 $marginleftonlyshort = 1;
5218 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5219 $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');
5220 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5221 if (preg_match('/selected/', $pictowithouttext)) {
5222 $facolor = '#888';
5223 }
5224 $marginleftonlyshort = 1;
5225 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5226 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5227 } else {
5228 $fakey = 'fa-'.$pictowithouttext;
5229 }
5230
5231 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5232 $morecss .= ' em092';
5233 }
5234 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5235 $morecss .= ' em088';
5236 }
5237 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5238 $morecss .= ' em080';
5239 }
5240
5241 // Define $marginleftonlyshort
5242 $arrayconvpictotomarginleftonly = array(
5243 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5244 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5245 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5246 );
5247 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5248 $marginleftonlyshort = 0;
5249 }
5250
5251 // Add CSS
5252 $arrayconvpictotomorcess = array(
5253 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5254 'bank_account' => 'infobox-bank_account',
5255 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5256 'bookcal' => 'infobox-action',
5257 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5258 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5259 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5260 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5261 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5262 'incoterm' => 'infobox-supplier_proposal',
5263 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5264 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5265 'order' => 'infobox-commande',
5266 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5267 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5268 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5269 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5270 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5271 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5272 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5273 'resource' => 'infobox-action',
5274 'salary' => 'infobox-bank_account', 'shapes' => 'infobox-adherent', 'shipment' => 'infobox-commande', 'stripe' => 'infobox-bank_account', 'supplier_invoice' => 'infobox-order_supplier', 'supplier_invoicea' => 'infobox-order_supplier', 'supplier_invoiced' => 'infobox-order_supplier',
5275 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5276 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5277 'vat' => 'infobox-bank_account',
5278 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5279 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5280 );
5281 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5282 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5283 }
5284
5285 // Define $color
5286 $arrayconvpictotocolor = array(
5287 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5288 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5289 'dynamicprice' => '#a69944',
5290 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5291 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5292 'lock' => '#ddd', 'lot' => '#a69944',
5293 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5294 'other' => '#ddd', 'world' => '#986c6a',
5295 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5296 //'shipment'=>'#a69944',
5297 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5298 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5299 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5300 'website' => '#304', 'workstation' => '#a69944'
5301 );
5302 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5303 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5304 }
5305
5306 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5307 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5308 $morestyle = '';
5309 $reg = array();
5310 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5311 $morecss .= ($morecss ? ' ' : '').$reg[1];
5312 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5313 }
5314 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5315 $morestyle = $reg[1];
5316 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5317 }
5318 $moreatt = trim($moreatt);
5319
5320 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5321 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5322 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5323 $enabledisablehtml .= $titlealt;
5324 }*/
5325 $enabledisablehtml .= '</span>';
5326
5327 return $enabledisablehtml;
5328 }
5329
5330 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5331 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5332 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5333 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_RES') . '/theme/' . getDolGlobalString('MAIN_OVERWRITE_THEME_RES'); // To allow an external module to overwrite image resources whatever is activated theme
5334 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5335 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5336 }
5337
5338 // If we ask an image into $url/$mymodule/img (instead of default path)
5339 $regs = array();
5340 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5341 $picto = $regs[1];
5342 $path = $regs[2]; // $path is $mymodule
5343 }
5344
5345 // Clean parameters
5346 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5347 $picto .= '.png';
5348 }
5349 // If alt path are defined, define url where img file is, according to physical path
5350 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5351 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5352 if ($type == 'main') {
5353 continue;
5354 }
5355 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5356 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5357 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5358 break;
5359 }
5360 }
5361
5362 // $url is '' or '/custom', $path is current theme or
5363 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5364 }
5365
5366 if ($srconly) {
5367 return $fullpathpicto;
5368 }
5369
5370 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5371 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
5372}
5373
5387function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5388{
5389 if (strpos($picto, '^') === 0) {
5390 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5391 } else {
5392 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5393 }
5394}
5395
5407function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5408{
5409 global $conf;
5410
5411 if (is_numeric($picto)) {
5412 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5413 //$picto = $leveltopicto[$picto];
5414 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5415 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5416 $picto .= '.png';
5417 }
5418
5419 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5420
5421 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5422}
5423
5435function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5436{
5437 global $conf;
5438
5439 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5440 $picto .= '.png';
5441 }
5442
5443 if ($pictoisfullpath) {
5444 $path = $picto;
5445 } else {
5446 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5447
5448 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5449 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5450
5451 if (file_exists($themepath)) {
5452 $path = $themepath;
5453 }
5454 }
5455 }
5456
5457 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5458}
5459
5473function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5474{
5475 global $langs;
5476
5477 if (empty($titlealt) || $titlealt == 'default') {
5478 if ($numaction == '-1' || $numaction == 'ST_NO') {
5479 $numaction = -1;
5480 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5481 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5482 $numaction = 0;
5483 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5484 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5485 $numaction = 1;
5486 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5487 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5488 $numaction = 2;
5489 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5490 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5491 $numaction = 3;
5492 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5493 } else {
5494 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5495 $numaction = 0;
5496 }
5497 }
5498 if (!is_numeric($numaction)) {
5499 $numaction = 0;
5500 }
5501
5502 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5503}
5504
5512function img_pdf($titlealt = 'default', $size = 3)
5513{
5514 global $langs;
5515
5516 if ($titlealt == 'default') {
5517 $titlealt = $langs->trans('Show');
5518 }
5519
5520 return img_picto($titlealt, 'pdf'.$size.'.png');
5521}
5522
5530function img_edit_add($titlealt = 'default', $other = '')
5531{
5532 global $langs;
5533
5534 if ($titlealt == 'default') {
5535 $titlealt = $langs->trans('Add');
5536 }
5537
5538 return img_picto($titlealt, 'edit_add.png', $other);
5539}
5547function img_edit_remove($titlealt = 'default', $other = '')
5548{
5549 global $langs;
5550
5551 if ($titlealt == 'default') {
5552 $titlealt = $langs->trans('Remove');
5553 }
5554
5555 return img_picto($titlealt, 'edit_remove.png', $other);
5556}
5557
5566function img_edit($titlealt = 'default', $float = 0, $other = '')
5567{
5568 global $langs;
5569
5570 if ($titlealt == 'default') {
5571 $titlealt = $langs->trans('Modify');
5572 }
5573
5574 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5575}
5576
5585function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5586{
5587 global $langs;
5588
5589 if ($titlealt == 'default') {
5590 $titlealt = $langs->trans('View');
5591 }
5592
5593 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5594
5595 return img_picto($titlealt, 'eye', $moreatt);
5596}
5597
5606function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5607{
5608 global $langs;
5609
5610 if ($titlealt == 'default') {
5611 $titlealt = $langs->trans('Delete');
5612 }
5613
5614 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5615}
5616
5624function img_printer($titlealt = "default", $other = '')
5625{
5626 global $langs;
5627 if ($titlealt == "default") {
5628 $titlealt = $langs->trans("Print");
5629 }
5630 return img_picto($titlealt, 'printer.png', $other);
5631}
5632
5640function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5641{
5642 global $langs;
5643
5644 if ($titlealt == 'default') {
5645 $titlealt = $langs->trans('Split');
5646 }
5647
5648 return img_picto($titlealt, 'split.png', $other);
5649}
5650
5658function img_help($usehelpcursor = 1, $usealttitle = 1)
5659{
5660 global $langs;
5661
5662 if ($usealttitle) {
5663 if (is_string($usealttitle)) {
5664 $usealttitle = dol_escape_htmltag($usealttitle);
5665 } else {
5666 $usealttitle = $langs->trans('Info');
5667 }
5668 }
5669
5670 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5671}
5672
5679function img_info($titlealt = 'default')
5680{
5681 global $langs;
5682
5683 if ($titlealt == 'default') {
5684 $titlealt = $langs->trans('Informations');
5685 }
5686
5687 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5688}
5689
5698function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5699{
5700 global $langs;
5701
5702 if ($titlealt == 'default') {
5703 $titlealt = $langs->trans('Warning');
5704 }
5705
5706 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5707 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5708}
5709
5716function img_error($titlealt = 'default')
5717{
5718 global $langs;
5719
5720 if ($titlealt == 'default') {
5721 $titlealt = $langs->trans('Error');
5722 }
5723
5724 return img_picto($titlealt, 'error.png');
5725}
5726
5734function img_next($titlealt = 'default', $moreatt = '')
5735{
5736 global $langs;
5737
5738 if ($titlealt == 'default') {
5739 $titlealt = $langs->trans('Next');
5740 }
5741
5742 //return img_picto($titlealt, 'next.png', $moreatt);
5743 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5744}
5745
5753function img_previous($titlealt = 'default', $moreatt = '')
5754{
5755 global $langs;
5756
5757 if ($titlealt == 'default') {
5758 $titlealt = $langs->trans('Previous');
5759 }
5760
5761 //return img_picto($titlealt, 'previous.png', $moreatt);
5762 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5763}
5764
5773function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5774{
5775 global $langs;
5776
5777 if ($titlealt == 'default') {
5778 $titlealt = $langs->trans('Down');
5779 }
5780
5781 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5782}
5783
5792function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5793{
5794 global $langs;
5795
5796 if ($titlealt == 'default') {
5797 $titlealt = $langs->trans('Up');
5798 }
5799
5800 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5801}
5802
5811function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5812{
5813 global $langs;
5814
5815 if ($titlealt == 'default') {
5816 $titlealt = $langs->trans('Left');
5817 }
5818
5819 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5820}
5821
5830function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5831{
5832 global $langs;
5833
5834 if ($titlealt == 'default') {
5835 $titlealt = $langs->trans('Right');
5836 }
5837
5838 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5839}
5840
5848function img_allow($allow, $titlealt = 'default')
5849{
5850 global $langs;
5851
5852 if ($titlealt == 'default') {
5853 $titlealt = $langs->trans('Active');
5854 }
5855
5856 if ($allow == 1) {
5857 return img_picto($titlealt, 'tick.png');
5858 }
5859
5860 return '-';
5861}
5862
5870function img_credit_card($brand, $morecss = null)
5871{
5872 if (is_null($morecss)) {
5873 $morecss = 'fa-2x';
5874 }
5875
5876 if ($brand == 'visa' || $brand == 'Visa') {
5877 $brand = 'cc-visa';
5878 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5879 $brand = 'cc-mastercard';
5880 } elseif ($brand == 'amex' || $brand == 'American Express') {
5881 $brand = 'cc-amex';
5882 } elseif ($brand == 'discover' || $brand == 'Discover') {
5883 $brand = 'cc-discover';
5884 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5885 $brand = 'cc-jcb';
5886 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5887 $brand = 'cc-diners-club';
5888 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5889 $brand = 'credit-card';
5890 }
5891
5892 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5893}
5894
5903function img_mime($file, $titlealt = '', $morecss = '')
5904{
5905 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5906
5907 $mimetype = dol_mimetype($file, '', 1);
5908 $mimeimg = dol_mimetype($file, '', 2);
5909 $mimefa = dol_mimetype($file, '', 4);
5910
5911 if (empty($titlealt)) {
5912 $titlealt = 'Mime type: '.$mimetype;
5913 }
5914
5915 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5916 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5917}
5918
5919
5927function img_search($titlealt = 'default', $other = '')
5928{
5929 global $langs;
5930
5931 if ($titlealt == 'default') {
5932 $titlealt = $langs->trans('Search');
5933 }
5934
5935 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5936
5937 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5938 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5939
5940 return $input;
5941}
5942
5950function img_searchclear($titlealt = 'default', $other = '')
5951{
5952 global $langs;
5953
5954 if ($titlealt == 'default') {
5955 $titlealt = $langs->trans('Search');
5956 }
5957
5958 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5959
5960 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5961 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5962
5963 return $input;
5964}
5965
5978function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5979{
5980 global $conf, $langs;
5981
5982 if ($infoonimgalt) {
5983 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5984 } else {
5985 if (empty($conf->use_javascript_ajax)) {
5986 $textfordropdown = '';
5987 }
5988
5989 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5990 $fa = 'info-circle';
5991 if ($picto == 'warning') {
5992 $fa = 'exclamation-triangle';
5993 }
5994 $result = ($nodiv ? '' : '<div class="wordbreak '.$class.($morecss ? ' '.$morecss : '').($textfordropdown ? ' hidden' : '').'">').'<span class="fa fa-'.$fa.'" title="'.dol_escape_htmltag($admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).'"></span> ';
5995 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5996 $result .= ($nodiv ? '' : '</div>');
5997
5998 if ($textfordropdown) {
5999 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
6000 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
6001 jQuery(document).ready(function() {
6002 jQuery(".'.$class.'text").click(function() {
6003 console.log("toggle text");
6004 jQuery(".'.$class.'").toggle();
6005 });
6006 });
6007 </script>';
6008
6009 $result = $tmpresult.$result;
6010 }
6011 }
6012
6013 return $result;
6014}
6015
6016
6028function dol_print_error($db = null, $error = '', $errors = null)
6029{
6030 global $conf, $langs, $user, $argv;
6031 global $dolibarr_main_prod;
6032
6033 $out = '';
6034 $syslog = '';
6035
6036 // If error occurs before the $lang object was loaded
6037 if (!$langs) {
6038 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6039 $langs = new Translate('', $conf);
6040 $langs->load("main");
6041 }
6042
6043 // Load translation files required by the error messages
6044 $langs->loadLangs(array('main', 'errors'));
6045
6046 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6047 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6048 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6049 $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";
6050 }
6051 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6052
6053 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6054 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6055 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6056 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6057 }
6058 if ($user instanceof User) {
6059 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6060 }
6061 if (function_exists("phpversion")) {
6062 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6063 }
6064 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6065 if (function_exists("php_uname")) {
6066 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6067 }
6068 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6069 $out .= "<br>\n";
6070 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
6071 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
6072 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
6073 $out .= "<br>\n";
6074 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
6075 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
6076 } else { // Mode CLI
6077 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6078 $syslog .= "pid=".dol_getmypid();
6079 }
6080
6081 if (!empty($conf->modules)) {
6082 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6083 }
6084
6085 if (is_object($db)) {
6086 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6087 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6088 $lastqueryerror = $db->lastqueryerror();
6089 if (!utf8_check($lastqueryerror)) {
6090 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6091 }
6092 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6093 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6094 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6095 $out .= "<br>\n";
6096 } else { // Mode CLI
6097 // No dol_escape_htmltag for output, we are in CLI mode
6098 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6099 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6100 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6101 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6102 }
6103 $syslog .= ", sql=".$db->lastquery();
6104 $syslog .= ", db_error=".$db->lasterror();
6105 }
6106
6107 if ($error || $errors) {
6108 // Merge all into $errors array
6109 if (is_array($error) && is_array($errors)) {
6110 $errors = array_merge($error, $errors);
6111 } elseif (is_array($error)) { // deprecated, use second parameters
6112 $errors = $error;
6113 } elseif (is_array($errors) && !empty($error)) {
6114 $errors = array_merge(array($error), $errors);
6115 } elseif (!empty($error)) {
6116 $errors = array_merge(array($error), array($errors));
6117 }
6118
6119 $langs->load("errors");
6120
6121 foreach ($errors as $msg) {
6122 if (empty($msg)) {
6123 continue;
6124 }
6125 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6126 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6127 } else { // Mode CLI
6128 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6129 }
6130 $syslog .= ", msg=".$msg;
6131 }
6132 }
6133 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6134 xdebug_print_function_stack();
6135 $out .= '<b>XDebug information:</b>'."<br>\n";
6136 $out .= 'File: '.xdebug_call_file()."<br>\n";
6137 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6138 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6139 $out .= "<br>\n";
6140 }
6141
6142 // Return a http header with error code if possible
6143 if (!headers_sent()) {
6144 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6145 top_httphead();
6146 }
6147 //http_response_code(500); // If we use 500, message is not output with some command line tools
6148 http_response_code(202); // If we use 202, this is not really an error message, but this allow to output message on command line tools
6149 }
6150
6151 if (empty($dolibarr_main_prod)) {
6152 print $out;
6153 } else {
6154 if (empty($langs->defaultlang)) {
6155 $langs->setDefaultLang();
6156 }
6157 $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.
6158 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6159 print 'This website or feature is currently temporarily 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";
6160 print $langs->trans("DolibarrHasDetectedError").'. ';
6161 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6162 if (!defined("MAIN_CORE_ERROR")) {
6163 define("MAIN_CORE_ERROR", 1);
6164 }
6165 }
6166
6167 dol_syslog("Error ".$syslog, LOG_ERR);
6168}
6169
6180function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6181{
6182 global $langs;
6183
6184 if (empty($email)) {
6185 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6186 }
6187
6188 $langs->load("errors");
6189 $now = dol_now();
6190
6191 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6192 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6193 if ($errormessage) {
6194 print '<br><br>'.$errormessage;
6195 }
6196 if (is_array($errormessages) && count($errormessages)) {
6197 foreach ($errormessages as $mesgtoshow) {
6198 print '<br><br>'.$mesgtoshow;
6199 }
6200 }
6201 print '</div></div>';
6202}
6203
6220function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6221{
6222 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6223}
6224
6243function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6244{
6245 global $langs, $form;
6246 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6247
6248 if ($moreattrib == 'class="right"') {
6249 $prefix .= 'right '; // For backward compatibility
6250 }
6251
6252 $sortorder = strtoupper($sortorder);
6253 $out = '';
6254 $sortimg = '';
6255
6256 $tag = 'th';
6257 if ($thead == 2) {
6258 $tag = 'div';
6259 }
6260
6261 $tmpsortfield = explode(',', $sortfield);
6262 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6263 $tmpfield = explode(',', $field);
6264 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6265
6266 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6267 $prefix = 'wrapcolumntitle '.$prefix;
6268 }
6269
6270 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6271 // If field is used as sort criteria we use a specific css class liste_titre_sel
6272 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6273 $liste_titre = 'liste_titre';
6274 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6275 $liste_titre = 'liste_titre_sel';
6276 }
6277
6278 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6279 //$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)).'"' : '');
6280 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6281 $tagstart .= '>';
6282
6283 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6284 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6285 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6286 $options = preg_replace('/&+/i', '&', $options);
6287 if (!preg_match('/^&/', $options)) {
6288 $options = '&'.$options;
6289 }
6290
6291 $sortordertouseinlink = '';
6292 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6293 if (preg_match('/^DESC/i', $sortorder)) {
6294 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6295 } else { // We reverse the var $sortordertouseinlink
6296 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6297 }
6298 } else { // We are on field that is the first current sorting criteria
6299 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6300 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6301 } else {
6302 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6303 }
6304 }
6305 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6306 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6307 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6308 $out .= '>';
6309 }
6310 if ($tooltip) {
6311 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6312 if (preg_match('/:\w+$/', $tooltip)) {
6313 $tmptooltip = explode(':', $tooltip);
6314 } else {
6315 $tmptooltip = array($tooltip);
6316 }
6317 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6318 } else {
6319 $out .= $langs->trans($name);
6320 }
6321
6322 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6323 $out .= '</a>';
6324 }
6325
6326 if (empty($thead) && $field) { // If this is a sort field
6327 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6328 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6329 $options = preg_replace('/&+/i', '&', $options);
6330 if (!preg_match('/^&/', $options)) {
6331 $options = '&'.$options;
6332 }
6333
6334 if (!$sortorder || ($field1 != $sortfield1)) {
6335 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6336 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6337 } else {
6338 if (preg_match('/^DESC/', $sortorder)) {
6339 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6340 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6341 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6342