dolibarr 23.0.3
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-2025 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-2025 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-2026 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
24 * Copyright (C) 2023-2024 Joachim Kueter <git-jk@bloxera.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 * Copyright (C) 2025 Vincent Maury <vmaury@timgroup.fr>
29 * Copyright (C) 2026 Benjamin Falière <benjamin@faliere.com>
30 *
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 3 of the License, or
34 * (at your option) any later version.
35 *
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program. If not, see <https://www.gnu.org/licenses/>.
43 * or see https://www.gnu.org/
44 */
45
52//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
53
54// Function for better PHP x compatibility
55if (!function_exists('utf8_encode')) {
63 function utf8_encode($elements)
64 {
65 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
66 }
67}
68
69if (!function_exists('utf8_decode')) {
77 function utf8_decode($elements)
78 {
79 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
80 }
81}
82if (!function_exists('str_starts_with')) {
91 function str_starts_with($haystack, $needle)
92 {
93 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
94 }
95}
96if (!function_exists('str_ends_with')) {
105 function str_ends_with($haystack, $needle)
106 {
107 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
108 }
109}
110if (!function_exists('str_contains')) {
119 function str_contains($haystack, $needle)
120 {
121 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
122 }
123}
124
125
137function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
138{
139 global $conf;
140
141 $subdirectory = '';
142 if (!is_object($object) && empty($module)) {
143 return null;
144 }
145 if (empty($module) && !empty($object->element)) {
146 $module = $object->element;
147 }
148
149 // Special case for backward compatibility
150 switch ($module) {
151 case 'fichinter':
152 $module = 'ficheinter';
153 break;
154 case 'invoice_supplier':
155 $module = 'supplier_invoice';
156 break;
157 case 'order_supplier':
158 $module = 'supplier_order';
159 break;
160 case 'recruitmentjobposition':
161 $module = 'recruitment';
162 $subdirectory = '/recruitmentjobposition';
163 break;
164 case 'recruitmentcandidature':
165 $module = 'recruitment';
166 $subdirectory = '/recruitmentcandidature';
167 break;
168 case 'knowledgerecord':
169 $module = 'knowledgemanagement';
170 $subdirectory = '/knowledgerecord';
171 break;
172 case 'service':
173 case 'produit':
174 $module = 'product';
175 break;
176 case 'action':
177 case 'actioncomm':
178 case 'event':
179 $module = 'agenda';
180 break;
181 default:
182 break;
183 }
184
185 // Get the relative path of directory
186 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
187 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
188 $s = '';
189 if ($mode != 'outputrel') {
190 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)] . $subdirectory;
191 }
192 if ($forobject && $object->id > 0) {
193 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
194 }
195 return $s;
196 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
197 $s = '';
198 if ($mode != 'outputrel') {
199 $s = $conf->$module->dir_output . $subdirectory;
200 }
201 if ($forobject && $object->id > 0) {
202 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
203 }
204 return $s;
205 } else {
206 return 'error-diroutput-not-defined-for-this-object=' . $module;
207 }
208 } elseif ($mode == 'temp') {
209 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
210 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
211 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
212 return $conf->$module->dir_temp;
213 } else {
214 return 'error-dirtemp-not-defined-for-this-object=' . $module;
215 }
216 } else {
217 return 'error-bad-value-for-mode';
218 }
219}
220
230function getMultidirTemp($object, $module = '', $forobject = 0)
231{
232 return getMultidirOutput($object, $module, $forobject, 'temp');
233}
234
244function getMultidirVersion($object, $module = '', $forobject = 0)
245{
246 return getMultidirOutput($object, $module, $forobject, 'version');
247}
248
249
258function getDolGlobalString($key, $default = '')
259{
260 global $conf;
261 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
262}
263
273function getDolGlobalInt($key, $default = 0)
274{
275 global $conf;
276 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
277}
278
288function getDolGlobalFloat($key, $default = 0)
289{
290 global $conf;
291 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
292}
293
302function getDolGlobalBool($key, $default = false)
303{
304 global $conf;
305 return (bool) ($conf->global->$key ?? $default);
306}
307
314{
315 global $conf;
316 return (string) $conf->currency;
317}
318
325{
326 global $conf;
327 return (string) $conf->dol_optimize_smallscreen;
328}
329
335function getDolEntity()
336{
337 global $conf;
338 return (int) $conf->entity;
339}
340
346function getDolDBType()
347{
348 global $conf;
349 return $conf->db->type;
350}
351
359{
360 return str_replace('_', '', basename(dirname($s)).basename($s, '.php'));
361}
362
372function getDolUserString($key, $default = '', $tmpuser = null)
373{
374 if (empty($tmpuser)) {
375 global $user;
376 $tmpuser = $user;
377 }
378
379 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
380}
381
390function getDolUserInt($key, $default = 0, $tmpuser = null)
391{
392 if (empty($tmpuser)) {
393 global $user;
394 $tmpuser = $user;
395 }
396
397 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
398}
399
400
410define(
411 'MODULE_MAPPING',
412 array(
413 // Map deprecated names to new names
414 'adherent' => 'member', // Has new directory
415 'member_type' => 'adherent_type', // No directory, but file called adherent_type
416 'banque' => 'bank', // Has new directory
417 'contrat' => 'contract', // Has new directory
418 'entrepot' => 'stock', // Has new directory
419 'projet' => 'project', // Has new directory
420 'categorie' => 'category', // Has old directory
421 'commande' => 'order', // Has old directory
422 'expedition' => 'shipping', // Has old directory
423 'facture' => 'invoice', // Has old directory
424 'fichinter' => 'intervention', // Has old directory
425 'ficheinter' => 'intervention', // Backup for 'fichinter'
426 'propale' => 'propal', // Has old directory
427 'socpeople' => 'contact', // Has old directory
428 'fournisseur' => 'supplier', // Has old directory
429
430 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
431 'product_price' => 'productprice', // NO directory
432 'product_fournisseur_price' => 'productsupplierprice', // NO directory
433 )
434);
435
442function isModEnabled($module)
443{
444 global $conf;
445
446 // Fix old names (map to new names)
447 $arrayconv = MODULE_MAPPING;
448 $arrayconvbis = array_flip(MODULE_MAPPING);
449
450 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
451 // Special cases: both use the same module.
452 $arrayconv['supplier_order'] = 'fournisseur';
453 $arrayconv['supplier_invoice'] = 'fournisseur';
454 }
455
456 $module_alt = $module;
457 if (!empty($arrayconv[$module])) {
458 $module_alt = $arrayconv[$module];
459 }
460 $module_bis = $module;
461 if (!empty($arrayconvbis[$module])) {
462 $module_bis = $arrayconvbis[$module];
463 }
464
465 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
466}
467
478function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
479{
480 global $conf;
481
482 // For compatibility with bad naming on module
483 $moduletomoduletouse = array(
484 'invoice' => 'facture',
485 );
486 $moduleParmsMapping = array(
487 'product' => 'produit',
488 );
489
490 if (!empty($moduletomoduletouse[$module])) {
491 $module = $moduletomoduletouse[$module];
492 }
493
494 $warningDelayPath = $parmlevel1;
495 if (!empty($moduleParmsMapping[$warningDelayPath])) {
496 $warningDelayPath = $moduleParmsMapping[$warningDelayPath];
497 }
498
499 if ($parmlevel2) {
500 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->$parmlevel2) && !empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
501 return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
502 }
503 } else {
504 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->warning_delay)) {
505 return (int) $conf->$module->$warningDelayPath->warning_delay;
506 }
507 }
508
509 return 0;
510}
511
518function isDolTms($timestamp)
519{
520 if ($timestamp === '') {
521 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page ' . $_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
522 return false;
523 }
524 if (is_null($timestamp) || !is_numeric($timestamp)) {
525 return false;
526 }
527
528 return true;
529}
530
542function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
543{
544 require_once DOL_DOCUMENT_ROOT . "/core/db/" . $type . '.class.php';
545
546 $class = 'DoliDB' . ucfirst($type);
547 $db = new $class($type, $host, $user, $pass, $name, $port);
548 return $db;
549}
550
568function getEntity($element, $shared = 1, $currentobject = null)
569{
570 global $conf, $mc, $hookmanager, $object, $action, $db;
571
572 if (!is_object($hookmanager)) {
573 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
574 $hookmanager = new HookManager($db);
575 }
576
577 // fix different element names (France to English)
578 switch ($element) {
579 case 'projet':
580 $element = 'project';
581 break;
582 case 'contrat':
583 $element = 'contract';
584 break; // "/contrat/class/contrat.class.php"
585 case 'order_supplier':
586 $element = 'supplier_order';
587 break; // "/fourn/class/fournisseur.commande.class.php"
588 case 'invoice_supplier':
589 $element = 'supplier_invoice';
590 break; // "/fourn/class/fournisseur.facture.class.php"
591 }
592
593 if (is_object($mc)) {
594 $out = $mc->getEntity($element, $shared, $currentobject);
595 } else {
596 $out = '';
597 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
598 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
599 $addzero[] = 'c_holiday_types';
600 }
601 if (in_array($element, $addzero)) {
602 $out .= '0,';
603 }
604 $out .= ((int) $conf->entity);
605 }
606
607 // Manipulate entities to query on the fly
608 $parameters = array(
609 'element' => $element,
610 'shared' => $shared,
611 'object' => $object,
612 'currentobject' => $currentobject,
613 'out' => $out
614 );
615 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
616 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
617
618 if (is_numeric($reshook)) {
619 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
620 $out .= ',' . $hookmanager->resPrint; // add
621 } elseif ($reshook == 1) {
622 $out = $hookmanager->resPrint; // replace
623 }
624 }
625
626 return $out;
627}
628
635function setEntity($currentobject)
636{
637 global $conf, $mc;
638
639 if (is_object($mc) && method_exists($mc, 'setEntity')) {
640 return $mc->setEntity($currentobject);
641 } else {
642 return ((is_object($currentobject) && $currentobject->id > 0 && ((int) $currentobject->entity) > 0) ? (int) $currentobject->entity : $conf->entity);
643 }
644}
645
652function isASecretKey($keyname)
653{
654 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
655}
656
657
664function num2Alpha($n)
665{
666 $r = '';
667 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
668 $r = chr($n % 26 + 0x41) . $r;
669 }
670 return $r;
671}
672
673
690function getBrowserInfo($user_agent)
691{
692 include_once DOL_DOCUMENT_ROOT . '/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
693
694 $name = 'unknown';
695 $version = '';
696 $os = 'unknown';
697 $phone = '';
698
699 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
700
701 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal Bad definition of Mobile_Detect function
702 $detectmobile = new Mobile_Detect(null, $user_agent);
703 $tablet = $detectmobile->isTablet();
704
705 if ($detectmobile->isMobile()) {
706 $phone = 'unknown';
707
708 // If phone/smartphone, we set phone os name.
709 if ($detectmobile->is('AndroidOS')) {
710 $os = $phone = 'android';
711 } elseif ($detectmobile->is('BlackBerryOS')) {
712 $os = $phone = 'blackberry';
713 } elseif ($detectmobile->is('iOS')) {
714 $os = 'ios';
715 $phone = 'iphone';
716 } elseif ($detectmobile->is('PalmOS')) {
717 $os = $phone = 'palm';
718 } elseif ($detectmobile->is('SymbianOS')) {
719 $os = 'symbian';
720 } elseif ($detectmobile->is('webOS')) {
721 $os = 'webos';
722 } elseif ($detectmobile->is('MaemoOS')) {
723 $os = 'maemo';
724 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
725 $os = 'windows';
726 }
727 }
728
729 // OS
730 if (preg_match('/linux/i', $user_agent)) {
731 $os = 'linux';
732 } elseif (preg_match('/macintosh/i', $user_agent)) {
733 $os = 'macintosh';
734 } elseif (preg_match('/windows/i', $user_agent)) {
735 $os = 'windows';
736 }
737
738 // Name
739 $reg = array();
740 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
741 $name = 'firefox';
742 $version = empty($reg[2]) ? '' : $reg[2];
743 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
744 $name = 'edge';
745 $version = empty($reg[2]) ? '' : $reg[2];
746 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
747 $name = 'chrome';
748 $version = empty($reg[2]) ? '' : $reg[2];
749 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
750 // we can have 'chrome (Mozilla...) chrome x.y' in one string
751 $name = 'chrome';
752 } elseif (preg_match('/iceweasel/i', $user_agent)) {
753 $name = 'iceweasel';
754 } elseif (preg_match('/epiphany/i', $user_agent)) {
755 $name = 'epiphany';
756 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
757 $name = 'safari';
758 $version = empty($reg[2]) ? '' : $reg[2];
759 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
760 // Safari is often present in string for mobile but its not.
761 $name = 'opera';
762 $version = empty($reg[2]) ? '' : $reg[2];
763 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
764 $name = 'ie';
765 $version = end($reg);
766 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
767 // MS products at end
768 $name = 'ie';
769 $version = end($reg);
770 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
771 // MS products at end
772 $name = 'textbrowser';
773 $version = empty($reg[3]) ? '' : $reg[3];
774 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
775 // MS products at end
776 $name = 'textbrowser';
777 $version = empty($reg[1]) ? '' : $reg[1];
778 }
779
780 if ($tablet) {
781 $layout = 'tablet';
782 } elseif ($phone) {
783 $layout = 'phone';
784 } else {
785 $layout = 'classic';
786 }
787
788 return array(
789 'browsername' => $name,
790 'browserversion' => $version,
791 'browseros' => $os,
792 'browserua' => $user_agent,
793 'layout' => $layout, // tablet, phone, classic
794 'phone' => $phone, // deprecated
795 'tablet' => $tablet // deprecated
796 );
797}
798
804function dol_shutdown()
805{
806 global $db;
807 $disconnectdone = false;
808 $depth = 0;
809 if (is_object($db) && !empty($db->connected)) {
810 $depth = $db->transaction_opened;
811 $disconnectdone = $db->close();
812 }
813 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));
814}
815
825function GETPOSTISSET($paramname)
826{
827 $isset = false;
828
829 $relativepathstring = $_SERVER["PHP_SELF"];
830 // Clean $relativepathstring
831 if (constant('DOL_URL_ROOT')) {
832 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
833 }
834 $relativepathstring = ltrim($relativepathstring, '/');
835 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
836
837 // Code for search criteria persistence.
838 // Retrieve values if restore_lastsearch_values
839 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
840 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
841 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
842 if (is_array($tmp)) {
843 foreach ($tmp as $key => $val) {
844 if ($key == $paramname) { // We are on the requested parameter
845 $isset = true;
846 break;
847 }
848 }
849 }
850 }
851 // If there is saved contextpage, limit, page or mode
852 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
853 $isset = true;
854 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
855 $isset = true;
856 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
857 $isset = true;
858 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
859 $isset = true;
860 }
861 } else {
862 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
863 }
864
865 return $isset;
866}
867
876function GETPOSTISARRAY($paramname, $method = 0)
877{
878 // for $method test need return the same $val as GETPOST
879 if (empty($method)) {
880 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
881 } elseif ($method == 1) {
882 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
883 } elseif ($method == 2) {
884 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
885 } elseif ($method == 3) {
886 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
887 } else {
888 $val = 'BadFirstParameterForGETPOST';
889 }
890
891 return is_array($val);
892}
893
894
904function GETPOSTINT($paramname, $method = 0)
905{
906 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
907}
908
922function GETPOSTFLOAT($paramname, $rounding = '', $option = 2)
923{
924 // price2num() can be used to round to an expected accuracy and/or to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.)
925 return (float) price2num(GETPOST($paramname), $rounding, $option);
926}
927
943function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto', $saverestore = '')
944{
945 $m = array();
946 if ($hourTime === 'getpost' || $hourTime === 'getpostend') {
947 $hour = (GETPOSTISSET($prefix . 'hour') && GETPOSTINT($prefix . 'hour') >= 0) ? GETPOSTINT($prefix . 'hour') : ($hourTime === 'getpostend' ? 23 : 0);
948 $minute = (GETPOSTISSET($prefix . 'min') && GETPOSTINT($prefix . 'min') >= 0) ? GETPOSTINT($prefix . 'min') : ($hourTime === 'getpostend' ? 59 : 0);
949 $second = (GETPOSTISSET($prefix . 'sec') && GETPOSTINT($prefix . 'sec') >= 0) ? GETPOSTINT($prefix . 'sec') : ($hourTime === 'getpostend' ? 59 : 0);
950 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
951 $hour = intval($m[1]);
952 $minute = intval($m[2]);
953 $second = intval($m[3]);
954 } elseif ($hourTime === 'end') {
955 $hour = 23;
956 $minute = 59;
957 $second = 59;
958 } else {
959 $hour = $minute = $second = 0;
960 }
961
962 if (
963 $saverestore
964 && !GETPOSTISSET($prefix . 'day')
965 && !GETPOSTISSET($prefix . 'month')
966 && !GETPOSTISSET($prefix . 'year')
967 && isset($_SESSION['DOLDATE_' . $saverestore . '_day'])
968 && isset($_SESSION['DOLDATE_' . $saverestore . '_month'])
969 && isset($_SESSION['DOLDATE_' . $saverestore . '_year'])
970 ) {
971 $day = $_SESSION['DOLDATE_' . $saverestore . '_day'];
972 $month = $_SESSION['DOLDATE_' . $saverestore . '_month'];
973 $year = $_SESSION['DOLDATE_' . $saverestore . '_year'];
974 } else {
975 $month = GETPOSTINT($prefix . 'month');
976 $day = GETPOSTINT($prefix . 'day');
977 $year = GETPOSTINT($prefix . 'year');
978 }
979
980 // normalize out of range values
981 $hour = (int) min($hour, 23);
982 $minute = (int) min($minute, 59);
983 $second = (int) min($second, 59);
984
985 if ($saverestore) {
986 $_SESSION['DOLDATE_' . $saverestore . '_day'] = $day;
987 $_SESSION['DOLDATE_' . $saverestore . '_month'] = $month;
988 $_SESSION['DOLDATE_' . $saverestore . '_year'] = $year;
989 }
990
991 //print "$hour, $minute, $second, $month, $day, $year, $gm<br>";
992 return dol_mktime($hour, $minute, $second, $month, $day, $year, $gm);
993}
994
1035function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
1036{
1037 global $mysoc, $user, $conf;
1038
1039 if (empty($paramname)) { // Explicit test for null for phan.
1040 return 'BadFirstParameterForGETPOST';
1041 }
1042 if (empty($check)) {
1043 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);
1044 // Enable this line to know who call the GETPOST with '' $check parameter.
1045 //var_dump(getCallerInfoString());
1046 }
1047
1048 if (empty($method)) {
1049 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
1050 } elseif ($method == 1) {
1051 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
1052 } elseif ($method == 2) {
1053 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
1054 } elseif ($method == 3) {
1055 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
1056 } else {
1057 return 'BadThirdParameterForGETPOST';
1058 }
1059
1060 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
1061
1062 if (empty($method) || $method == 3 || $method == 4) {
1063 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
1064 // Clean $relativepathstring
1065 if (constant('DOL_URL_ROOT')) {
1066 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
1067 }
1068 $relativepathstring = ltrim($relativepathstring, '/');
1069 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1070
1071 // Code for search criteria persistence.
1072 // Retrieve saved values if restore_lastsearch_values is set
1073 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
1074 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
1075 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
1076 if (is_array($tmp)) {
1077 foreach ($tmp as $key => $val) {
1078 if ($key == $paramname) { // We are on the requested parameter
1079 $out = $val;
1080 break;
1081 }
1082 }
1083 }
1084 }
1085 // If there is saved contextpage, page or limit
1086 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
1087 $out = $_SESSION['lastsearch_contextpage_' . $relativepathstring];
1088 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
1089 $out = $_SESSION['lastsearch_limit_' . $relativepathstring];
1090 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
1091 $out = $_SESSION['lastsearch_page_' . $relativepathstring];
1092 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
1093 $out = $_SESSION['lastsearch_mode_' . $relativepathstring];
1094 }
1095 } elseif (!isset($_GET['sortfield'])) {
1096 // Else, retrieve default values if we are not doing a sort
1097 // 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
1098 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1099 // Search default value from $object->field
1100 global $object;
1101 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
1102 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
1103 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
1104 $out = $object->fields[$paramname]['default'];
1105 }
1106 }
1107 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1108 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1109 // Now search in setup to overwrite default values
1110 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
1111 if (isset($user->default_values[$relativepathstring]['createform'])) {
1112 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
1113 $qualified = 0;
1114 if ($defkey != '_noquery_') {
1115 $tmpqueryarraytohave = explode('&', $defkey);
1116 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1117 $foundintru = 0;
1118 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1119 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1120 $foundintru = 1;
1121 }
1122 }
1123 if (!$foundintru) {
1124 $qualified = 1;
1125 }
1126 } else {
1127 $qualified = 1;
1128 }
1129
1130 if ($qualified) {
1131 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
1132 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
1133 break;
1134 }
1135 }
1136 }
1137 }
1138 }
1139 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1140 // Management of default search_filters and sort order
1141 if (!empty($user->default_values)) {
1142 // $user->default_values defined from menu 'Setup - Default values'
1143 //var_dump($user->default_values[$relativepathstring]);
1144 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
1145 // Sorted on which fields ? ASC or DESC ?
1146 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
1147 // Even if paramname is sortfield, data are stored into ['sortorder...']
1148 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
1149 $qualified = 0;
1150 if ($defkey != '_noquery_') {
1151 $tmpqueryarraytohave = explode('&', $defkey);
1152 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1153 $foundintru = 0;
1154 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1155 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1156 $foundintru = 1;
1157 }
1158 }
1159 if (!$foundintru) {
1160 $qualified = 1;
1161 }
1162 } else {
1163 $qualified = 1;
1164 }
1165
1166 if ($qualified) {
1167 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1168 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
1169 if ($out) {
1170 $out .= ', ';
1171 }
1172 if ($paramname == 'sortfield') {
1173 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
1174 }
1175 if ($paramname == 'sortorder') {
1176 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
1177 }
1178 }
1179 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
1180 }
1181 }
1182 }
1183 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
1184 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
1185 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
1186 continue;
1187 }
1188 $qualified = 0;
1189 if ($defkey != '_noquery_') {
1190 $tmpqueryarraytohave = explode('&', $defkey);
1191 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1192 $foundintru = 0;
1193 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1194 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1195 $foundintru = 1;
1196 }
1197 }
1198 if (!$foundintru) {
1199 $qualified = 1;
1200 }
1201 } else {
1202 $qualified = 1;
1203 }
1204
1205 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
1206 // We must keep $_POST and $_GET here
1207 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
1208 // We made a search from quick search menu, do we still use default filter ?
1209 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
1210 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1211 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1212 }
1213 } else {
1214 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1215 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1216 }
1217 break;
1218 }
1219 }
1220 }
1221 }
1222 }
1223 }
1224 }
1225 }
1226
1227 // 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)
1228 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1229 // 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.
1230 '@phan-var-force string $paramname';
1231 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1232 $reg = array();
1233 $regreplace = array();
1234 $maxloop = 20;
1235 $loopnb = 0; // Protection against infinite loop
1236
1237 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.
1238 $loopnb++;
1239 $newout = '';
1240
1241 if ($reg[1] == 'DAY') {
1242 $tmp = dol_getdate(dol_now(), true);
1243 $newout = $tmp['mday'];
1244 } elseif ($reg[1] == 'MONTH') {
1245 $tmp = dol_getdate(dol_now(), true);
1246 $newout = $tmp['mon'];
1247 } elseif ($reg[1] == 'YEAR') {
1248 $tmp = dol_getdate(dol_now(), true);
1249 $newout = $tmp['year'];
1250 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1251 $tmp = dol_getdate(dol_now(), true);
1252 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1253 $newout = $tmp2['day'];
1254 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1255 $tmp = dol_getdate(dol_now(), true);
1256 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1257 $newout = $tmp2['month'];
1258 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1259 $tmp = dol_getdate(dol_now(), true);
1260 $newout = ($tmp['year'] - 1);
1261 } elseif ($reg[1] == 'NEXT_DAY') {
1262 $tmp = dol_getdate(dol_now(), true);
1263 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1264 $newout = $tmp2['day'];
1265 } elseif ($reg[1] == 'NEXT_MONTH') {
1266 $tmp = dol_getdate(dol_now(), true);
1267 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1268 $newout = $tmp2['month'];
1269 } elseif ($reg[1] == 'NEXT_YEAR') {
1270 $tmp = dol_getdate(dol_now(), true);
1271 $newout = ($tmp['year'] + 1);
1272 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1273 $newout = $mysoc->country_id;
1274 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1275 $newout = $user->id;
1276 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1277 $newout = $user->fk_user;
1278 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1279 $newout = $conf->entity;
1280 } elseif ($reg[1] == 'ID') {
1281 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1282 } else {
1283 $newout = 'REGREPLACE_' . $loopnb; // Key not found, we replace with temporary string to reload later
1284 $regreplace[$loopnb] = $reg[0];
1285 }
1286 //var_dump('__'.$reg[1].'__ -> '.$newout);
1287 $out = preg_replace('/__' . preg_quote($reg[1], '/') . '__/', $newout, $out);
1288 }
1289 if (!empty($regreplace)) {
1290 foreach ($regreplace as $key => $value) {
1291 $out = preg_replace('/REGREPLACE_' . $key . '/', $value, $out);
1292 }
1293 }
1294 }
1295
1296 // Check type of variable and make sanitization according to this
1297 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1298 $tmpcheck = 'alphanohtml';
1299 if (empty($out)) {
1300 $out = array();
1301 } elseif (!is_array($out)) {
1302 $out = explode(',', $out);
1303 } else {
1304 $tmparray = explode(':', $check);
1305 if (!empty($tmparray[1])) {
1306 $tmpcheck = $tmparray[1];
1307 }
1308 }
1309 foreach ($out as $outkey => $outval) {
1310 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1311 }
1312 } else {
1313 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1314 // 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
1315 if (strpos($paramname, 'search_') === 0) {
1316 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1317 }
1318
1319 // @phan-suppress-next-line UnknownSanitizeType
1320 $out = sanitizeVal($out, $check, $filter, $options);
1321 }
1322
1323 // Sanitizing for special parameters.
1324 // 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.
1325 // @TODO Merge backtopage with backtourl
1326 // @TODO Rename backtolist into backtopagelist
1327 if (preg_match('/^backto/i', $paramname)) {
1328 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1329 $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.
1330 do {
1331 $oldstringtoclean = $out;
1332 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1333 $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'
1334 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1335 } while ($oldstringtoclean != $out);
1336 }
1337
1338 // Code for search criteria persistence.
1339 // Save data into session if key start with 'search_'
1340 if (empty($method) || $method == 3 || $method == 4) {
1341 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1342 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1343
1344 // We save search key only if $out not empty that means:
1345 // - posted value not empty, or
1346 // - 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).
1347
1348 if ($out != '' && isset($user)) { // $out = '0' or 'abc', it is a search criteria to keep
1349 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1350 }
1351 }
1352 }
1353
1354 return $out;
1355}
1356
1366function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1367{
1368 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1369 // Check is done after replacement
1370 if ($out === null) {
1371 $out = '';
1372 }
1373 switch ($check) {
1374 case 'none':
1375 case 'password':
1376 break;
1377 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1378 if (!is_numeric($out)) {
1379 $out = '';
1380 }
1381 break;
1382 case 'intcomma':
1383 if (is_array($out)) {
1384 $out = implode(',', $out);
1385 }
1386 if (preg_match('/[^0-9,-]+/i', $out)) {
1387 $out = '';
1388 }
1389 break;
1390 case 'san_alpha':
1391 dol_syslog("Use of parameter value 'san_alpha' in GETPOST is deprecated. Use 'alphanohtml', 'aZ09comma', ...", LOG_WARNING);
1392 $out = filter_var($out, FILTER_SANITIZE_STRING);
1393 break;
1394 case 'email':
1395 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1396 break;
1397 case 'url':
1398 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1399 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1400 // TODO Allow ( ) but only into password of https://login:password@domain...
1401 break;
1402 case 'aZ':
1403 if (!is_array($out)) {
1404 $out = trim($out);
1405 if (preg_match('/[^a-z]+/i', $out)) {
1406 $out = '';
1407 }
1408 }
1409 break;
1410 case 'aZ09':
1411 if (!is_array($out)) {
1412 $out = trim($out);
1413 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1414 $out = '';
1415 }
1416 }
1417 break;
1418 case 'aZ09arobase': // great to sanitize $objecttype parameter
1419 if (!is_array($out)) {
1420 $out = trim($out);
1421 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1422 $out = '';
1423 }
1424 }
1425 break;
1426 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1427 if (!is_array($out)) {
1428 $out = trim($out);
1429 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1430 $out = '';
1431 }
1432 }
1433 break;
1434 case 'alpha': // No html and no ../ and "
1435 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1436 if (!is_array($out)) {
1437 $out = trim($out);
1438 do {
1439 $oldstringtoclean = $out;
1440 // Remove html tags
1441 $out = dol_string_nohtmltag($out, 0);
1442 // 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).
1443 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1444 // Remove also other dangerous string sequences
1445 // '../' or '..\' is dangerous because it allows dir transversals
1446 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1447 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1448 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1449 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1450 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1451 } while ($oldstringtoclean != $out);
1452 // keep lines feed
1453 }
1454 break;
1455 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'
1456 if (!is_array($out)) {
1457 $out = trim($out);
1458 do {
1459 $oldstringtoclean = $out;
1460 // Decode html entities
1461 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1462 // 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).
1463 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1464 // Remove also other dangerous string sequences
1465 // '../' or '..\' is dangerous because it allows dir transversals
1466 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1467 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1468 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1469 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1470 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1471 } while ($oldstringtoclean != $out);
1472 }
1473 break;
1474 case 'nohtml': // No html. Valid for JSON strings.
1475 $out = dol_string_nohtmltag($out, 0);
1476 break;
1477 case 'restricthtmlnolink':
1478 case 'restricthtml': // Recommended for most html textarea
1479 case 'restricthtmlallowclass':
1480 case 'restricthtmlallowiframe':
1481 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1482 case 'restricthtmlallowunvalid':
1483 $out = dol_htmlwithnojs($out, 1, $check);
1484 break;
1485 case 'custom':
1486 if (!empty($out)) {
1487 if (empty($filter)) {
1488 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1489 }
1490 if (is_null($options)) {
1491 $options = 0;
1492 }
1493 $out = filter_var($out, $filter, $options);
1494 }
1495 break;
1496 default:
1497 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1498 $out = GETPOST($out, 'alphanohtml');
1499 break;
1500 }
1501
1502 return $out;
1503}
1504
1513function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1514{
1515 global $dolibarr_main_force_https;
1516
1517 if ($expire == -1) {
1518 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1519 }
1520
1521 if (PHP_VERSION_ID < 70300) {
1522 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1523 } else {
1524 // Only available for php >= 7.3
1525 $cookieparams = array(
1526 'expires' => empty($cookievalue) ? 0 : $expire,
1527 'path' => '/',
1528 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1529 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1530 'httponly' => true,
1531 'samesite' => 'Lax' // None || Lax || Strict
1532 );
1533 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1534 }
1535 if (empty($cookievalue)) {
1536 unset($_COOKIE[$cookiename]);
1537 }
1538}
1539
1540if (!function_exists('dol_getprefix')) {
1551 function dol_getprefix($mode = '')
1552 {
1553 // If prefix is for email (we need to have $conf already loaded for this case)
1554 if ($mode == 'email') {
1555 global $conf;
1556
1557 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1558 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1559 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1560 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1561 return $_SERVER["SERVER_NAME"];
1562 }
1563 }
1564
1565 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1566 if (!empty($conf->file->instance_unique_id)) {
1567 return sha1('dolibarr' . $conf->file->instance_unique_id);
1568 }
1569
1570 // For backward compatibility when instance_unique_id is not set
1571 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1572 }
1573
1574 // If prefix is for session (no need to have $conf loaded)
1575 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1576 $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
1577
1578 // The recommended value (may be not defined for old versions)
1579 if (!empty($tmp_instance_unique_id)) {
1580 return sha1('dolibarr' . $tmp_instance_unique_id);
1581 }
1582
1583 // For backward compatibility when instance_unique_id is not set
1584 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1585 return sha1($_SERVER["SERVER_NAME"] . $_SERVER["DOCUMENT_ROOT"] . DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1586 } else {
1587 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1588 }
1589 }
1590}
1591
1602function dol_include_once($relpath, $classname = '')
1603{
1604 global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we make "include". Other globals var must be retrieved with $GLOBALS['var']
1605
1606 if (strpos($relpath, '..') !== false) {
1607 // Found a not valid path
1608 dol_syslog('functions::dol_include_once Tried to load a file with a path including a forbidden sequence ".." : ' . $relpath, LOG_WARNING);
1609 return false;
1610 }
1611 if (!preg_match('/\.php$/', $relpath)) {
1612 // Found a not valid path
1613 dol_syslog('functions::dol_include_once Tried to load a file that is not a PHP file : ' . $relpath, LOG_WARNING);
1614 return false;
1615 }
1616
1617 $fullpath = dol_buildpath($relpath);
1618
1619 if (!file_exists($fullpath)) {
1620 dol_syslog('functions::dol_include_once Tried to load unexisting file: ' . $relpath, LOG_WARNING);
1621 return false;
1622 }
1623 if (!empty($classname) && !class_exists($classname)) {
1624 return include $fullpath;
1625 } else {
1626 return include_once $fullpath;
1627 }
1628}
1629
1630
1644function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1645{
1646 global $conf;
1647
1648 $path = preg_replace('/^\//', '', $path);
1649
1650 if (empty($type)) { // For a filesystem path
1651 $res = DOL_DOCUMENT_ROOT . '/' . $path; // Standard default path
1652 if (is_array($conf->file->dol_document_root)) {
1653 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1654 if ($key == 'main') {
1655 continue;
1656 }
1657 // if (@file_exists($dirroot.'/'.$path)) {
1658 if (@file_exists($dirroot . '/' . $path)) { // avoid [php:warn]
1659 if ($key != 'main' && preg_match('/^core\//', $path)) { // When searching into an alternative custom path, we don't want path like 'core/...' because path should be 'modulename/core/...'
1660 continue;
1661 }
1662 $res = $dirroot . '/' . $path;
1663 return $res;
1664 }
1665 }
1666 }
1667 if ($returnemptyifnotfound) {
1668 // Not found into alternate dir
1669 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1670 return '';
1671 }
1672 }
1673 } else {
1674 // For an url path
1675 // We try to get local path of file on filesystem from url
1676 // Note that trying to know if a file on disk exist by forging path on disk from url
1677 // works only for some web server and some setup. This is bugged when
1678 // using proxy, rewriting, virtual path, etc...
1679 $res = '';
1680 if ($type == 1) {
1681 $res = DOL_URL_ROOT . '/' . $path; // Standard value
1682 }
1683 if ($type == 2) {
1684 $res = DOL_MAIN_URL_ROOT . '/' . $path; // Standard value
1685 }
1686 if ($type == 3) {
1687 $res = DOL_URL_ROOT . '/' . $path;
1688 }
1689
1690 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1691 if ($key == 'main') {
1692 if ($type == 3) {
1693 /*global $dolibarr_main_url_root;*/
1694
1695 // Define $urlwithroot
1696 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1697 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1698 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1699
1700 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . '/' . $path; // Test on start with http is for old conf syntax
1701 }
1702 continue;
1703 }
1704 $regs = array();
1705 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1706 if (!empty($regs[1])) {
1707 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1708 //if (file_exists($dirroot.'/'.$regs[1])) {
1709 if (@file_exists($dirroot . '/' . $regs[1])) { // avoid [php:warn]
1710 if ($type == 1) {
1711 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1712 } elseif ($type == 2) {
1713 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1714 } elseif ($type == 3) {
1715 /*global $dolibarr_main_url_root;*/
1716
1717 // Define $urlwithroot
1718 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1719 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1720 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1721
1722 $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
1723 }
1724 break;
1725 }
1726 }
1727 }
1728 }
1729
1730 return $res;
1731}
1732
1741function dolBuildUrl($url, $params = [], $addtoken = false)
1742{
1743 global $db, $hookmanager;
1744
1745 if (!is_object($hookmanager)) {
1746 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
1747 $hookmanager = new HookManager($db);
1748 }
1749 if ((!isset($params['mainmenu']) || empty($params['mainmenu'])) && GETPOSTISSET('mainmenu')) {
1750 $params = array_merge($params, ['mainmenu' => (GETPOST('mainmenu', 'restricthtml'))]);
1751 }
1752 if ((!isset($params['leftmenu'])/* || empty($params['leftmenu']) */) && GETPOSTISSET('leftmenu')) { // do not fill leftmenu if we have leftmenu=
1753 $params = array_merge($params, ['leftmenu' => (GETPOST('leftmenu', 'restricthtml'))]);
1754 }
1755 $parameters = [
1756 'path' => &$url,
1757 'params' => &$params,
1758 'addtoken' => &$addtoken,
1759 ];
1760 $hookmanager->executeHooks('buildurl', $parameters);
1761 if ($addtoken) {
1762 $params = array_merge($params, ['token' => newToken()]);
1763 }
1764 // TODO TO REMOVE
1765 if (getDolGlobalString('MAIN_DEBUG_DOL_BUILDURL')) {
1766 $params = array_merge($params, ['debug' => 'debug']);
1767 }
1768 if ($params) {
1769 $url .= '?' . http_build_query($params);
1770 }
1771
1772 return $url;
1773}
1774
1785function dol_get_object_properties($obj, $properties = [])
1786{
1787 // Get real properties using get_object_vars() if $properties is empty
1788 if (empty($properties)) {
1789 return get_object_vars($obj);
1790 }
1791
1792 $existingProperties = [];
1793 $realProperties = get_object_vars($obj);
1794
1795 // Get the real or magic property values
1796 foreach ($properties as $property) {
1797 if (array_key_exists($property, $realProperties)) {
1798 // Real property, add the value
1799 $existingProperties[$property] = $obj->{$property};
1800 } elseif (property_exists($obj, $property)) {
1801 // Magic property
1802 $existingProperties[$property] = $obj->{$property};
1803 }
1804 }
1805
1806 return $existingProperties;
1807}
1808
1809
1825function dol_clone($srcobject, $native = 2)
1826{
1827 if ($native == 0) {
1828 // deprecated method, use the method with native = 2 instead
1829 dol_syslog("Warning, call to dol_clone() with the deprecated parameter native=0, use 2 instead", LOG_WARNING);
1830
1831 $tmpsavdb = null;
1832 if (isset($srcobject->db) && isset($srcobject->db->db) && is_object($srcobject->db->db) && get_class($srcobject->db->db) == 'PgSql\Connection') {
1833 $tmpsavdb = $srcobject->db;
1834 unset($srcobject->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1835 }
1836
1837 $myclone = unserialize(serialize($srcobject)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1838
1839 if (!empty($tmpsavdb)) {
1840 $srcobject->db = $tmpsavdb;
1841 }
1842 } elseif ($native == 2) {
1843 // recommended method to have a full secured isolated cloned object
1844 $myclone = new stdClass();
1845 $tmparray = get_object_vars($srcobject); // return only public properties
1846
1847 if (is_array($tmparray)) {
1848 foreach ($tmparray as $propertykey => $propertyval) {
1849 if (is_scalar($propertyval) || is_array($propertyval)) {
1850 $myclone->$propertykey = $propertyval;
1851 }
1852 }
1853 }
1854 } else {
1855 $myclone = clone $srcobject; // 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)
1856 }
1857
1858 return $myclone;
1859}
1860
1861
1870function dol_clone_in_array($srcobject, $startlevel = 0)
1871{
1872 if (is_object($srcobject)) {
1873 $srcobject = get_object_vars($srcobject); // exclude private/protected properties
1874 }
1875
1876 if (is_array($srcobject)) {
1877 $result = [];
1878 foreach ($srcobject as $key => $value) {
1879 if (in_array($key, array('db', 'fields', 'error', 'errorhidden', 'errors', 'oldcopy', 'linkedObjects', 'linked_objects'))) {
1880 continue;
1881 }
1882 $result[$key] = dol_clone_in_array($value, $startlevel + 1);
1883 }
1884 return $result;
1885 }
1886
1887 return $srcobject;
1888}
1889
1890
1900function dol_size($size, $type = '')
1901{
1902 global $conf;
1903 if (empty($conf->dol_optimize_smallscreen)) {
1904 return $size;
1905 }
1906 if ($type == 'width' && $size > 250) {
1907 return 250;
1908 } else {
1909 return 10;
1910 }
1911}
1912
1913
1927function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0, $allowdash = 0)
1928{
1929 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1930 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1931 // Char '/' and '\' are file delimiters.
1932 // 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
1933 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1934 if ($includequotes) {
1935 $filesystem_forbidden_chars[] = "'";
1936 }
1937 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1938 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1939 if (empty($allowdash)) {
1940 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1941 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1942 }
1943 $tmp = str_replace('..', '', $tmp);
1944 $tmp = str_replace('~', $newstr, $tmp);
1945 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1946
1947 return $tmp;
1948}
1949
1950
1963function dol_sanitizePathName($str, $newstr = '_', $unaccent = 0, $allowdash = 0)
1964{
1965 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1966 // Char '>' '<' '|' '$' ';' and '`' are special chars for shells.
1967 // Char '?' and '*' are for wild card chars.
1968 // Char '"' is dangerous.
1969 // Char '°' is just not expected.
1970 // Chars '-' and '--' 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
1971 // Chars '--' and '~' can be used for path transversal
1972 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1973
1974 $tmp = $str;
1975 if ($unaccent) {
1976 $tmp = dol_string_unaccent($tmp);
1977 }
1978 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1979 $tmp = preg_replace('/\-\-+/', $newstr, $tmp);
1980 if (empty($allowdash)) {
1981 $tmp = preg_replace('/\s+\-([^\s])/', ' '.$newstr.'$1', $tmp);
1982 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1983 }
1984 $tmp = str_replace('..', $newstr, $tmp);
1985 $tmp = str_replace('~', $newstr, $tmp);
1986 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1987
1988 return $tmp;
1989}
1990
1998function dol_sanitizeUrl($stringtoclean, $type = 1)
1999{
2000 // 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)
2001 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
2002 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2003 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
2004 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
2005
2006 $stringtoclean = str_replace('\\', '/', $stringtoclean);
2007 if ($type == 1) {
2008 // removing : should disable links to external url like http:aaa)
2009 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
2010 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
2011 }
2012
2013 do {
2014 $oldstringtoclean = $stringtoclean;
2015 // removing '&colon' should disable links to external url like http:aaa)
2016 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
2017 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
2018 } while ($oldstringtoclean != $stringtoclean);
2019
2020 if ($type == 1) {
2021 // removing '//' should disable links to external url like //aaa or http//)
2022 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
2023 }
2024
2025 return $stringtoclean;
2026}
2027
2034function dol_sanitizeEmail($stringtoclean)
2035{
2036 do {
2037 $oldstringtoclean = $stringtoclean;
2038 $stringtoclean = str_ireplace(array('"', ':', '[', ']', "\n", "\r", '\\', '\/'), '', $stringtoclean);
2039 } while ($oldstringtoclean != $stringtoclean);
2040
2041 return $stringtoclean;
2042}
2043
2052function dol_sanitizeKeyCode($str)
2053{
2054 return preg_replace('/[^\w]+/', '', $str);
2055}
2056
2057
2066function dol_string_unaccent($str)
2067{
2068 if (is_null($str)) {
2069 return '';
2070 }
2071
2072 if (utf8_check($str)) {
2073 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
2074 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
2075 return $transliterator->transliterate($str);
2076 }
2077 // See http://www.utf8-chartable.de/
2078 $string = rawurlencode($str);
2079 $replacements = array(
2080 '%C3%80' => 'A',
2081 '%C3%81' => 'A',
2082 '%C3%82' => 'A',
2083 '%C3%83' => 'A',
2084 '%C3%84' => 'A',
2085 '%C3%85' => 'A',
2086 '%C3%87' => 'C',
2087 '%C3%88' => 'E',
2088 '%C3%89' => 'E',
2089 '%C3%8A' => 'E',
2090 '%C3%8B' => 'E',
2091 '%C3%8C' => 'I',
2092 '%C3%8D' => 'I',
2093 '%C3%8E' => 'I',
2094 '%C3%8F' => 'I',
2095 '%C3%91' => 'N',
2096 '%C3%92' => 'O',
2097 '%C3%93' => 'O',
2098 '%C3%94' => 'O',
2099 '%C3%95' => 'O',
2100 '%C3%96' => 'O',
2101 '%C5%A0' => 'S',
2102 '%C3%99' => 'U',
2103 '%C3%9A' => 'U',
2104 '%C3%9B' => 'U',
2105 '%C3%9C' => 'U',
2106 '%C3%9D' => 'Y',
2107 '%C5%B8' => 'y',
2108 '%C3%A0' => 'a',
2109 '%C3%A1' => 'a',
2110 '%C3%A2' => 'a',
2111 '%C3%A3' => 'a',
2112 '%C3%A4' => 'a',
2113 '%C3%A5' => 'a',
2114 '%C3%A7' => 'c',
2115 '%C3%A8' => 'e',
2116 '%C3%A9' => 'e',
2117 '%C3%AA' => 'e',
2118 '%C3%AB' => 'e',
2119 '%C3%AC' => 'i',
2120 '%C3%AD' => 'i',
2121 '%C3%AE' => 'i',
2122 '%C3%AF' => 'i',
2123 '%C3%B1' => 'n',
2124 '%C3%B2' => 'o',
2125 '%C3%B3' => 'o',
2126 '%C3%B4' => 'o',
2127 '%C3%B5' => 'o',
2128 '%C3%B6' => 'o',
2129 '%C5%A1' => 's',
2130 '%C3%B9' => 'u',
2131 '%C3%BA' => 'u',
2132 '%C3%BB' => 'u',
2133 '%C3%BC' => 'u',
2134 '%C3%BD' => 'y',
2135 '%C3%BF' => 'y',
2136 '%CC%80' => '',
2137 '%CC%81' => '',
2138 '%CC%82' => '',
2139 '%CC%83' => '',
2140 '%CC%84' => '',
2141 '%CC%85' => '',
2142 '%CC%86' => '',
2143 '%CC%87' => '',
2144 '%CC%88' => '',
2145 '%CC%89' => '',
2146 '%CC%8A' => '',
2147 '%CC%8B' => '',
2148 '%CC%8C' => '',
2149 '%CC%8D' => '',
2150 '%CC%8E' => '',
2151 '%CC%8F' => '',
2152 '%CC%90' => '',
2153 '%CC%91' => '',
2154 '%CC%A7' => '',
2155 );
2156 $string = strtr($string, $replacements);
2157 return rawurldecode($string);
2158 } else {
2159 // See http://www.ascii-code.com/
2160 $string = strtr(
2161 $str,
2162 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
2163 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
2164 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
2165 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
2166 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
2167 \xF9\xFA\xFB\xFC\xFD\xFF",
2168 "AAAAAAC
2169 EEEEIIIIDN
2170 OOOOOUUUY
2171 aaaaaaceeee
2172 iiiidnooooo
2173 uuuuyy"
2174 );
2175 $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"));
2176 return $string;
2177 }
2178}
2179
2193function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
2194{
2195 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
2196 if (empty($keepspaces)) {
2197 $forbidden_chars_to_replace[] = " ";
2198 }
2199 $forbidden_chars_to_remove = array();
2200 //$forbidden_chars_to_remove=array("(",")");
2201
2202 if (is_array($badcharstoreplace)) {
2203 $forbidden_chars_to_replace = $badcharstoreplace;
2204 }
2205 if (is_array($badcharstoremove)) {
2206 $forbidden_chars_to_remove = $badcharstoremove;
2207 }
2208
2209 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2210 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
2211}
2212
2213
2227function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2228{
2229 if ($removetabcrlf) {
2230 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2231 } else {
2232 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
2233 }
2234}
2235
2242function dolSlugify($stringtoslugify)
2243{
2244 $slug = dol_string_unaccent($stringtoslugify);
2245
2246 // Convert special characters to their ASCII equivalents
2247 if (function_exists('iconv')) {
2248 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2249 }
2250
2251 // Convert to lowercase
2252 $slug = strtolower($slug);
2253
2254 // Replace non-alphanumeric characters with hyphens
2255 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2256
2257 // Remove leading and trailing hyphens
2258 $slug = trim($slug, '-');
2259
2260 return $slug;
2261}
2262
2271function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2272{
2273 if (is_null($stringtoescape)) {
2274 return '';
2275 }
2276
2277 // escape quotes and backslashes, newlines, etc.
2278 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2279 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2280 if (empty($noescapebackslashn)) {
2281 $substitjs["\n"] = '\\n';
2282 $substitjs['\\'] = '\\\\';
2283 }
2284 if (empty($mode)) {
2285 $substitjs["'"] = "\\'";
2286 $substitjs['"'] = "\\'";
2287 } elseif ($mode == 1) {
2288 $substitjs["'"] = "\\'";
2289 } elseif ($mode == 2) {
2290 $substitjs['"'] = '\\"';
2291 } elseif ($mode == 3) {
2292 $substitjs["'"] = "\\'";
2293 $substitjs['"'] = "\\\"";
2294 }
2295 return strtr((string) $stringtoescape, $substitjs);
2296}
2297
2307function dol_escape_uri($stringtoescape)
2308{
2309 return rawurlencode($stringtoescape);
2310}
2311
2318function dol_escape_json($stringtoescape)
2319{
2320 return str_replace('"', '\"', $stringtoescape);
2321}
2322
2330function dol_escape_php($stringtoescape, $stringforquotes = 2)
2331{
2332 if (is_null($stringtoescape)) {
2333 return '';
2334 }
2335
2336 if ($stringforquotes == 2) {
2337 return str_replace('"', "'", $stringtoescape);
2338 } elseif ($stringforquotes == 1) {
2339 // We remove the \ char.
2340 // If we allow the \ char, we can have $stringtoescape =
2341 // abc\';phpcodedanger; so the escapement will become
2342 // abc\\';phpcodedanger; and injecting this into
2343 // $a='...' will give $ac='abc\\';phpcodedanger;
2344 $stringtoescape = str_replace('\\', '', $stringtoescape);
2345 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2346 }
2347
2348 return 'Bad parameter for stringforquotes in dol_escape_php';
2349}
2350
2357function dol_escape_all($stringtoescape)
2358{
2359 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2360}
2361
2368function dol_escape_xml($stringtoescape)
2369{
2370 return $stringtoescape;
2371}
2372
2382function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2383{
2384 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2385}
2386
2395function dolPrintText($s)
2396{
2397 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2398}
2399
2410function dolPrintHTML($s, $allowiframe = 0)
2411{
2412 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2413 //$isAlreadyHTML = dol_textishtml($s);
2414
2415 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2416 // encode only special char like é but not &, <, >, ", ' if already HTML.
2417 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2418
2419 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2420}
2421
2432function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2433{
2434 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2435 if (!empty($allowothertags) && is_array($allowothertags)) {
2436 $allowedtags = array_merge($allowedtags, $allowothertags);
2437 }
2438 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2439 // The dol_escape_htmltag will escape html tags.
2440 if ($escapeonlyhtmltags) {
2441 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2442 } else {
2443 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2444 }
2445}
2446
2455function dolPrintHTMLForAttributeUrl($s)
2456{
2457 // The dol_htmlentitiesbr has been removed compared to dolPrintHTMLForAttribute because we know content is a HTML URL string (even if we have no way to detect it automatically)
2458 // The dol_escape_htmltag will escape html chars.
2459 $escapeonlyhtmltags = 1;
2460 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2461}
2462
2472function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2473{
2474 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2475}
2476
2483function dolPrintPassword($s)
2484{
2485 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2486}
2487
2488
2505function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2506{
2507 if ($noescapetags == 'common') {
2508 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,ol,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
2509 // Add also html5 tags
2510 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2511 }
2512 if ($cleanalsojavascript) {
2513 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2514 }
2515
2516 // escape quotes and backslashes, newlines, etc.
2517 if ($escapeonlyhtmltags) {
2518 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2519 } else {
2520 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2521 // Because content can be or not already HTML.
2522 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2523 // In a future, we should not need this
2524
2525 $tmp = (string) $stringtoescape;
2526
2527 // We protect the 6 special entities that we don't want to decode.
2528 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2529 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2530 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2531 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2532 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2533 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2534
2535 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2536
2537 // We restore the 6 special entities that we don't want to have been decoded by previous command
2538 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2539 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2540 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2541 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2542 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2543 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2544
2545 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2546 }
2547 if (!$keepb) {
2548 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2549 }
2550 if (!$keepn) {
2551 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2552 } elseif ($keepn == -1) {
2553 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2554 }
2555
2556 if ($escapeonlyhtmltags) {
2557 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2558 return $tmp;
2559 } else {
2560 // Now we protect all the tags we want to keep
2561 $tmparrayoftags = array();
2562 if ($noescapetags) {
2563 $tmparrayoftags = explode(',', $noescapetags);
2564 }
2565
2566 if (count($tmparrayoftags)) {
2567 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2568
2569 $reg = array();
2570 // Remove reserved keywords. They are forbidden in a source string
2571 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2572
2573 foreach ($tmparrayoftags as $tagtoreplace) {
2574 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2575 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . '>/', '__BEGINTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2576 $tmp = str_ireplace('</' . $tagtoreplace . '>', '__ENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2577 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . ' \/>/', '__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2578
2579 // For case of tag with attributes
2580 do {
2581 $tmpold = $tmp;
2582
2583 if (preg_match('/<' . preg_quote($tagtoreplace, '/') . '(\s+)([^>]+)>/', $tmp, $reg)) {
2584 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2585 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2586 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2587 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2588 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2589 $tmp = str_replace('<' . $tagtoreplace . $reg[1] . $reg[2] . '>', '__BEGINTAGTOREPLACE' . $tagtoreplace . '[' . $tmpattributes . ']__', $tmp);
2590 }
2591
2592 $diff = strcmp($tmpold, $tmp);
2593 } while ($diff);
2594 }
2595
2596 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2597 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2598 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2599 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2600 }
2601
2602 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2603 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2604
2605 //print $result;
2606
2607 if (count($tmparrayoftags)) {
2608 // Restore protected tags
2609 foreach ($tmparrayoftags as $tagtoreplace) {
2610 $result = str_ireplace('__BEGINTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . '>', $result);
2611 $result = preg_replace('/__BEGINTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1>', $result);
2612 $result = str_ireplace('__ENDTAGTOREPLACE' . $tagtoreplace . '__', '</' . $tagtoreplace . '>', $result);
2613 $result = str_ireplace('__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . ' />', $result);
2614 $result = preg_replace('/__BEGINENDTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1 />', $result);
2615 }
2616
2617 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2618
2619 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2620 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2621 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2622 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2623 }
2624
2625 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2626
2627 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2628
2629 return $result;
2630 }
2631}
2632
2640function dol_strtolower($string, $encoding = "UTF-8")
2641{
2642 if (function_exists('mb_strtolower')) {
2643 return mb_strtolower($string, $encoding);
2644 } else {
2645 return strtolower($string);
2646 }
2647}
2648
2657function dol_strtoupper($string, $encoding = "UTF-8")
2658{
2659 if (function_exists('mb_strtoupper')) {
2660 return mb_strtoupper($string, $encoding);
2661 } else {
2662 return strtoupper($string);
2663 }
2664}
2665
2674function dol_ucfirst($string, $encoding = "UTF-8")
2675{
2676 if (function_exists('mb_substr')) {
2677 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding) . mb_substr($string, 1, null, $encoding);
2678 } else {
2679 return ucfirst($string);
2680 }
2681}
2682
2691function dol_ucwords($string, $encoding = "UTF-8")
2692{
2693 if (function_exists('mb_convert_case')) {
2694 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2695 } else {
2696 return ucwords($string);
2697 }
2698}
2699
2700
2706function getCallerInfoString()
2707{
2708 $backtrace = debug_backtrace();
2709 $msg = "";
2710 if (count($backtrace) >= 1) {
2711 $pos = 1;
2712 if (count($backtrace) == 1) {
2713 $pos = 0;
2714 }
2715 $trace = $backtrace[$pos];
2716 if (isset($trace['file'], $trace['line'])) {
2717 $msg = " From {$trace['file']}:{$trace['line']}.";
2718 }
2719 }
2720 return $msg;
2721}
2722
2745function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2746{
2747 global $conf, $user, $debugbar;
2748
2749 // If syslog module enabled
2750 if (!isModEnabled('syslog')) {
2751 return;
2752 }
2753
2754 // Check if we are into execution of code of a website
2755 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2756 global $website, $websitekey;
2757 if (is_object($website) && !empty($website->ref)) {
2758 $suffixinfilename .= '_website_' . $website->ref;
2759 } elseif (!empty($websitekey)) {
2760 $suffixinfilename .= '_website_' . $websitekey;
2761 }
2762 }
2763
2764 // Check if we have a forced suffix
2765 if (defined('USESUFFIXINLOG')) {
2766 $suffixinfilename .= constant('USESUFFIXINLOG');
2767 }
2768
2769 if ($ident < 0) {
2770 foreach ($conf->loghandlers as $loghandlerinstance) {
2771 $loghandlerinstance->setIdent($ident);
2772 }
2773 }
2774
2775 if (!empty($message)) {
2776 // Test log level
2777 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2778 $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');
2779
2780 if (!array_key_exists($level, $logLevels)) {
2781 dol_syslog('Error Bad Log Level ' . $level, LOG_ERR);
2782 $level = LOG_ERR;
2783 }
2784 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2785 return;
2786 }
2787
2788 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2789 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2790 }
2791
2792 // If adding log inside HTML page is required
2793 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2794 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))
2795 ) {
2796 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2797 $osuser = " " . sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2798
2799 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S") . " " . sprintf("%-7s", $logLevels[$level]) . " " . $ospid . " " . $osuser . " " . $message;
2800 }
2801
2802 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2803 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2804 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2805 print "\n\n<!-- Log start\n";
2806 print dol_escape_htmltag($message) . "\n";
2807 print "Log end -->\n";
2808 }
2809
2810 $data = array(
2811 'message' => $message,
2812 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2813 'level' => $level,
2814 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2815 'ip' => '',
2816 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2817 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2818 );
2819
2820 // For log, we want the reliable IP first.
2821 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2822 if (!empty($remoteip)) {
2823 $data['ip'] = $remoteip;
2824 // This is when server run behind a reverse proxy
2825 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2826 // $data['ip'] is last
2827 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2828 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2829 $data['ip'] = '';
2830 $foundremoteip = 0;
2831 $j = 0;
2832 foreach ($tmpips as $tmpip) {
2833 $tmpip = trim($tmpip);
2834 if (strtolower($tmpip) == strtolower($remoteip)) {
2835 $foundremoteip = 1;
2836 }
2837 if (empty($data['ip'])) {
2838 $data['ip'] = $tmpip;
2839 } else {
2840 $j++;
2841 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2842 }
2843 }
2844 if (!$foundremoteip) {
2845 $j++;
2846 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2847 }
2848 $data['ip'] .= (($j > 0) ? ']' : '');
2849 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2850 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2851 $data['ip'] = '';
2852 $foundremoteip = 0;
2853 $j = 0;
2854 foreach ($tmpips as $tmpip) {
2855 $tmpip = trim($tmpip);
2856 if (strtolower($tmpip) == strtolower($remoteip)) {
2857 $foundremoteip = 1;
2858 }
2859 if (empty($data['ip'])) {
2860 $data['ip'] = $tmpip;
2861 } else {
2862 $j++;
2863 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2864 }
2865 }
2866 if (!$foundremoteip) {
2867 $j++;
2868 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2869 }
2870 $data['ip'] .= (($j > 0) ? ']' : '');
2871 }
2872 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2873 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2874 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2875 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2876 // 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).
2877 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2878 } else {
2879 $data['ip'] = '???';
2880 }
2881
2882 if (!empty($_SERVER['USERNAME'])) {
2883 // 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).
2884 $data['osuser'] = (string) $_SERVER['USERNAME'];
2885 } elseif (!empty($_SERVER['LOGNAME'])) {
2886 // 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).
2887 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2888 }
2889
2890 // Loop on each log handler and send output
2891 foreach ($conf->loghandlers as $loghandlerinstance) {
2892 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2893 continue;
2894 }
2895 $loghandlerinstance->export($data, $suffixinfilename);
2896 }
2897 unset($data);
2898 }
2899
2900 if ($ident > 0) {
2901 foreach ($conf->loghandlers as $loghandlerinstance) {
2902 $loghandlerinstance->setIdent($ident);
2903 }
2904 }
2905}
2906
2918function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2919{
2920 global $langs, $db;
2921
2922 $form = new Form($db);
2923
2924 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2925 if (empty($templatenameforexport)) {
2926 $templatenameforexport = 'website_' . $website->ref;
2927 }
2928
2929 $out = '';
2930 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="' . dol_escape_htmltag($buttonstring) . '"/>';
2931
2932 // for generate popup
2933 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2934 $out .= 'jQuery(document).ready(function () {';
2935 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2936 $out .= ' var dialogHtml = \'';
2937
2938 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2939 $dialogcontent .= ' <div style="margin-top: 20px;">';
2940 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>' . $langs->trans("ExportSiteLabel") . '...</label><br>';
2941 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2942 $dialogcontent .= ' </div>';
2943 $dialogcontent .= ' <br>';
2944 $dialogcontent .= ' <div style="margin-top: 20px;">';
2945 $dialogcontent .= ' <strong>' . $langs->trans("ExportSiteGitLabel") . ' ' . $form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '') . '</strong><br>';
2946 $dialogcontent .= ' <form action="' . dol_escape_htmltag($overwriteGitUrl) . '" method="POST">';
2947 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2948 $dialogcontent .= ' <input type="hidden" name="token" value="' . newToken() . '">';
2949 $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>';
2950 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2951 $dialogcontent .= ' </form>';
2952 $dialogcontent .= ' </div>';
2953 $dialogcontent .= ' </div>';
2954
2955 $out .= dol_escape_js($dialogcontent);
2956
2957 $out .= '\';';
2958
2959
2960 // Add the content of the dialog to the body of the page
2961 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2962 $out .= ' if ($dialog.length > 0) {
2963 $dialog.remove();
2964 }
2965 jQuery("body").append(dialogHtml);';
2966
2967 // Configuration of popup
2968 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2969 $out .= ' autoOpen: false,';
2970 $out .= ' modal: true,';
2971 $out .= ' height: 290,';
2972 $out .= ' width: "40%",';
2973 $out .= ' title: "' . dol_escape_js($label) . '",';
2974 $out .= ' });';
2975
2976 // Simulate a click on the original "submit" input to export the site.
2977 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2978 $out .= ' console.log("Clic on exportsite.");';
2979 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2980 $out .= ' console.log("element founded:", target.length > 0);';
2981 $out .= ' if (target.length > 0) { target.click(); }';
2982 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2983 $out .= ' });';
2984
2985 // open popup
2986 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2987 $out .= ' return false;';
2988 $out .= ' });';
2989 $out .= '});';
2990 $out .= '</script>';
2991
2992 return $out;
2993}
2994
2995
3012function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
3013{
3014 global $conf;
3015
3016 if (strpos($url, '?') > 0) {
3017 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
3018 } else {
3019 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
3020 }
3021
3022 if (preg_match('/^https/i', $url)) {
3023 $urltoopen = $url;
3024 } else {
3025 $urltoopen = DOL_URL_ROOT . $url;
3026 }
3027
3028 $out = '';
3029
3030 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
3031 $out .= '<!-- a link for button to open url into a dialog popup -->';
3032 $out .= '<a ' . ($accesskey ? ' accesskey="' . $accesskey . '"' : '') . ' class="cursorpointer reposition button_' . $name . ($morecss ? ' ' . $morecss : '') . '"' . $disabled . ' title="' . dol_escape_htmltag($label) . '"';
3033 if (empty($conf->use_javascript_ajax)) {
3034 $out .= ' href="' . $urltoopen . '" target="_blank"';
3035 } elseif ($jsonopen) {
3036 $out .= ' href="#" onclick="' . $jsonopen . '"';
3037 } else {
3038 $out .= ' href="#"';
3039 }
3040 $out .= '>' . $buttonstring . '</a>';
3041
3042 if (!empty($conf->use_javascript_ajax)) {
3043 // Add code to open url using the popup.
3044 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
3045 $out .= '<div id="idfordialog' . $name . '" class="hidden">' . (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '') . '</div>';
3046
3047 $out .= '<!-- Add js code to open dialog popup on dialog -->';
3048 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">
3049 jQuery(document).ready(function () {
3050 jQuery(".button_' . $name . '").click(function () {
3051 console.log(\'Open popup with jQuery(...).dialog() on URL ' . dol_escape_js($urltoopen) . '\');
3052 var $tmpdialog = $(\'#idfordialog' . $name . '\');
3053 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog' . $name . '" style="border: 0px;" src="' . $urltoopen . '" width="100%" height="98%"></iframe>\');
3054 $tmpdialog.dialog({
3055 autoOpen: false,
3056 modal: true,
3057 height: (window.innerHeight - 150),
3058 width: \'80%\',
3059 title: \'' . dol_escape_js($label) . '\',
3060 open: function (event, ui) {
3061 console.log("open popup name=' . $name . '");
3062 },
3063 close: function (event, ui) {
3064 console.log("Popup is closed, run jsonclose = ' . $jsonclose . '");
3065 ' . (empty($jsonclose) ? '' : $jsonclose . ';') . '
3066 }
3067 });
3068
3069 $tmpdialog.dialog(\'open\');
3070 return false;
3071 });
3072 });
3073 </script>';
3074 }
3075 return $out;
3076}
3077
3094function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
3095{
3096 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
3097}
3098
3116function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
3117{
3118 global $conf, $langs, $hookmanager;
3119
3120 // Show title
3121 $showtitle = 1;
3122 if (!empty($conf->dol_optimize_smallscreen)) {
3123 $showtitle = 0;
3124 }
3125
3126 $out = "\n" . '<!-- dol_fiche_head - dol_get_fiche_head -->';
3127
3128 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3129 $out .= '<div class="tabs' . ($picto ? '' : ' nopaddingleft') . '" data-role="controlgroup" data-type="horizontal">' . "\n";
3130 }
3131
3132 // Show right part
3133 if ($morehtmlright) {
3134 $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.
3135 }
3136
3137 // Show tabs
3138
3139 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
3140 $maxkey = -1;
3141 if (is_array($links) && !empty($links)) {
3142 $keys = array_keys($links);
3143 if (count($keys)) {
3144 $maxkey = max($keys);
3145 }
3146 }
3147
3148 // Show tabs
3149 // if =0 we don't use the feature
3150 if (empty($limittoshow)) {
3151 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
3152 }
3153 if (!empty($conf->dol_optimize_smallscreen)) {
3154 $limittoshow = 2;
3155 }
3156
3157 $displaytab = 0;
3158 $nbintab = 0;
3159 $popuptab = 0;
3160 $outmore = '';
3161 for ($i = 0; $i <= $maxkey; $i++) {
3162 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3163 // If active tab is already present
3164 if ($i >= $limittoshow) {
3165 $limittoshow--;
3166 }
3167 }
3168 }
3169
3170 for ($i = 0; $i <= $maxkey; $i++) {
3171 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3172 $isactive = true;
3173 } else {
3174 $isactive = false;
3175 }
3176
3177 if ($i < $limittoshow || $isactive) {
3178 // Output entry with a visible tab
3179 $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])) . ' -->';
3180
3181 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3182 if (!empty($links[$i][0])) {
3183 $out .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3184 } else {
3185 $out .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3186 }
3187 } elseif (!empty($links[$i][1])) {
3188 //print "x $i $active ".$links[$i][2]." z";
3189 $out .= '<div class="tab tab' . ($isactive ? 'active' : 'unactive') . '" style="margin: 0 !important">';
3190
3191 if (!empty($links[$i][0])) {
3192 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
3193 $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) . '">';
3194 }
3195
3196 if ($displaytab == 0 && $picto) {
3197 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
3198 }
3199
3200 $out .= $links[$i][1];
3201 if (!empty($links[$i][0])) {
3202 $out .= '</a>' . "\n";
3203 }
3204 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
3205 $out .= '</div>';
3206 }
3207
3208 $out .= '</div>';
3209 } else {
3210 // Add entry into the combo popup with the other tabs
3211 if (!$popuptab) {
3212 $popuptab = 1;
3213 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
3214 }
3215 $outmore_content = '';
3216
3217 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3218 if (!empty($links[$i][0])) {
3219 $outmore_content .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3220 } else {
3221 $outmore_content .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3222 }
3223 } elseif (!empty($links[$i][1])) {
3224 $outmore_content .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="wordwrap inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
3225 $outmore_content .= 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.
3226 $outmore_content .= '</a>' . "\n";
3227 }
3228 if ($outmore_content !== '') {
3229 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
3230 }
3231
3232 $nbintab++;
3233 }
3234
3235 $displaytab = $i + 1;
3236 }
3237 if ($popuptab) {
3238 $outmore .= '</div>';
3239 }
3240
3241 if ($popuptab) { // If there is some tabs not shown
3242 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3243 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3244 $widthofpopup = 240;
3245
3246 $tabsname = $moretabssuffix;
3247 if (empty($tabsname)) {
3248 $tabsname = str_replace("@", "", $picto);
3249 }
3250 $out .= '<div id="moretabs' . $tabsname . '" class="inline-block tabsElem valignmiddle">';
3251 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3252 $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".
3253 }
3254 $out .= '<div id="moretabsList' . $tabsname . '" style="width: ' . $widthofpopup . 'px; position: absolute; ' . $left . ': -999em; text-align: ' . $left . '; margin:0px; padding:2px; z-index:10;">';
3255 $out .= $outmore;
3256 $out .= '</div>';
3257 $out .= '<div></div>';
3258 $out .= "</div>\n";
3259
3260 $out .= '<script nonce="' . getNonce() . '">';
3261 $out .= "$('#moretabs" . $tabsname . "').mouseenter( function() {
3262 var x = this.offsetLeft, y = this.offsetTop;
3263 console.log('mouseenter " . $left . " x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3264 if ((window.innerWidth - x) < " . ($widthofpopup + 10) . ") {
3265 $('#moretabsList" . $tabsname . "').css('" . $right . "','8px');
3266 }
3267 $('#moretabsList" . $tabsname . "').css('" . $left . "','auto');
3268 });
3269 ";
3270 $out .= "$('#moretabs" . $tabsname . "').mouseleave( function() { console.log('mouseleave " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','-999em');});";
3271 $out .= "</script>";
3272 }
3273
3274 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3275 $out .= "</div>\n";
3276 }
3277
3278 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3279 $out .= "\n" . '<div id="dragDropAreaTabBar" class="tabBar' . ($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '') . ($notab == -4 ? '' : ' tabBarWithBottom'))));
3280 $out .= ($morecssdiv ? ' ' . $morecssdiv : '');
3281 $out .= '">' . "\n";
3282 }
3283 if (!empty($dragdropfile)) {
3284 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3285 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3286 }
3287 $parameters = array('tabname' => $active, 'out' => $out);
3288 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3289 if ($reshook > 0) {
3290 $out = $hookmanager->resPrint;
3291 }
3292
3293 return $out;
3294}
3295
3303function dol_fiche_end($notab = 0)
3304{
3305 print dol_get_fiche_end($notab);
3306}
3307
3314function dol_get_fiche_end($notab = 0)
3315{
3316 if (!$notab || $notab == -1) {
3317 return "\n</div>\n";
3318 } else {
3319 return '';
3320 }
3321}
3322
3342function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3343{
3344 global $conf, $form, $user, $langs, $hookmanager, $action;
3345
3346 $error = 0;
3347
3348 $maxvisiblephotos = 1;
3349 $showimage = 1;
3350 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3351 // @phan-suppress-next-line PhanUndeclaredMethod
3352 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3353 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3354 $showbarcode = 0;
3355 }
3356 $modulepart = 'unknown';
3357
3358 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3359 $modulepart = $object->element;
3360 } elseif ($object->element == 'member') {
3361 $modulepart = 'memberphoto';
3362 } elseif ($object->element == 'user') {
3363 $modulepart = 'userphoto';
3364 }
3365
3366 if (class_exists("Imagick")) {
3367 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3368 $modulepart = $object->element;
3369 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3370 $modulepart = 'ficheinter';
3371 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3372 $modulepart = 'contract';
3373 } elseif ($object->element == 'order_supplier') {
3374 $modulepart = 'supplier_order';
3375 } elseif ($object->element == 'invoice_supplier') {
3376 $modulepart = 'supplier_invoice';
3377 }
3378 }
3379
3380 if ($object->element == 'product') {
3382 '@phan-var-force Product $object';
3383 $width = 80;
3384 $cssclass = 'photowithmargin photoref';
3385 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3386 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3387 if ($conf->browser->layout == 'phone') {
3388 $maxvisiblephotos = 1;
3389 }
3390 if ($showimage) {
3391 $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>';
3392 } else {
3393 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3394 $nophoto = '';
3395 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3396 } else { // Show no photo link
3397 $nophoto = '/public/theme/common/nophoto.png';
3398 $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>';
3399 }
3400 }
3401 } elseif ($object->element == 'category') {
3403 '@phan-var-force Categorie $object';
3404 $width = 80;
3405 $cssclass = 'photowithmargin photoref';
3406 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3407 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3408 if ($conf->browser->layout == 'phone') {
3409 $maxvisiblephotos = 1;
3410 }
3411 if ($showimage) {
3412 $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>';
3413 } else {
3414 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3415 $nophoto = '';
3416 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3417 } else { // Show no photo link
3418 $nophoto = '/public/theme/common/nophoto.png';
3419 $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>';
3420 }
3421 }
3422 } elseif ($object->element == 'bom') {
3424 '@phan-var-force Bom $object';
3425 $width = 80;
3426 $cssclass = 'photowithmargin photoref';
3427 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3428 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3429 if ($conf->browser->layout == 'phone') {
3430 $maxvisiblephotos = 1;
3431 }
3432 if ($showimage) {
3433 $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>';
3434 } else {
3435 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3436 $nophoto = '';
3437 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3438 } else { // Show no photo link
3439 $nophoto = '/public/theme/common/nophoto.png';
3440 $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>';
3441 }
3442 }
3443 } elseif ($object->element == 'ticket') {
3444 $width = 80;
3445 $cssclass = 'photoref';
3447 '@phan-var-force Ticket $object';
3448 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity] . '/' . $object->ref);
3449 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3450 if ($conf->browser->layout == 'phone') {
3451 $maxvisiblephotos = 1;
3452 }
3453
3454 if ($showimage) {
3455 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3456 if ($object->nbphoto > 0) {
3457 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $showphoto . '</div>';
3458 } else {
3459 $showimage = 0;
3460 }
3461 }
3462 if (!$showimage) {
3463 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3464 $nophoto = '';
3465 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3466 } else { // Show no photo link
3467 $nophoto = img_picto('No photo', 'object_ticket');
3468 $morehtmlleft .= '<!-- No photo to show -->';
3469 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3470 $morehtmlleft .= $nophoto;
3471 $morehtmlleft .= '</div></div>';
3472 }
3473 }
3474 } else {
3475 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3476 $phototoshow = '';
3477 // Check if a preview file is available
3478 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3479 $objectref = dol_sanitizeFileName($object->ref);
3480 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity]) . "/";
3481 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3482 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3483 $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
3484 } else {
3485 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3486 }
3487 if (empty($subdir)) {
3488 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3489 }
3490
3491 $filepath = $dir_output . $subdir . "/";
3492
3493 $filepdf = $filepath . $objectref . ".pdf";
3494 $relativepath = $subdir . '/' . $objectref . '.pdf';
3495
3496 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3497 $fileimage = $filepdf . '_preview.png';
3498 $relativepathimage = $relativepath . '_preview.png';
3499
3500 $pdfexists = file_exists($filepdf);
3501
3502 // If PDF file exists
3503 if ($pdfexists) {
3504 // Conversion du PDF en image png si fichier png non existent
3505 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3506 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3507 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3508 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3509 if ($ret < 0) {
3510 $error++;
3511 }
3512 }
3513 }
3514 }
3515
3516 if ($pdfexists && !$error) {
3517 $heightforphotref = 80;
3518 if (!empty($conf->dol_optimize_smallscreen)) {
3519 $heightforphotref = 60;
3520 }
3521 // If the preview file is found
3522 if (file_exists($fileimage)) {
3523 $phototoshow = '<div class="photoref">';
3524 $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithborder" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($relativepathimage) . '">';
3525 $phototoshow .= '</div>';
3526 }
3527 }
3528 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3529 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3530 }
3531
3532 if ($phototoshow) {
3533 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3534 $morehtmlleft .= $phototoshow;
3535 $morehtmlleft .= '</div>';
3536 }
3537 }
3538
3539 if (empty($phototoshow)) { // Show No photo link (picto of object)
3540 if ($object->element == 'action') {
3541 $width = 80;
3542 $cssclass = 'photorefcenter';
3543 $nophoto = img_picto('No photo', 'title_agenda');
3544 } else {
3545 $width = 14;
3546 $cssclass = 'photorefcenter';
3547 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3548 $prefix = 'object_';
3549 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3550 $picto = 'project'; // instead of projectpub
3551 }
3552 if (strpos($picto, 'fontawesome_') !== false) {
3553 $prefix = '';
3554 }
3555 $nophoto = img_picto('No photo', $prefix . $picto);
3556 }
3557 $morehtmlleft .= '<!-- No photo to show -->';
3558 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3559 $morehtmlleft .= $nophoto;
3560 $morehtmlleft .= '</div></div>';
3561 }
3562 }
3563
3564 // Show barcode
3565 if ($showbarcode) {
3566 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $form->showbarcode($object, 100, 'photoref valignmiddle') . '</div>';
3567 }
3568
3569 if ($object->element == 'societe') {
3571 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3572 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3573 } else {
3574 $morehtmlstatus .= $object->getLibStatut(6);
3575 }
3576 } elseif ($object->element == 'product') {
3578 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3579 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3580 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3581 } else {
3582 $morehtmlstatus .= '<span class="statusrefsell">' . $object->getLibStatut(6, 0) . '</span>';
3583 }
3584 $morehtmlstatus .= ' &nbsp; ';
3585 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3586 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3587 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'status_buy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3588 } else {
3589 $morehtmlstatus .= '<span class="statusrefbuy">' . $object->getLibStatut(6, 1) . '</span>';
3590 }
3591 } elseif (in_array($object->element, array('salary'))) {
3593 '@phan-var-force Salary $object';
3594 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3595 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3596 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3597 }
3598 $morehtmlstatus .= $tmptxt;
3599 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) {
3601 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3602 if (!isset($object->alreadypaid)) {
3603 $object->totalpaid = $object->getSommePaiement(0);
3604 $object->totalcreditnotes = $object->getSumCreditNotesUsed(0);
3605 $object->totaldeposits = $object->getSumDepositsUsed(0);
3606 $object->alreadypaid = $object->totalpaid + $object->totalcreditnotes + $object->totaldeposits;
3607 }
3608 $tmptxt = $object->getLibStatut(6, (float) $object->alreadypaid);
3609 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3610 $tmptxt = $object->getLibStatut(5, (float) $object->alreadypaid);
3611 }
3612 $morehtmlstatus .= $tmptxt;
3613 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid like for invoices
3615 '@phan-var-force ChargeSociales|Loan|Tva $object';
3616 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3617 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3618 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3619 }
3620 $morehtmlstatus .= $tmptxt;
3621 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3623 if ($object->status == 0) {
3624 $morehtmlstatus .= $object->getLibStatut(5);
3625 } else {
3626 $morehtmlstatus .= $object->getLibStatut(4);
3627 }
3628 } elseif ($object->element == 'facturerec') {
3630 '@phan-var-force FactureRec $object';
3631 if ($object->frequency == 0) {
3632 $morehtmlstatus .= $object->getLibStatut(2);
3633 } else {
3634 $morehtmlstatus .= $object->getLibStatut(5);
3635 }
3636 } elseif ($object->element == 'project_task') {
3638 $tmptxt = $object->getLibStatut(4);
3639 $morehtmlstatus .= $tmptxt;
3640 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3641 $tmptxt = $object->getLibStatut(6);
3642 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3643 $tmptxt = $object->getLibStatut(5);
3644 }
3645 $morehtmlstatus .= $tmptxt;
3646 }
3647
3648 // Say if object was dispatched/transferred "into accountancy"
3649 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3650 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3651 if (method_exists($object, 'getVentilExportCompta')) {
3652 $accounted = $object->getVentilExportCompta(1);
3653 $langs->load("accountancy");
3654 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">' . ($accounted > 0 ? '<a href="' . DOL_URL_ROOT . '/accountancy/bookkeeping/list.php?search_mvt_num=' . ((int) $accounted) . '">' . $langs->trans("Accounted") . '</a>' : $langs->trans("NotYetAccounted")) . '</span>';
3655 }
3656 }
3657
3658 // Add alias for thirdparty
3659 if (!empty($object->name_alias)) {
3661 '@phan-var-force Societe $object';
3662 $morehtmlref .= '<div class="refidno opacitymedium">' . dol_escape_htmltag($object->name_alias) . '</div>';
3663 }
3664
3665 // Add label
3666 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3668 if (!empty($object->label)) {
3669 $morehtmlref .= '<div class="refidno banner-object-label">' . $object->label . '</div>';
3670 }
3671 }
3672 // Show address and email
3673 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3674 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3675 if ($moreaddress) {
3676 $morehtmlref .= '<div class="refidno refaddress">';
3677 $morehtmlref .= $moreaddress;
3678 $morehtmlref .= '</div>';
3679 }
3680 }
3681 if (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') == '1' || preg_match('/' . preg_quote($object->element, '/') . '/i', getDolGlobalString('MAIN_SHOW_TECHNICAL_ID'))) && !empty($object->id)) {
3682 $morehtmlref .= '<div style="clear: both;"></div>';
3683 $morehtmlref .= '<div class="refidno opacitymedium">';
3684 $morehtmlref .= $langs->trans("TechnicalID") . ': ' . ((int) $object->id);
3685 $morehtmlref .= '</div>';
3686 }
3687
3688 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3689 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3690 if ($reshook < 0) {
3691 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3692 } elseif (empty($reshook)) {
3693 $morehtmlref .= $hookmanager->resPrint;
3694 } elseif ($reshook > 0) {
3695 $morehtmlref = $hookmanager->resPrint;
3696 }
3697
3698 // $morehtml is the right part (link "Back to list")
3699 // $morehtmlref is the part after the ref
3700 // $morehtmlleft is the picto or photo of banner
3701 // $morehtmlstatus is part under the status
3702 // $morehtmlright is part of htmlright
3703
3704 print '<div class="' . ($onlybanner ? 'arearefnobottom ' : 'arearef ') . 'heightref valignmiddle centpercent object-banner-tab-container" data-module-part="'.dolPrintHTMLForAttribute($modulepart).'">';
3705 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3706 print '</div>';
3707 print '<div class="underrefbanner clearboth"></div>';
3708}
3709
3719function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3720{
3721 global $langs;
3722 $ret = '';
3723 if ($fieldrequired) {
3724 $ret .= '<span class="fieldrequired">';
3725 }
3726 $ret .= '<label for="' . $fieldkey . '">';
3727 $ret .= $langs->trans($langkey);
3728 $ret .= '</label>';
3729 if ($fieldrequired) {
3730 $ret .= '</span>';
3731 }
3732 return $ret;
3733}
3734
3748function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3749{
3750 global $langs, $hookmanager;
3751
3752 $ret = '';
3753 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3754
3755 // See format of addresses on https://en.wikipedia.org/wiki/Address
3756 // Address
3757 if (empty($mode)) {
3758 $ret .= (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3759 }
3760 // Zip/Town/State
3761 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3762 // US: title firstname name \n address lines \n town, state, zip \n country
3763 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3764 $ret .= (($ret && $town) ? $sep : '') . $town;
3765
3766 if (!empty($object->state)) {
3767 $ret .= ($ret ? ($town ? ", " : $sep) : '') . $object->state;
3768 }
3769 if (!empty($object->zip)) {
3770 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '') . $object->zip;
3771 }
3772 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3773 // UK: title firstname name \n address lines \n town state \n zip \n country
3774 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3775 $ret .= ($ret ? $sep : '') . $town;
3776 if (!empty($object->state)) {
3777 $ret .= ($ret ? ", " : '') . $object->state;
3778 }
3779 if (!empty($object->zip)) {
3780 $ret .= ($ret ? $sep : '') . $object->zip;
3781 }
3782 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3783 // ES: title firstname name \n address lines \n zip town \n state \n country
3784 $ret .= ($ret ? $sep : '') . $object->zip;
3785 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3786 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3787 if (!empty($object->state)) {
3788 $ret .= $sep . $object->state;
3789 }
3790 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3791 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3792 // See https://www.sljfaq.org/afaq/addresses.html
3793 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3794 $ret .= ($ret ? $sep : '') . ($object->state ? $object->state . ', ' : '') . $town . ($object->zip ? ' ' : '') . $object->zip;
3795 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3796 // IT: title firstname name\n address lines \n zip town state_code \n country
3797 $ret .= ($ret ? $sep : '') . $object->zip;
3798 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3799 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3800 $ret .= (empty($object->state_code) ? '' : (' ' . $object->state_code));
3801 } else {
3802 // Other: title firstname name \n address lines \n zip town[, state] \n country
3803 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3804 $ret .= !empty($object->zip) ? (($ret ? $sep : '') . $object->zip) : '';
3805 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')) . $town) : '');
3806 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3807 $ret .= ($ret ? ", " : '') . $object->state;
3808 }
3809 }
3810
3811 if (!is_object($outputlangs)) {
3812 $outputlangs = $langs;
3813 }
3814 if ($withcountry) {
3815 $langs->load("dict");
3816 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '') . $outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country" . $object->country_code)));
3817 }
3818 if ($hookmanager) {
3819 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs, 'mode' => $mode, 'extralangcode' => $extralangcode);
3820 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3821 if ($reshook > 0) {
3822 $ret = '';
3823 }
3824 $ret .= $hookmanager->resPrint;
3825 }
3826
3827 return $ret;
3828}
3829
3830
3831
3841function dol_strftime($fmt, $ts = false, $is_gmt = false)
3842{
3843 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3844 return dol_print_date($ts, $fmt, $is_gmt);
3845 } else {
3846 return 'Error date outside supported range';
3847 }
3848}
3849
3872function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false, $decorate = 0)
3873{
3874 global $conf, $langs;
3875
3876 // If date undefined or "", we return ""
3877 if (dol_strlen((string) $time) == 0) {
3878 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3879 }
3880
3881 if ($tzoutput === 'auto') {
3882 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3883 }
3884
3885 // Clean parameters
3886 $to_gmt = false; // false if we want date in server timezone, true if we want to add offset
3887 $offsettz = $offsetdst = 0;
3888 if ($tzoutput) {
3889 $to_gmt = true; // For backward compatibility
3890 if (is_string($tzoutput)) {
3891 if ($tzoutput == 'tzserver') {
3892 $to_gmt = false;
3893 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3894 // @phan-suppress-next-line PhanPluginRedundantAssignment
3895 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3896 // @phan-suppress-next-line PhanPluginRedundantAssignment
3897 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3898 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3899 $to_gmt = true;
3900 // if no session (by example in cron) may use MAIN_DOLIBARR_USER_TIMEZONE instead UTC
3901 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? getDolGlobalString('MAIN_DOLIBARR_USER_TIMEZONE', 'UTC') : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3902
3903 if (class_exists('DateTimeZone')) {
3904 try {
3905 $user_date_tz = new DateTimeZone($offsettzstring);
3906 } catch (Exception $e) {
3907 // Bad value for $offsettzstring
3908 dol_syslog("DateInvalidTimeZoneException for timezone string '".$offsettzstring."'. Falling back to UTC.", LOG_ERR);
3909 $user_date_tz = new DateTimeZone('UTC'); // Force valid timezone as UTC
3910 }
3911 $user_dt = new DateTime();
3912 $user_dt->setTimezone($user_date_tz);
3913 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3914 $offsettz = $user_dt->getOffset(); // should include dst ?
3915 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3916 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3917 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3918 }
3919 }
3920 }
3921 }
3922 if (!is_object($outputlangs)) {
3923 $outputlangs = $langs;
3924 }
3925 if (!$format) {
3926 $format = 'daytextshort';
3927 }
3928
3929 // Do we have to reduce the length of date (year on 2 chars) to save space.
3930 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3931 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3932 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3933 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3934 if ($formatwithoutreduce != $format) {
3935 $format = $formatwithoutreduce;
3936 $reduceformat = 1;
3937 } // so format 'dayreduceformat' is processed like day
3938
3939 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3940 // TODO Add format daysmallyear and dayhoursmallyear
3941 if ($format == 'day') {
3942 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3943 } elseif ($format == 'hour') {
3944 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3945 } elseif ($format == 'hourduration') {
3946 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3947 } elseif ($format == 'daytext') {
3948 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3949 } elseif ($format == 'daytextshort') {
3950 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3951 } elseif ($format == 'dayhour') {
3952 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3953 } elseif ($format == 'dayhoursec') {
3954 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3955 } elseif ($format == 'dayhourtext') {
3956 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3957 } elseif ($format == 'dayhourtextshort') {
3958 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3959 } elseif ($format == 'dayhourlog') {
3960 // Format not sensitive to language
3961 $format = '%Y%m%d%H%M%S';
3962 } elseif ($format == 'dayhourlogsmall') {
3963 // Format not sensitive to language
3964 $format = '%y%m%d%H%M';
3965 } elseif ($format == 'dayhourldap') {
3966 $format = '%Y%m%d%H%M%SZ';
3967 } elseif ($format == 'dayhourxcard') {
3968 $format = '%Y%m%dT%H%M%SZ';
3969 } elseif ($format == 'dayxcard') {
3970 $format = '%Y%m%d';
3971 } elseif ($format == 'dayrfc') {
3972 $format = '%Y-%m-%d'; // DATE_RFC3339
3973 } elseif ($format == 'dayhourrfc') {
3974 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3975 } elseif ($format == 'standard') {
3976 $format = '%Y-%m-%d %H:%M:%S';
3977 }
3978
3979 if ($reduceformat) {
3980 $format = str_replace('%Y', '%y', $format);
3981 $format = str_replace('yyyy', 'yy', $format);
3982 }
3983
3984 // Clean format
3985 if (preg_match('/%b/i', $format)) { // There is some text to translate
3986 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3987 $format = str_replace('%b', '__b__', $format);
3988 $format = str_replace('%B', '__B__', $format);
3989 }
3990 if (preg_match('/%a/i', $format)) { // There is some text to translate
3991 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3992 $format = str_replace('%a', '__a__', $format);
3993 $format = str_replace('%A', '__A__', $format);
3994 }
3995
3996 // Analyze date
3997 $reg = array();
3998 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
3999 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString());
4000 return '';
4001 } 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
4002 // This part of code should not be used anymore.
4003 dol_syslog("Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString(), LOG_WARNING);
4004 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
4005 $syear = (!empty($reg[1]) ? $reg[1] : '');
4006 $smonth = (!empty($reg[2]) ? $reg[2] : '');
4007 $sday = (!empty($reg[3]) ? $reg[3] : '');
4008 $shour = (!empty($reg[4]) ? $reg[4] : '');
4009 $smin = (!empty($reg[5]) ? $reg[5] : '');
4010 $ssec = (!empty($reg[6]) ? $reg[6] : '');
4011
4012 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
4013
4014 if ($to_gmt) {
4015 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4016 } else {
4017 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4018 }
4019 $dtts = new DateTime();
4020 $dtts->setTimestamp($time);
4021 $dtts->setTimezone($tzo);
4022 $newformat = str_replace(
4023 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4024 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4025 $format
4026 );
4027 $ret = $dtts->format($newformat);
4028 $ret = str_replace(
4029 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4030 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4031 $ret
4032 );
4033 } else {
4034 // Date is a timestamps
4035 if ($time < 100000000000) { // Protection against bad date values
4036 $dtts = new DateTime();
4037 //var_dump($tzoutput.' '.$offsettzstring.' '.$offsettz.$offsetdst.' x '.$to_gmt);
4038 if ($to_gmt) { // to_gmt means "not in php server timezone" (if tzoutput = 'gmt', offsets should be 0 but if = 'tzuser...', offsets may be defined
4039 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4040
4041 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4042 $dtts->setTimezone($tzo); // important: must be before the setTimestamp
4043 $dtts->setTimestamp($timetouse);
4044 } else {
4045 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4046
4047 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4048 $dtts->setTimestamp($timetouse); // TODO May be we can invert setTimestamp and setTimezone
4049 $dtts->setTimezone($tzo);
4050 }
4051
4052 $newformat = str_replace(
4053 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4054 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4055 $format
4056 );
4057
4058 $ret = $dtts->format($newformat);
4059 //var_dump($timetouse, $offsettz, $offsetdst, $tzo, $newformat, $ret);
4060 $ret = str_replace(
4061 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4062 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4063 $ret
4064 );
4065 } else {
4066 $ret = 'Bad value ' . $time . ' for date';
4067 }
4068 }
4069
4070 if (preg_match('/__b__/i', $format)) {
4071 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4072
4073 if ($to_gmt) {
4074 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4075 } else {
4076 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4077 }
4078 $dtts = new DateTime();
4079 $dtts->setTimestamp($timetouse);
4080 $dtts->setTimezone($tzo);
4081 $month = (int) $dtts->format("m");
4082 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
4083 if ($encodetooutput) {
4084 $monthtext = $outputlangs->transnoentities('Month' . $month);
4085 $monthtextshort = $outputlangs->transnoentities('MonthShort' . $month);
4086 } else {
4087 $monthtext = $outputlangs->transnoentitiesnoconv('Month' . $month);
4088 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort' . $month);
4089 }
4090 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
4091 $ret = str_replace('__b__', $monthtextshort, $ret);
4092 $ret = str_replace('__B__', $monthtext, $ret);
4093 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
4094 //return $ret;
4095 }
4096 if (preg_match('/__a__/i', $format)) {
4097 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
4098 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
4099
4100 if ($to_gmt) {
4101 $tzo = new DateTimeZone('UTC');
4102 } else {
4103 $tzo = new DateTimeZone(date_default_timezone_get());
4104 }
4105 $dtts = new DateTime();
4106 $dtts->setTimestamp($timetouse);
4107 $dtts->setTimezone($tzo);
4108 $w = $dtts->format("w");
4109 $dayweek = $outputlangs->transnoentitiesnoconv('Day' . $w);
4110
4111 $ret = str_replace('__A__', $dayweek, $ret);
4112 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
4113 }
4114
4115 if ($decorate) {
4116 $ret = preg_replace('/(\d\d:\d\d [AP]M)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4117 $ret = preg_replace('/(\d\d:\d\d)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4118 }
4119
4120 return $ret;
4121}
4122
4123
4144function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
4145{
4146 if ($timestamp === '') {
4147 return array();
4148 }
4149
4150 $datetimeobj = new DateTime();
4151 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
4152 if ($forcetimezone) {
4153 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
4154 }
4155 $arrayinfo = array(
4156 'year' => ((int) date_format($datetimeobj, 'Y')),
4157 'mon' => ((int) date_format($datetimeobj, 'm')),
4158 'mday' => ((int) date_format($datetimeobj, 'd')),
4159 'wday' => ((int) date_format($datetimeobj, 'w')),
4160 'yday' => ((int) date_format($datetimeobj, 'z')),
4161 'hours' => ((int) date_format($datetimeobj, 'H')),
4162 'minutes' => ((int) date_format($datetimeobj, 'i')),
4163 'seconds' => ((int) date_format($datetimeobj, 's')),
4164 '0' => $timestamp
4165 );
4166
4167 return $arrayinfo;
4168}
4169
4191function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
4192{
4193 global $conf;
4194 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
4195
4196 if ($gm === 'auto') {
4197 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
4198 }
4199 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
4200
4201 // Clean parameters
4202 if ($hour == -1 || empty($hour)) {
4203 $hour = 0;
4204 }
4205 if ($minute == -1 || empty($minute)) {
4206 $minute = 0;
4207 }
4208 if ($second == -1 || empty($second)) {
4209 $second = 0;
4210 }
4211
4212 // Check parameters
4213 if ($check) {
4214 if (!$month || !$day) {
4215 return '';
4216 }
4217 if ($day > 31) {
4218 return '';
4219 }
4220 if ($month > 12) {
4221 return '';
4222 }
4223 if ($hour < 0 || $hour > 24) {
4224 return '';
4225 }
4226 if ($minute < 0 || $minute > 60) {
4227 return '';
4228 }
4229 if ($second < 0 || $second > 60) {
4230 return '';
4231 }
4232 }
4233
4234 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
4235 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
4236 $localtz = new DateTimeZone($default_timezone);
4237 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
4238 // We use dol_tz_string first because it is more reliable.
4239 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
4240 try {
4241 $localtz = new DateTimeZone($default_timezone);
4242 } catch (Exception $e) {
4243 dol_syslog("Warning dol_tz_string contains an invalid value " . json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
4244 $default_timezone = @date_default_timezone_get();
4245 }
4246 } elseif (strrpos($gm, "tz,") !== false) {
4247 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
4248 try {
4249 $localtz = new DateTimeZone($timezone);
4250 } catch (Exception $e) {
4251 dol_syslog("Warning passed timezone contains an invalid value " . $timezone, LOG_WARNING);
4252 }
4253 }
4254
4255 if (empty($localtz)) {
4256 $localtz = new DateTimeZone('UTC');
4257 }
4258 $dt = new DateTime('now', $localtz);
4259 $dt->setDate((int) $year, (int) $month, (int) $day);
4260 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4261 $date = $dt->getTimestamp(); // should include daylight saving time
4262
4263 return $date;
4264}
4265
4266
4277function dol_now($mode = 'gmt')
4278{
4279 $ret = 0;
4280
4281 if ($mode === 'auto') {
4282 $mode = 'gmt';
4283 }
4284
4285 if ($mode == 'gmt') {
4286 $ret = time(); // Time for now at greenwich.
4287 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4288 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
4289 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4290 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4291 // } elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4292 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4293 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4294 // $ret=dol_now('gmt')+($tzsecond*3600);
4295 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4296 // Time for now with user timezone added
4297 // print 'time: '.time();
4298 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4299 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4300 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4301 }
4302
4303 return $ret;
4304}
4305
4306
4315function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4316{
4317 global $conf, $langs;
4318 $level = 1024;
4319
4320 if (!empty($conf->dol_optimize_smallscreen)) {
4321 $shortunit = 1;
4322 }
4323
4324 // Set value text
4325 if (empty($shortvalue) || $size < ($level * 10)) {
4326 $ret = $size;
4327 $textunitshort = $langs->trans("b");
4328 $textunitlong = $langs->trans("Bytes");
4329 } else {
4330 $ret = round($size / $level, 0);
4331 $textunitshort = $langs->trans("Kb");
4332 $textunitlong = $langs->trans("KiloBytes");
4333 }
4334 // Use long or short text unit
4335 if (empty($shortunit)) {
4336 $ret .= ' ' . $textunitlong;
4337 } else {
4338 $ret .= ' ' . $textunitshort;
4339 }
4340
4341 return $ret;
4342}
4343
4354function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4355{
4356 global $langs;
4357
4358 if (empty($url)) {
4359 return '';
4360 }
4361
4362 $linkstart = '<a href="';
4363 if (!preg_match('/^http/i', $url)) {
4364 $linkstart .= 'http://';
4365 }
4366 $linkstart .= $url;
4367 $linkstart .= '"';
4368 if ($target) {
4369 $linkstart .= ' target="' . $target . '"';
4370 }
4371 $linkstart .= ' title="' . $langs->trans("URL") . ': ' . $url . '"';
4372 $linkstart .= '>';
4373
4374 $link = '';
4375 if (!preg_match('/^http/i', $url)) {
4376 $link .= 'http://';
4377 }
4378 $link .= dol_trunc($url, $max);
4379
4380 $linkend = '</a>';
4381
4382 if ($morecss == 'float') { // deprecated
4383 return '<div class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '') . $link . '</div>';
4384 } else {
4385 return $linkstart . '<span class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '') . $link . '</span>' . $linkend;
4386 }
4387}
4388
4402function dol_print_email($email, $contactid = 0, $socid = 0, $addlink = 0, $max = 0, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
4403{
4404 global $user, $langs, $hookmanager;
4405
4406 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4407 //$showinvalid = 1; $email = 'rrrrr';
4408
4409 $newemail = dol_escape_htmltag($email);
4410
4411 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4412 $withpicto = 0;
4413 }
4414
4415 if (empty($email)) {
4416 return '&nbsp;';
4417 }
4418
4419 if ($addlink == 1) {
4420 $newemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="';
4421 if (!preg_match('/^mailto:/i', $email)) {
4422 $newemail .= 'mailto:';
4423 }
4424 $newemail .= $email;
4425 $newemail .= '" target="_blank">';
4426
4427 $newemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4428
4429 if ($max > 0) {
4430 $newemail .= dol_trunc($email, $max);
4431 } else {
4432 $newemail .= $email;
4433 }
4434 $newemail .= '</a>';
4435
4436 if ($showinvalid) {
4437 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4438 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4439 $emailonly = CMailFile::getValidAddress($email, 2);
4440 if (!isValidEmail($emailonly)) {
4441 $langs->load("errors");
4442 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $emailonly), '', 'paddingrightonly');
4443 } elseif (!isValidMailDomain($emailonly)) {
4444 $langs->load("errors");
4445 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly), '', 'paddingrightonly');
4446 }
4447 }
4448
4449 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4450 $type = 'AC_EMAIL';
4451 $linktoaddaction = '';
4452 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4453 $linktoaddaction = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode=' . urlencode($type) . '&amp;contactid=' . ((int) $contactid) . '&amp;socid=' . ((int) $socid) . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
4454 }
4455 if ($linktoaddaction) {
4456 $newemail = '<div>' . $newemail . ' ' . $linktoaddaction . '</div>';
4457 }
4458 }
4459 } elseif ($addlink === 'thirdparty') {
4460 $tmpnewemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="' . DOL_URL_ROOT . '/societe/card.php?socid=' . $socid . '&action=presend&mode=init#formmailbeforetitle">';
4461 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4462 if ($withpicto == 1) {
4463 $tmpnewemail .= $newemail;
4464 }
4465 $tmpnewemail .= '</a>';
4466
4467 $newemail = $tmpnewemail;
4468 } else {
4469 $newemail = ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '') . $newemail;
4470
4471 if ($showinvalid) {
4472 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4473 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4474 $emailonly = CMailFile::getValidAddress($email, 2);
4475 if (!isValidEmail($emailonly)) {
4476 $langs->load("errors");
4477 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $email));
4478 } elseif (!isValidMailDomain($emailonly)) {
4479 $langs->load("errors");
4480 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly));
4481 }
4482 }
4483 }
4484
4485 //$rep = '<div class="nospan" style="margin-right: 10px">';
4486 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4487 //$rep .= '</div>';
4488 $rep = $newemail;
4489
4490 if ($hookmanager) {
4491 $parameters = array('cid' => $contactid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4492
4493 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4494 if ($reshook > 0) {
4495 $rep = '';
4496 }
4497 $rep .= $hookmanager->resPrint;
4498 }
4499
4500 return $rep;
4501}
4502
4508function getArrayOfSocialNetworks()
4509{
4510 global $db;
4511
4512 $socialnetworks = array();
4513 // Enable caching of array
4514 require_once DOL_DOCUMENT_ROOT . '/core/lib/memory.lib.php';
4515 $cachekey = dol_sanitizeKeyCode(str_replace(',', '_', 'socialnetworks_'.getEntity('c_socialnetworks')));
4516 $dataretrieved = dol_getcache($cachekey);
4517
4518 if (!is_null($dataretrieved)) {
4519 $socialnetworks = $dataretrieved;
4520 } else {
4521 $sql = "SELECT rowid, code, label, url, icon, active FROM " . MAIN_DB_PREFIX . "c_socialnetworks";
4522 $sql .= " WHERE entity IN (" . getEntity('c_socialnetworks').")";
4523
4524 $resql = $db->query($sql);
4525 if ($resql) {
4526 while ($obj = $db->fetch_object($resql)) {
4527 $socialnetworks[$obj->code] = array(
4528 'rowid' => $obj->rowid,
4529 'label' => $obj->label,
4530 'url' => $obj->url,
4531 'icon' => $obj->icon,
4532 'active' => $obj->active,
4533 );
4534 }
4535 }
4536 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4537 }
4538
4539 return (is_array($socialnetworks) ? $socialnetworks : array());
4540}
4541
4552function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4553{
4554 global $hookmanager, $langs, $user;
4555
4556 $htmllink = $value;
4557
4558 if (empty($value)) {
4559 return '&nbsp;';
4560 }
4561
4562 if (!empty($type)) {
4563 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4564 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4565 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4566 if ($type == 'skype') {
4567 $htmllink .= dol_escape_htmltag($value);
4568 $htmllink .= '&nbsp; <a href="skype:';
4569 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4570 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4571 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4572 $htmllink .= '</a><a href="skype:';
4573 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4574 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4575 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4576 $htmllink .= '</a>';
4577 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4578 $addlink = 'AC_SKYPE';
4579 $link = '';
4580 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4581 $link = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&backtopage=1&actioncode=' . $addlink . '&contactid=' . $contactid . '&socid=' . $socid . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
4582 }
4583 $htmllink .= ($link ? ' ' . $link : '');
4584 }
4585 } else {
4586 if (!empty($dictsocialnetworks[$type]['url'])) {
4587 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4588 if ($tmpvirginurl) {
4589 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4590 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4591
4592 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4593 if ($tmpvirginurl3) {
4594 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4595 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4596 }
4597
4598 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4599 if ($tmpvirginurl2) {
4600 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4601 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4602 }
4603 }
4604 if (preg_match('/^https?:\/\//i', $value)) {
4605 $link = $value;
4606 } else {
4607 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4608 }
4609 $valuetoshow = $value;
4610 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4611 if (preg_match('/^https?:\/\//i', $link)) {
4612 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4613 } else {
4614 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4615 }
4616 } else {
4617 $htmllink .= dol_escape_htmltag($value);
4618 }
4619 }
4620 $htmllink .= '</div>';
4621 } else {
4622 $langs->load("errors");
4623 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4624 }
4625
4626 if ($hookmanager) {
4627 $parameters = array(
4628 'value' => $value,
4629 'cid' => $contactid,
4630 'socid' => $socid,
4631 'type' => $type,
4632 'dictsocialnetworks' => $dictsocialnetworks,
4633 );
4634
4635 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4636 if ($reshook > 0) {
4637 $htmllink = '';
4638 }
4639 $htmllink .= $hookmanager->resPrint;
4640 }
4641
4642 return $htmllink;
4643}
4644
4654function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4655{
4656 global $mysoc;
4657
4658 if (empty($profID) || empty($profIDtype)) {
4659 return '';
4660 }
4661 if (empty($countrycode)) {
4662 $countrycode = $mysoc->country_code;
4663 }
4664 $newProfID = $profID;
4665 $id = substr($profIDtype, -1);
4666 $ret = '';
4667 if (strtoupper($countrycode) == 'FR') {
4668 // France
4669 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4670
4671 if ($id == 1 && dol_strlen($newProfID) == 9) {
4672 // SIREN (ex: 123 123 123)
4673 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3);
4674 }
4675 if ($id == 2 && dol_strlen($newProfID) == 14) {
4676 // SIRET (ex: 123 123 123 12345)
4677 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3) . ' ' . substr($newProfID, 9, 5);
4678 }
4679 if ($id == 3 && dol_strlen($newProfID) == 5) {
4680 // NAF/APE (ex: 69.20Z)
4681 $newProfID = substr($newProfID, 0, 2) . '.' . substr($newProfID, 2, 3);
4682 }
4683 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4684 // TVA intracommunautaire (ex: FR12 123 123 123)
4685 $newProfID = substr($newProfID, 0, 4) . ' ' . substr($newProfID, 4, 3) . ' ' . substr($newProfID, 7, 3) . ' ' . substr($newProfID, 10, 3);
4686 }
4687 }
4688 if (!empty($addcpButton)) {
4689 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4690 } else {
4691 $ret = $newProfID;
4692 }
4693 return $ret;
4694}
4695
4711function dol_print_phone($phone, $countrycode = '', $contactid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4712{
4713 global $conf, $user, $langs, $mysoc, $hookmanager;
4714
4715 // Clean phone parameter
4716 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4717 if (empty($phone)) {
4718 return '';
4719 }
4720 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4721 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4722 }
4723 if (empty($countrycode) && is_object($mysoc)) {
4724 $countrycode = $mysoc->country_code;
4725 }
4726
4727 // Short format for small screens
4728 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4729 $separ = '';
4730 }
4731
4732 $newphone = $phone;
4733 $newphonewa = $phone;
4734 if (strtoupper($countrycode) == "FR") {
4735 // France
4736 if (dol_strlen($phone) == 10) {
4737 $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);
4738 } elseif (dol_strlen($phone) == 7) {
4739 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2);
4740 } elseif (dol_strlen($phone) == 9) {
4741 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2);
4742 } elseif (dol_strlen($phone) == 11) {
4743 $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);
4744 } elseif (dol_strlen($phone) == 12) {
4745 $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);
4746 } elseif (dol_strlen($phone) == 13) {
4747 $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);
4748 }
4749 } elseif (strtoupper($countrycode) == "CA") {
4750 if (dol_strlen($phone) == 10) {
4751 $newphone = ($separ != '' ? '(' : '') . substr($newphone, 0, 3) . ($separ != '' ? ')' : '') . $separ . substr($newphone, 3, 3) . ($separ != '' ? '-' : '') . substr($newphone, 6, 4);
4752 }
4753 } elseif (strtoupper($countrycode) == "PT") { //Portugal
4754 if (dol_strlen($phone) == 13) { //ex: +351_ABC_DEF_GHI
4755 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4756 }
4757 } elseif (strtoupper($countrycode) == "SR") { //Suriname
4758 if (dol_strlen($phone) == 10) { //ex: +597_ABC_DEF
4759 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3);
4760 } elseif (dol_strlen($phone) == 11) { //ex: +597_ABC_DEFG
4761 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
4762 }
4763 } elseif (strtoupper($countrycode) == "DE") { //Allemagne
4764 if (dol_strlen($phone) == 14) { //ex: +49_ABCD_EFGH_IJK
4765 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 4) . $separ . substr($newphone, 11, 3);
4766 } elseif (dol_strlen($phone) == 13) { //ex: +49_ABC_DEFG_HIJ
4767 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 4) . $separ . substr($newphone, 10, 3);
4768 }
4769 } elseif (strtoupper($countrycode) == "ES") { //Espagne
4770 if (dol_strlen($phone) == 12) { //ex: +34_ABC_DEF_GHI
4771 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4772 }
4773 } elseif (strtoupper($countrycode) == "BF") { // Burkina Faso
4774 if (dol_strlen($phone) == 12) { //ex : +22 A BC_DE_FG_HI
4775 $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);
4776 }
4777 } elseif (strtoupper($countrycode) == "RO") { // Roumanie
4778 if (dol_strlen($phone) == 12) { //ex : +40 AB_CDE_FG_HI
4779 $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);
4780 }
4781 } elseif (strtoupper($countrycode) == "TR") { //Turquie
4782 if (dol_strlen($phone) == 13) { //ex : +90 ABC_DEF_GHIJ
4783 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4784 }
4785 } elseif (strtoupper($countrycode) == "US") { //Etat-Unis
4786 if (dol_strlen($phone) == 12) { //ex: +1 ABC_DEF_GHIJ
4787 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4788 }
4789 } elseif (strtoupper($countrycode) == "MX") { //Mexique
4790 if (dol_strlen($phone) == 12) { //ex: +52 ABCD_EFG_HI
4791 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4792 } elseif (dol_strlen($phone) == 11) { //ex: +52 AB_CD_EF_GH
4793 $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);
4794 } elseif (dol_strlen($phone) == 13) { //ex: +52 ABC_DEF_GHIJ
4795 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4796 }
4797 } elseif (strtoupper($countrycode) == "ML") { //Mali
4798 if (dol_strlen($phone) == 12) { //ex: +223 AB_CD_EF_GH
4799 $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);
4800 }
4801 } elseif (strtoupper($countrycode) == "TH") { //Thaïlande
4802 if (dol_strlen($phone) == 11) { //ex: +66_ABC_DE_FGH
4803 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4804 } elseif (dol_strlen($phone) == 12) { //ex: +66_A_BCD_EF_GHI
4805 $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);
4806 }
4807 } elseif (strtoupper($countrycode) == "MU") {
4808 //Maurice
4809 if (dol_strlen($phone) == 11) { //ex: +230_ABC_DE_FG
4810 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4811 } elseif (dol_strlen($phone) == 12) { //ex: +230_ABCD_EF_GH
4812 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4813 }
4814 } elseif (strtoupper($countrycode) == "ZA") { //Afrique du sud
4815 if (dol_strlen($phone) == 12) { //ex: +27_AB_CDE_FG_HI
4816 $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);
4817 }
4818 } elseif (strtoupper($countrycode) == "SY") { //Syrie
4819 if (dol_strlen($phone) == 12) { //ex: +963_AB_CD_EF_GH
4820 $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);
4821 } elseif (dol_strlen($phone) == 13) { //ex: +963_AB_CD_EF_GHI
4822 $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);
4823 }
4824 } elseif (strtoupper($countrycode) == "AE") { //Emirats Arabes Unis
4825 if (dol_strlen($phone) == 12) { //ex: +971_ABC_DEF_GH
4826 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4827 } elseif (dol_strlen($phone) == 13) { //ex: +971_ABC_DEF_GHI
4828 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4829 } elseif (dol_strlen($phone) == 14) { //ex: +971_ABC_DEF_GHIK
4830 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 4);
4831 }
4832 } elseif (strtoupper($countrycode) == "DZ") { //Algérie
4833 if (dol_strlen($phone) == 13) { //ex: +213_ABC_DEF_GHI
4834 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4835 }
4836 } elseif (strtoupper($countrycode) == "BE") { //Belgique
4837 if (dol_strlen($phone) == 11) { //ex: +32_ABC_DE_FGH
4838 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4839 } elseif (dol_strlen($phone) == 12) { //ex: +32_ABC_DEF_GHI
4840 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4841 }
4842 } elseif (strtoupper($countrycode) == "PF") { //Polynésie française
4843 if (dol_strlen($phone) == 12) { //ex: +689_AB_CD_EF_GH
4844 $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);
4845 }
4846 } elseif (strtoupper($countrycode) == "CO") { //Colombie
4847 if (dol_strlen($phone) == 13) { //ex: +57_ABC_DEF_GH_IJ
4848 $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);
4849 }
4850 } elseif (strtoupper($countrycode) == "JO") { //Jordanie
4851 if (dol_strlen($phone) == 12) { //ex: +962_A_BCD_EF_GH
4852 $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);
4853 }
4854 } elseif (strtoupper($countrycode) == "JM") { //Jamaïque
4855 if (dol_strlen($newphone) == 12) { //ex: +1867_ABC_DEFG
4856 $newphone = substr($newphone, 0, 5) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4857 }
4858 } elseif (strtoupper($countrycode) == "MG") { //Madagascar
4859 if (dol_strlen($phone) == 13) { //ex: +261_AB_CD_EFG_HI
4860 $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);
4861 }
4862 } elseif (strtoupper($countrycode) == "GB") { //Royaume uni
4863 if (dol_strlen($phone) == 13) { //ex: +44_ABCD_EFG_HIJ
4864 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4865 }
4866 } elseif (strtoupper($countrycode) == "CH") { //Suisse
4867 if (dol_strlen($phone) == 12) { //ex: +41_AB_CDE_FG_HI
4868 $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);
4869 } elseif (dol_strlen($phone) == 15) { // +41_AB_CDE_FGH_IJKL
4870 $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);
4871 }
4872 } elseif (strtoupper($countrycode) == "TN") { //Tunisie
4873 if (dol_strlen($phone) == 12) { //ex: +216_AB_CDE_FGH
4874 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4875 }
4876 } elseif (strtoupper($countrycode) == "GF") { //Guyane francaise
4877 if (dol_strlen($phone) == 13) { //ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4878 $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);
4879 }
4880 } elseif (strtoupper($countrycode) == "GP") { //Guadeloupe
4881 if (dol_strlen($phone) == 13) { //ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4882 $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);
4883 }
4884 } elseif (strtoupper($countrycode) == "MQ") { //Martinique
4885 if (dol_strlen($phone) == 13) { //ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4886 $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);
4887 }
4888 } elseif (strtoupper($countrycode) == "IT") { //Italie
4889 if (dol_strlen($phone) == 12) { //ex: +39_ABC_DEF_GHI
4890 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4891 } elseif (dol_strlen($phone) == 13) { //ex: +39_ABC_DEF_GH_IJ
4892 $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);
4893 }
4894 } elseif (strtoupper($countrycode) == "AU") {
4895 //Australie
4896 if (dol_strlen($phone) == 12) {
4897 //ex: +61_A_BCDE_FGHI
4898 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 4);
4899 }
4900 } elseif (strtoupper($countrycode) == "LU") {
4901 // Luxembourg
4902 if (dol_strlen($phone) == 10) { // fix 6 digits +352_AA_BB_CC
4903 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2);
4904 } elseif (dol_strlen($phone) == 11) { // fix 7 digits +352_AA_BB_CC_D
4905 $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);
4906 } elseif (dol_strlen($phone) == 12) { // fix 8 digits +352_AA_BB_CC_DD
4907 $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);
4908 } elseif (dol_strlen($phone) == 13) { // mobile +352_AAA_BB_CC_DD
4909 $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);
4910 }
4911 } elseif (strtoupper($countrycode) == "PE") {
4912 // Peru
4913 if (dol_strlen($phone) == 7) { // fix 7 chiffres without code AAA_BBBB
4914 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4);
4915 } elseif (dol_strlen($phone) == 9) { // mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4916 $newphonewa = '+51' . $newphone;
4917 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 10, 3);
4918 } elseif (dol_strlen($phone) == 11) { // fix 11 chiffres +511_AAA_BBBB
4919 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 8, 4);
4920 } elseif (dol_strlen($phone) == 12) { // mobile +51_AAA_BBB_CCC
4921 $newphonewa = $newphone;
4922 $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);
4923 }
4924 } elseif (strtoupper($countrycode) == "IN") { //India
4925 if (dol_strlen($phone) == 13) {
4926 if ($withpicto == 'phone') { //ex: +91_AB_CDEF_GHIJ
4927 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 4) . $separ . substr($newphone, 9, 4);
4928 } else { //ex: +91_ABCDE_FGHIJ
4929 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 5) . $separ . substr($newphone, 8, 5);
4930 }
4931 }
4932 }
4933
4934 $newphoneastart = $newphoneaend = '';
4935 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4936 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
4937 $newphoneastart = '<a href="tel:' . urlencode($phone) . '">';
4938 $newphoneaend .= '</a>';
4939 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4940 if (empty($user->clicktodial_loaded)) {
4941 $user->fetch_clicktodial();
4942 }
4943
4944 // Define urlmask
4945 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4946 if (!empty($user->clicktodial_url)) {
4947 $urlmask = $user->clicktodial_url;
4948 }
4949
4950 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4951 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4952 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4953 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4954 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4955 // Those lines are for substitution
4956 $substitarray = array(
4957 '__PHONEFROM__' => $clicktodial_poste,
4958 '__PHONETO__' => urlencode($phone),
4959 '__LOGIN__' => $clicktodial_login,
4960 '__PASS__' => $clicktodial_password
4961 );
4962 $url = make_substitutions($url, $substitarray);
4963 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4964 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4965 $newphoneastart = '<a href="' . $url . '" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4966 $newphoneaend = '</a>';
4967 } else {
4968 // Old method
4969 $newphoneastart = '<a href="' . $url . '"';
4970 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4971 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4972 }
4973 $newphoneastart .= '>';
4974 $newphoneaend .= '</a>';
4975 }
4976 }
4977
4978 //if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4979 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4980 $type = 'AC_TEL';
4981 $addlinktoagenda = '';
4982 if ($addlink == 'AC_FAX') {
4983 $type = 'AC_FAX';
4984 }
4985 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4986 $addlinktoagenda = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&amp;backtopage=' . urlencode($_SERVER['REQUEST_URI']) . '&amp;actioncode=' . $type . ($contactid ? '&amp;contactid=' . $contactid : '') . ($socid ? '&amp;socid=' . $socid : '') . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
4987 }
4988 if ($addlinktoagenda) {
4989 $newphone = '<span>' . $newphone . ' ' . $addlinktoagenda . '</span>';
4990 }
4991 }
4992 }
4993
4994 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4995 // Link to Whatsapp
4996 $newphone .= ' <a href="https://wa.me/' . $newphonewa . '" target="_blank"'; // Use api to whatasapp contacts
4997 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4998 }
4999
5000 if (empty($titlealt)) {
5001 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
5002 }
5003 $rep = '';
5004
5005 if ($hookmanager) {
5006 $parameters = array('countrycode' => $countrycode, 'cid' => $contactid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
5007 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
5008 $rep .= $hookmanager->resPrint;
5009 }
5010 if (empty($reshook)) {
5011 $picto = '';
5012 if ($withpicto) {
5013 if ($withpicto == 'fax') {
5014 $picto = 'phoning_fax';
5015 } elseif ($withpicto == 'phone') {
5016 $picto = 'phone';
5017 } elseif ($withpicto == 'mobile') {
5018 $picto = 'phoning_mobile';
5019 } else {
5020 $picto = '';
5021 }
5022 }
5023 if ($adddivfloat == 1) {
5024 $rep .= '<div class="nospan float' . ($morecss ? ' ' . $morecss : '') . '">';
5025 } elseif (empty($adddivfloat)) {
5026 $rep .= '<span' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
5027 }
5028
5029 $rep .= $newphoneastart;
5030 $rep .= ($withpicto ? img_picto($titlealt, $picto) : '');
5031 if ($separ != 'hidenum') {
5032 $rep .= ($withpicto ? ' ' : '') . $newphone;
5033 }
5034 $rep .= $newphoneaend;
5035
5036 if ($adddivfloat == 1) {
5037 $rep .= '</div>';
5038 } elseif (empty($adddivfloat)) {
5039 $rep .= '</span>';
5040 }
5041 }
5042
5043 return $rep;
5044}
5045
5054function dol_print_ip($ip, $mode = 0, $showname = 0)
5055{
5056 global $conf;
5057
5058 $ret = '';
5059 if (!isset($conf->cache['resolveips'])) {
5060 $conf->cache['resolveips'] = array();
5061 }
5062
5063 if ($mode != 2) {
5064 $countrycode = dolGetCountryCodeFromIp($ip);
5065 if ($countrycode) { // If success, countrycode is us, fr, ...
5066 if (file_exists(DOL_DOCUMENT_ROOT . '/theme/common/flags/' . $countrycode . '.png')) {
5067 $ret .= picto_from_langcode($countrycode);
5068 } else {
5069 $ret .= '(' . $countrycode . ')';
5070 }
5071 $ret .= '&nbsp;';
5072 } else {
5073 // Nothing
5074 }
5075 }
5076
5077 if (in_array($mode, [0, 2])) {
5078 $domain = '';
5079 if ($showname) {
5080 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
5081 $domain = gethostbyaddr($ip);
5082 $conf->cache['resolveips'][$ip] = $domain; // false or domain
5083 } else {
5084 $domain = $conf->cache['resolveips'][$ip];
5085 }
5086 }
5087 if ($domain) {
5088 $ret .= $domain;
5089 } else {
5090 $ret .= $ip;
5091 }
5092 }
5093
5094 return $ret;
5095}
5096
5109function getUserRemoteIP($trusted = 0)
5110{
5111 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
5112 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
5113 // Note that if apache module remoteip has been enabled, REMOTE_ADDR can contain the real client (the value from cloudFlare HTTP_CF_CONNECTING_IP for example)
5114 // This can happen if the proxy were added in the list of trusted proxy.
5115 return $ip;
5116 }
5117
5118 // Try to guess the real IP of client (but this may not be reliable)
5119 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
5120 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
5121 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
5122 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
5123 } else {
5124 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
5125 }
5126 } else {
5127 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
5128 }
5129 } else {
5130 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
5131 }
5132 return $ip;
5133}
5134
5143function isHTTPS()
5144{
5145 $isSecure = false;
5146 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
5147 $isSecure = true;
5148 } 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') {
5149 $isSecure = true;
5150 }
5151 return $isSecure;
5152}
5153
5160function dolGetCountryCodeFromIp($ip)
5161{
5162 $countrycode = '';
5163
5164 if (isModEnabled('geoipmaxmind')) {
5165 if (getDolGlobalString('GEOIP_VERSION') == 'php') {
5166 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5167 } else {
5168 $diroffile = getMultidirOutput(null, 'geoipmaxmind');
5169 $datafile = $diroffile . '/' . getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE_EMBEDDED');
5170 }
5171 //$ip='24.24.24.24';
5172 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
5173 if ($datafile) {
5174 try {
5175 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5176 $geoip = new DolGeoIP('country', $datafile);
5177 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
5178 $countrycode = $geoip->getCountryCodeFromIP($ip);
5179 } catch (Exception $e) {
5180 //print 'Error with GeoIP database: '.$e->getMessage();
5181 }
5182 }
5183 }
5184
5185 return $countrycode;
5186}
5187
5188
5195function dol_user_country()
5196{
5197 global $conf, $langs, $user;
5198
5199 //$ret=$user->xxx;
5200 $ret = '';
5201 if (isModEnabled('geoipmaxmind')) {
5202 $ip = getUserRemoteIP();
5203 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5204 //$ip='24.24.24.24';
5205 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
5206 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5207 $geoip = new DolGeoIP('country', $datafile);
5208 $countrycode = $geoip->getCountryCodeFromIP($ip);
5209 $ret = $countrycode;
5210 }
5211 return $ret;
5212}
5213
5226function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
5227{
5228 global $hookmanager;
5229
5230 $out = '';
5231
5232 if ($address) {
5233 if ($hookmanager) {
5234 $parameters = array('element' => $element, 'id' => $id);
5235 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
5236 $out .= $hookmanager->resPrint;
5237 }
5238 if (empty($reshook)) {
5239 if (empty($charfornl)) {
5240 $out .= nl2br((string) $address);
5241 } else {
5242 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
5243 }
5244
5245 // TODO Remove this block, we can add this using the hook now
5246 $showgmap = $showomap = 0;
5247 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
5248 $showgmap = 1;
5249 }
5250 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
5251 $showgmap = 1;
5252 }
5253 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
5254 $showgmap = 1;
5255 }
5256 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
5257 $showgmap = 1;
5258 }
5259 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
5260 $showomap = 1;
5261 }
5262 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
5263 $showomap = 1;
5264 }
5265 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
5266 $showomap = 1;
5267 }
5268 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
5269 $showomap = 1;
5270 }
5271 if ($showgmap) {
5272 $url = dol_buildpath('/google/gmaps.php?mode=' . $element . '&id=' . $id, 1);
5273 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5274 }
5275 if ($showomap) {
5276 $url = dol_buildpath('/openstreetmap/maps.php?mode=' . $element . '&id=' . $id, 1);
5277 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '_openstreetmap" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5278 }
5279 }
5280 }
5281 if ($noprint) {
5282 return $out;
5283 } else {
5284 print $out;
5285 return null;
5286 }
5287}
5288
5289
5299function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5300{
5301 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5302 return true;
5303 }
5304 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5305 return true;
5306 }
5307 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5308 return true;
5309 }
5310
5311 return false;
5312}
5313
5323function isValidMXRecord($domain)
5324{
5325 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5326 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5327 return 0;
5328 }
5329 if (function_exists('getmxrr')) {
5330 $mxhosts = array();
5331 $weight = array();
5332 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5333 if (count($mxhosts) > 1) {
5334 return 1;
5335 }
5336 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5337 return 1;
5338 }
5339
5340 return 0;
5341 }
5342 }
5343
5344 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5345 return -1;
5346}
5347
5355function isValidPhone($phone)
5356{
5357 return true;
5358}
5359
5360
5370function dolGetFirstLetters($s, $nbofchar = 1)
5371{
5372 $ret = '';
5373 $tmparray = explode(' ', $s);
5374 foreach ($tmparray as $tmps) {
5375 $ret .= dol_substr($tmps, 0, $nbofchar);
5376 }
5377
5378 return $ret;
5379}
5380
5381
5389function dol_strlen($string, $stringencoding = 'UTF-8')
5390{
5391 if (is_null($string)) {
5392 return 0;
5393 }
5394
5395 if (function_exists('mb_strlen')) {
5396 return mb_strlen($string, $stringencoding);
5397 } else {
5398 return strlen($string);
5399 }
5400}
5401
5412function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5413{
5414 global $langs;
5415
5416 if (empty($stringencoding)) {
5417 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5418 }
5419
5420 $ret = '';
5421 if (empty($trunconbytes)) {
5422 if (function_exists('mb_substr')) {
5423 $ret = mb_substr($string, $start, $length, $stringencoding);
5424 } else {
5425 $ret = substr($string, $start, $length);
5426 }
5427 } else {
5428 if (function_exists('mb_strcut')) {
5429 $ret = mb_strcut($string, $start, $length, $stringencoding);
5430 } else {
5431 $ret = substr($string, $start, $length);
5432 }
5433 }
5434 return $ret;
5435}
5436
5437
5451function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5452{
5453 global $conf;
5454
5455 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5456 return $string;
5457 }
5458
5459 if (empty($stringencoding)) {
5460 $stringencoding = 'UTF-8';
5461 }
5462 // reduce for small screen
5463 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5464 $size = round($size / 3);
5465 }
5466
5467 // We go always here
5468 if ($trunc == 'right') {
5469 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5470 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5471 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5472 return dol_substr($newstring, 0, $size, $stringencoding) . ($nodot ? '' : '…');
5473 } else {
5474 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5475 return $string;
5476 }
5477 } elseif ($trunc == 'middle') {
5478 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5479 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5480 $size1 = (int) round($size / 2);
5481 $size2 = (int) round($size / 2);
5482 return dol_substr($newstring, 0, $size1, $stringencoding) . '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5483 } else {
5484 return $string;
5485 }
5486 } elseif ($trunc == 'left') {
5487 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5488 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5489 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5490 return '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5491 } else {
5492 return $string;
5493 }
5494 } elseif ($trunc == 'wrap') {
5495 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5496 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5497 return dol_substr($newstring, 0, $size, $stringencoding) . "\n" . dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5498 } else {
5499 return $string;
5500 }
5501 } else {
5502 return 'BadParam3CallingDolTrunc';
5503 }
5504}
5505
5513function getPictoForType($key, $morecss = '')
5514{
5515 // Set array with type -> picto
5516 $type2picto = array(
5517 'varchar' => 'font',
5518 'text' => 'font',
5519 'html' => 'code',
5520 'int' => 'sort-numeric-down',
5521 'double' => 'sort-numeric-down',
5522 'price' => 'currency',
5523 'pricecy' => 'multicurrency',
5524 'password' => 'key',
5525 'boolean' => 'check-square',
5526 'date' => 'calendar',
5527 'datetime' => 'calendar',
5528 'duration' => 'hourglass',
5529 'phone' => 'phone',
5530 'mail' => 'email',
5531 'url' => 'url',
5532 'ip' => 'country',
5533 'select' => 'list',
5534 'sellist' => 'list',
5535 'stars' => 'fontawesome_star_fas',
5536 'radio' => 'check-circle',
5537 'checkbox' => 'list',
5538 'chkbxlst' => 'list',
5539 'link' => 'link',
5540 'icon' => "question",
5541 'point' => "country",
5542 'multipts' => 'country',
5543 'linestrg' => "country",
5544 'polygon' => "country",
5545 'separate' => 'minus'
5546 );
5547
5548 if (!empty($type2picto[$key])) {
5549 return img_picto('', $type2picto[$key], 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5550 }
5551
5552 return img_picto('', 'generic', 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5553}
5554
5555
5579function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5580{
5581 global $conf;
5582
5583 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5584 $url = DOL_URL_ROOT;
5585 $theme = isset($conf->theme) ? $conf->theme : null;
5586 $path = 'theme/' . $theme;
5587 if (empty($picto)) {
5588 $picto = 'generic';
5589 }
5590
5591 // Define fullpathpicto to use into src
5592 if ($pictoisfullpath) {
5593 // Clean parameters
5594 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5595 $picto .= '.png';
5596 }
5597 $fullpathpicto = $picto;
5598 $reg = array();
5599 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5600 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5601 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5602 }
5603 } else {
5604 // $picto can not be null since replaced with 'generic' in that case
5605 // $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5606 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5607 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5608 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5609
5610 // Fix some values of $pictowithouttext
5611 $pictoconvertkey = array(
5612 'facture' => 'bill',
5613 'shipping' => 'shipment',
5614 'fichinter' => 'intervention',
5615 'agenda' => 'calendar',
5616 'invoice_supplier' => 'supplier_invoice',
5617 'order_supplier' => 'supplier_order');
5618 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5619 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5620 }
5621
5622 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5623 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5624 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5625 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5626
5627 // Compatibility with old fontawesome versions
5628 if ($pictowithouttext == 'file-o') {
5629 $pictowithouttext = 'file';
5630 }
5631
5632 $pictowithouttextarray = explode('_', $pictowithouttext);
5633 $marginleftonlyshort = 0;
5634
5635 if (!empty($pictowithouttextarray[1])) {
5636 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5637 $fakey = 'fa-' . $pictowithouttextarray[0];
5638 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5639 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5640 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5641 } else {
5642 $fakey = 'fa-' . $pictowithouttext;
5643 $faprefix = 'fas';
5644 $facolor = '';
5645 $fasize = '';
5646 }
5647
5648 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5649 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5650 $morestyle = '';
5651 $reg = array();
5652 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5653 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5654 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5655 }
5656 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5657 $morestyle = $reg[1];
5658 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5659 }
5660 $moreatt = trim($moreatt);
5661
5662 $enabledisablehtml = '<span class="' . $faprefix . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5663 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5664 $enabledisablehtml .= '</span>';
5665
5666 return $enabledisablehtml;
5667 }
5668
5669 if (empty($srconly) && !preg_match('/[\.\/@]/', $picto)) { // If original picto code does not contains a / and no . inside, it is not a path to an image file on disk
5670 $fakey = $pictowithouttext;
5671 $facolor = '';
5672 $fasize = '';
5673 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5674 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'))) {
5675 $fa = 'far';
5676 }
5677 if (in_array($pictowithouttext, array('black-tie', 'discord', 'facebook', 'flickr', 'github', 'google', 'google-plus-g', 'instagram', 'linkedin', 'meetup', 'microsoft', 'pinterest', 'skype', 'slack', 'twitter', 'reddit', 'snapchat', 'stripe', 'stripe-s', 'tumblr', 'viadeo', 'whatsapp', 'youtube'))) {
5678 $fa = 'fab';
5679 }
5680
5681 $arrayconvpictotofa = getImgPictoConv('fa');
5682
5683 if ($pictowithouttext == 'off') {
5684 $fakey = 'fa-square';
5685 $fasize = '1.3em';
5686 } elseif ($pictowithouttext == 'on') {
5687 $fakey = 'fa-check-square';
5688 $fasize = '1.3em';
5689 } elseif ($pictowithouttext == 'listlight') {
5690 $fakey = 'fa-download';
5691 $marginleftonlyshort = 1;
5692 } elseif ($pictowithouttext == 'printer') {
5693 $fakey = 'fa-print';
5694 $fasize = '1.2em';
5695 } elseif ($pictowithouttext == 'note') {
5696 $fakey = 'fa-sticky-note';
5697 $marginleftonlyshort = 1;
5698 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5699 $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');
5700 $fakey = 'fa-' . $convertarray[$pictowithouttext];
5701 if (preg_match('/selected/', $pictowithouttext)) {
5702 $facolor = '#888';
5703 }
5704 $marginleftonlyshort = 1;
5705 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5706 $fakey = 'fa-' . $arrayconvpictotofa[$pictowithouttext];
5707 } else {
5708 $fakey = 'fa-' . $pictowithouttext;
5709 }
5710
5711 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5712 $morecss .= ' em092';
5713 }
5714 if (in_array($pictowithouttext, array('conferenceorbooth', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5715 $morecss .= ' em088';
5716 }
5717 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5718 $morecss .= ' em080';
5719 }
5720
5721 // Define $marginleftonlyshort
5722 $arrayconvpictotomarginleftonly = array(
5723 'bank',
5724 'check',
5725 'delete',
5726 'generic',
5727 'grip',
5728 'grip_title',
5729 'jabber',
5730 'grip_title',
5731 'grip',
5732 'listlight',
5733 'note',
5734 'on',
5735 'off',
5736 'playdisabled',
5737 'printer',
5738 'resize',
5739 'sign-out',
5740 'stats',
5741 'switch_on',
5742 'switch_on_grey',
5743 'switch_on_red',
5744 'switch_off',
5745 'switch_off_grey',
5746 'switch_off_red',
5747 'uparrow',
5748 '1uparrow',
5749 '1downarrow',
5750 '1leftarrow',
5751 '1rightarrow',
5752 '1uparrow_selected',
5753 '1downarrow_selected',
5754 '1leftarrow_selected',
5755 '1rightarrow_selected'
5756 );
5757 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5758 $marginleftonlyshort = 0;
5759 }
5760
5761 // Add CSS
5762 $arrayconvpictotomorcess = array(
5763 'action' => 'infobox-action',
5764 'account' => 'infobox-bank_account',
5765 'accounting_account' => 'infobox-bank_account',
5766 'accountline' => 'infobox-bank_account',
5767 'accountancy' => 'infobox-bank_account',
5768 'admin' => 'opacitymedium',
5769 'asset' => 'infobox-bank_account',
5770 'bank_account' => 'infobox-bank_account',
5771 'bill' => 'infobox-commande',
5772 'billa' => 'infobox-commande',
5773 'billr' => 'infobox-commande',
5774 'billd' => 'infobox-commande',
5775 'bookcal' => 'infobox-portal',
5776 'margin' => 'infobox-bank_account',
5777 'conferenceorbooth' => 'infobox-project',
5778 'cash-register' => 'infobox-portal',
5779 'contract' => 'infobox-contrat',
5780 'check' => 'font-status4',
5781 'conversation' => 'infobox-contrat',
5782 'donation' => 'infobox-commande',
5783 'dolly' => 'infobox-commande',
5784 'dollyrevert' => 'flip infobox-order_supplier',
5785 'ecm' => 'infobox-action',
5786 'eventorganization' => 'infobox-project',
5787 'hrm' => 'infobox-adherent',
5788 'group' => 'infobox-adherent',
5789 'intervention' => 'infobox-contrat',
5790 'incoterm' => 'infobox-supplier_proposal',
5791 'intracommreport' => 'infobox-bank_account',
5792 'currency' => 'infobox-bank_account',
5793 'multicurrency' => 'infobox-bank_account',
5794 'members' => 'infobox-adherent',
5795 'member' => 'infobox-adherent',
5796 'money-bill-alt' => 'infobox-bank_account',
5797 'order' => 'infobox-commande',
5798 'user' => 'infobox-adherent',
5799 'users' => 'infobox-adherent',
5800 'error' => 'pictoerror',
5801 'warning' => 'pictowarning',
5802 'switch_on' => 'font-status4',
5803 'switch_on_warning' => 'font-status4 warning',
5804 'switch_on_red' => 'font-status8',
5805 'switch_off_warning' => 'font-status4 warning',
5806 'switch_off_red' => 'font-status8',
5807 'holiday' => 'infobox-holiday',
5808 'info' => 'opacityhigh',
5809 'info_black' => 'purple',
5810 'invoice' => 'infobox-commande',
5811 'knowledgemanagement' => 'infobox-contrat rotate90',
5812 'loan' => 'infobox-commande',
5813 'payment' => 'infobox-bank_account',
5814 'payment_vat' => 'infobox-bank_account',
5815 'poll' => 'infobox-portal',
5816 'pos' => 'infobox-bank_account',
5817 'project' => 'infobox-project',
5818 'projecttask' => 'infobox-project',
5819 'propal' => 'infobox-propal',
5820 'proposal' => 'infobox-propal',
5821 'private' => 'infobox-project',
5822 'reception' => 'flip infobox-order_supplier',
5823 'recruitmentjobposition' => 'infobox-adherent',
5824 'recruitmentcandidature' => 'infobox-adherent',
5825 'resource' => 'infobox-action',
5826 'salary' => 'infobox-commande',
5827 'shapes' => 'infobox-adherent',
5828 'shipment' => 'infobox-commande',
5829 'store' => 'infobox-portal',
5830 'stripe' => 'infobox-bank_account',
5831 'supplier_invoice' => 'infobox-order_supplier',
5832 'supplier_invoicea' => 'infobox-order_supplier',
5833 'supplier_invoiced' => 'infobox-order_supplier',
5834 'supplier' => 'infobox-order_supplier',
5835 'supplier_order' => 'infobox-order_supplier',
5836 'supplier_proposal' => 'infobox-supplier_proposal',
5837 'ticket' => 'infobox-contrat',
5838 'title_accountancy' => 'infobox-bank_account',
5839 'title_hrm' => 'infobox-holiday',
5840 'expensereport' => 'infobox-expensereport',
5841 'trip' => 'infobox-expensereport',
5842 'title_agenda' => 'infobox-action',
5843 'vat' => 'infobox-bank_account',
5844 'webportal' => 'infobox-portal',
5845 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5846 'list-alt' => 'imgforviewmode',
5847 'calendar' => 'imgforviewmode',
5848 'calendarweek' => 'imgforviewmode',
5849 'calendarmonth' => 'imgforviewmode',
5850 'calendarday' => 'imgforviewmode',
5851 'calendarperuser' => 'imgforviewmode',
5852 'calendarpertype' => 'imgforviewmode'
5853 );
5854 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5855 $morecss .= ($morecss ? ' ' : '') . $arrayconvpictotomorcess[$pictowithouttext];
5856 }
5857
5858 // Define $color
5859 $arrayconvpictotocolor = array(
5860 'address' => '#6c6aa8',
5861 'building' => '#6c6aa8',
5862 'bom' => '#a69944',
5863 'clone' => '#999',
5864 'cog' => '#999',
5865 'companies' => '#6c6aa8',
5866 'company' => '#6c6aa8',
5867 'contact' => '#6c6aa8',
5868 'cron' => '#555',
5869 'dynamicprice' => '#a69944',
5870 'edit' => '#444',
5871 'note' => '#999',
5872 'error' => '',
5873 'help' => '#bbb',
5874 'listlight' => '#999',
5875 'language' => '#555',
5876 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5877 'lock' => '#ddd',
5878 'lot' => '#a69944',
5879 'map-marker-alt' => '#aaa',
5880 'mrp' => '#a69944',
5881 'product' => '#a69944',
5882 'service' => '#a69944',
5883 'inventory' => '#a69944',
5884 'stock' => '#a69944',
5885 'movement' => '#a69944',
5886 'other' => '#ddd',
5887 'world' => '#986c6a',
5888 'partnership' => '#6c6aa8',
5889 'playdisabled' => '#ccc',
5890 'printer' => '#444',
5891 'projectpub' => '#986c6a',
5892 'resize' => '#444',
5893 'rss' => '#cba',
5894 //'shipment'=>'#a69944',
5895 'search-plus' => '#808080',
5896 'security' => '#999',
5897 'square' => '#888',
5898 'stop-circle' => '#888',
5899 'stats' => '#444',
5900 'superadmin' => '#600',
5901 'switch_off' => '#999',
5902 'technic' => '#999',
5903 'tick' => '#282',
5904 'timespent' => '#555',
5905 'uncheck' => '#800',
5906 'uparrow' => '#555',
5907 'user-cog' => '#999',
5908 'country' => '#aaa',
5909 'globe-americas' => '#aaa',
5910 'region' => '#aaa',
5911 'state' => '#aaa',
5912 'website' => '#304',
5913 'workstation' => '#a69944'
5914 );
5915 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5916 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5917 }
5918
5919 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5920 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5921 $morestyle = '';
5922 $reg = array();
5923 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5924 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5925 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5926 }
5927 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5928 $morestyle = $reg[1];
5929 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5930 }
5931 $moreatt = trim($moreatt);
5932
5933 $enabledisablehtml = '<span class="' . $fa . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5934 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5935 $enabledisablehtml .= '</span>';
5936
5937 return $enabledisablehtml;
5938 }
5939
5940 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5941 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/' . $theme; // If the theme does not have the same name as the module
5942 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5943 $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
5944 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5945 $path = $theme . '/theme/' . $theme; // If the theme have the same name as the module
5946 }
5947
5948 // If we ask an image into $url/$mymodule/img (instead of default path)
5949 $regs = array();
5950 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5951 $picto = $regs[1];
5952 $path = $regs[2]; // $path is $mymodule
5953 }
5954
5955 // Clean parameters
5956 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5957 $picto .= '.png';
5958 }
5959 // If alt path are defined, define url where img file is, according to physical path
5960 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5961 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5962 if ($type == 'main') {
5963 continue;
5964 }
5965 // This consumes a lot of time, that's why enabling alternative dir like "custom" dir should be avoid
5966 if (file_exists($dirroot . '/' . $path . '/img/' . $picto) && !empty($conf->file->dol_url_root)) {
5967 $url = DOL_URL_ROOT . $conf->file->dol_url_root[$type];
5968 break;
5969 }
5970 }
5971
5972 // $url is '' or '/custom', $path is current theme or
5973 $fullpathpicto = $url . '/' . $path . '/img/' . $picto;
5974 }
5975
5976 if ($srconly) {
5977 return $fullpathpicto;
5978 }
5979
5980 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5981 return '<img src="' . $fullpathpicto . '"' . ($notitle ? '' : ' alt="' . dolPrintHTMLForAttribute($alt, 0, $allowothertags) . '"') . (($notitle || empty($titlealt)) ? '' : ' title="' . dolPrintHTMLForAttribute($titlealt, 0, $allowothertags) . '"') . ($moreatt ? ' ' . $moreatt . ($morecss ? ' class="' . $morecss . '"' : '') : ' class="inline-block' . ($morecss ? ' ' . $morecss : '') . '"') . '>'; // Alt is used for accessibility, title for popup
5982}
5983
5991function getImgPictoConv($mode = 'fa')
5992{
5993 global $conf;
5994
5995 if (empty($mode) || $mode == 'fa') {
5996 // Array when the fa picto key is different than the Dolibarr picto key.
5997 $arrayconvpictotofa = array(
5998 'account' => 'university',
5999 'accounting_account' => 'clipboard-list',
6000 'accountline' => 'receipt',
6001 'accountancy' => 'search-dollar',
6002 'action' => 'calendar-alt',
6003 'add' => 'plus-circle',
6004 'address' => 'address-book',
6005 'ai' => 'magic',
6006 'admin' => 'star',
6007 'asset' => 'money-check-alt',
6008 'autofill' => 'fill',
6009 'back' => 'arrow-left',
6010 'bank_account' => 'university',
6011 'bill' => 'file-invoice-dollar',
6012 'billa' => 'file-excel',
6013 'billr' => 'file-invoice-dollar',
6014 'billd' => 'file-medical',
6015 'blockedlog' => 'file-archive',
6016 'bookcal' => 'calendar-check',
6017 'supplier_invoice' => 'file-invoice-dollar',
6018 'supplier_invoicea' => 'file-excel',
6019 'supplier_invoicer' => 'file-invoice-dollar',
6020 'supplier_invoiced' => 'file-medical',
6021 'bom' => 'shapes',
6022 'card' => 'address-card',
6023 'chart' => 'chart-line',
6024 'company' => 'building',
6025 'contact' => 'address-book',
6026 'contract' => 'suitcase',
6027 'collab' => 'people-arrows',
6028 'conversation' => 'comments',
6029 'country' => 'globe-americas',
6030 'cron' => 'business-time',
6031 'cross' => 'times',
6032 'chevron-double-left' => 'angle-double-left',
6033 'chevron-double-right' => 'angle-double-right',
6034 'chevron-double-down' => 'angle-double-down',
6035 'chevron-double-top' => 'angle-double-up',
6036 'donation' => 'gift',
6037 'dynamicprice' => 'hand-holding-usd',
6038 'setup' => 'cog',
6039 'companies' => 'building',
6040 'products' => 'cube',
6041 'commercial' => 'suitcase',
6042 'invoicing' => 'coins',
6043 'accounting' => 'search-dollar',
6044 'category' => 'tag',
6045 'dollyrevert' => 'dolly',
6046 'file-o' => 'file',
6047 'generate' => 'plus-square',
6048 'hrm' => 'user-tie',
6049 'incoterm' => 'truck-loading',
6050 'margin' => 'calculator',
6051 'members' => 'user-friends',
6052 'ticket' => 'ticket-alt',
6053 'globe' => 'external-link-alt',
6054 'lot' => 'barcode',
6055 'email' => 'at',
6056 'establishment' => 'building',
6057 'edit' => 'pencil-alt',
6058 'entity' => 'globe',
6059 'graph' => 'chart-line',
6060 'grip_title' => 'arrows-alt',
6061 'grip' => 'arrows-alt',
6062 'help' => 'question-circle',
6063 'generic' => 'file',
6064 'holiday' => 'umbrella-beach',
6065 'info' => 'info-circle',
6066 'info_black' => 'info-circle',
6067 'inventory' => 'boxes',
6068 'intracommreport' => 'globe-europe',
6069 'jobprofile' => 'cogs',
6070 'knowledgemanagement' => 'ticket-alt',
6071 'label' => 'layer-group',
6072 'layout' => 'columns',
6073 'line' => 'bars',
6074 'loan' => 'money-bill-alt',
6075 'member' => 'user-alt',
6076 'meeting' => 'chalkboard-teacher',
6077 'mrp' => 'cubes',
6078 'next' => 'arrow-alt-circle-right',
6079 'trip' => 'wallet',
6080 'expensereport' => 'wallet',
6081 'group' => 'users',
6082 'movement' => 'people-carry',
6083 'sign-out' => 'sign-out-alt',
6084 'superadmin' => 'star',
6085 'switch_off' => 'toggle-off',
6086 'switch_off_grey' => 'toggle-off',
6087 'switch_off_warning' => 'toggle-off',
6088 'switch_off_red' => 'toggle-off',
6089 'switch_on' => 'toggle-on',
6090 'switch_on_grey' => 'toggle-on',
6091 'switch_on_warning' => 'toggle-on',
6092 'switch_on_red' => 'toggle-on',
6093 'check' => 'check',
6094 'bookmark' => 'star',
6095 'bank' => 'university',
6096 'close_title' => 'times',
6097 'delete' => 'trash',
6098 'filter' => 'filter',
6099 'list-alt' => 'list-alt',
6100 'calendarlist' => 'bars',
6101 'calendar' => 'calendar-alt',
6102 'calendarmonth' => 'calendar-alt',
6103 'calendarweek' => 'calendar-week',
6104 'calendarday' => 'calendar-day',
6105 'calendarperuser' => 'table',
6106 'calendarpertype' => 'table',
6107 'intervention' => 'ambulance',
6108 'invoice' => 'file-invoice-dollar',
6109 'order' => 'file-invoice',
6110 'error' => 'exclamation-triangle',
6111 'warning' => 'exclamation-triangle',
6112 'other' => 'square',
6113 'playdisabled' => 'play',
6114 'pdf' => 'file-pdf',
6115 'poll' => 'check-double',
6116 'pos' => 'cash-register',
6117 'preview' => 'binoculars',
6118 'project' => 'project-diagram',
6119 'projectpub' => 'project-diagram',
6120 'projecttask' => 'tasks',
6121 'propal' => 'file-signature',
6122 'proposal' => 'file-signature',
6123 'partnership' => 'handshake',
6124 'payment' => 'money-check-alt',
6125 'payment_vat' => 'money-check-alt',
6126 'pictoconfirm' => 'check-square',
6127 'phoning' => 'phone',
6128 'phoning_mobile' => 'mobile-alt',
6129 'phoning_fax' => 'fax',
6130 'previous' => 'arrow-alt-circle-left',
6131 'printer' => 'print',
6132 'product' => 'cube',
6133 'puce' => 'angle-right',
6134 'recent' => 'check-square',
6135 'reception' => 'dolly',
6136 'recruitmentjobposition' => 'id-card-alt',
6137 'recruitmentcandidature' => 'id-badge',
6138 'resize' => 'crop',
6139 'supplier_order' => 'dol-order_supplier',
6140 'supplier_proposal' => 'file-signature',
6141 'refresh' => 'redo',
6142 'region' => 'map-marked',
6143 'replacement' => 'exchange-alt',
6144 'resource' => 'laptop-house',
6145 'recurring' => 'history',
6146 'service' => 'concierge-bell',
6147 'skill' => 'shapes',
6148 'state' => 'map-marked-alt',
6149 'security' => 'key',
6150 'salary' => 'wallet',
6151 'shipment' => 'dolly',
6152 'stock' => 'box-open',
6153 'stats' => 'chart-bar',
6154 'split' => 'code-branch',
6155 'status' => 'stop-circle',
6156 'stripe' => 'stripe-s',
6157 'supplier' => 'building',
6158 'technic' => 'cogs',
6159 'tick' => 'check',
6160 'timespent' => 'clock',
6161 'title_setup' => 'tools',
6162 'title_accountancy' => 'money-check-alt',
6163 'title_bank' => 'university',
6164 'title_hrm' => 'umbrella-beach',
6165 'title_agenda' => 'calendar-alt',
6166 'uncheck' => 'times',
6167 'uparrow' => 'share',
6168 'url' => 'external-link-alt',
6169 'vat' => 'money-check-alt',
6170 'vcard' => 'arrow-alt-circle-down',
6171 'jabber' => 'comment',
6172 'website' => 'globe-americas',
6173 'workstation' => 'pallet',
6174 'webhook' => 'bullseye',
6175 'world' => 'globe',
6176 'private' => 'user-lock',
6177 'conferenceorbooth' => 'chalkboard-teacher',
6178 'eventorganization' => 'project-diagram',
6179 'webportal' => 'door-open'
6180 );
6181
6182 if ($conf->currency == 'EUR') {
6183 $arrayconvpictotofa['currency'] = 'euro-sign';
6184 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
6185 } else {
6186 $arrayconvpictotofa['currency'] = 'dollar-sign';
6187 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
6188 }
6189 } else {
6190 $arrayconvpictotofa = array();
6191 }
6192
6193 return $arrayconvpictotofa;
6194}
6195
6196
6211function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
6212{
6213 if (strpos($picto, '^') === 0) {
6214 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6215 } else {
6216 return img_picto($titlealt, 'object_' . $picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6217 }
6218}
6219
6231function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
6232{
6233 global $conf;
6234
6235 if (is_numeric($picto)) {
6236 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
6237 //$picto = $leveltopicto[$picto];
6238 return '<i class="fa fa-weather-level' . $picto . '"></i>';
6239 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6240 $picto .= '.png';
6241 }
6242
6243 $path = DOL_URL_ROOT . '/theme/' . $conf->theme . '/img/weather/' . $picto;
6244
6245 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
6246}
6247
6259function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
6260{
6261 global $conf;
6262
6263 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6264 $picto .= '.png';
6265 }
6266
6267 if ($pictoisfullpath) {
6268 $path = $picto;
6269 } else {
6270 $path = DOL_URL_ROOT . '/theme/common/' . $picto;
6271
6272 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
6273 $themepath = DOL_DOCUMENT_ROOT . '/theme/' . $conf->theme . '/img/' . $picto;
6274
6275 if (file_exists($themepath)) {
6276 $path = $themepath;
6277 }
6278 }
6279 }
6280
6281 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
6282}
6283
6297function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
6298{
6299 global $langs;
6300
6301 if (empty($titlealt) || $titlealt == 'default') {
6302 if ($numaction == '-1' || $numaction == 'ST_NO') {
6303 $numaction = -1;
6304 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
6305 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
6306 $numaction = 0;
6307 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
6308 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
6309 $numaction = 1;
6310 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
6311 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
6312 $numaction = 2;
6313 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
6314 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
6315 $numaction = 3;
6316 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
6317 } else {
6318 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus ' . $numaction);
6319 $numaction = 0;
6320 }
6321 }
6322 if (!is_numeric($numaction)) {
6323 $numaction = 0;
6324 }
6325
6326 return img_picto($titlealt, (empty($picto) ? 'stcomm' . $numaction . '.png' : $picto), $moreatt);
6327}
6328
6336function img_edit_add($titlealt = 'default', $other = '')
6337{
6338 global $langs;
6339
6340 if ($titlealt == 'default') {
6341 $titlealt = $langs->trans('Add');
6342 }
6343
6344 return img_picto($titlealt, 'edit_add.png', $other);
6345}
6353function img_edit_remove($titlealt = 'default', $other = '')
6354{
6355 global $langs;
6356
6357 if ($titlealt == 'default') {
6358 $titlealt = $langs->trans('Remove');
6359 }
6360
6361 return img_picto($titlealt, 'edit_remove.png', $other);
6362}
6363
6372function img_edit($titlealt = 'default', $float = 0, $other = '')
6373{
6374 global $langs;
6375
6376 if ($titlealt == 'default') {
6377 $titlealt = $langs->trans('Modify');
6378 }
6379
6380 return img_picto($titlealt, 'edit', ($float ? 'style="float: ' . ($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right') . '"' : "") . ($other ? ' ' . $other : ''));
6381}
6382
6391function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
6392{
6393 global $langs;
6394
6395 if ($titlealt == 'default') {
6396 $titlealt = $langs->trans('View');
6397 }
6398
6399 $moreatt = ($float ? 'style="float: right" ' : '') . $other;
6400
6401 return img_picto($titlealt, 'eye', $moreatt);
6402}
6403
6412function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
6413{
6414 global $langs;
6415
6416 if ($titlealt == 'default') {
6417 $titlealt = $langs->trans('Delete');
6418 }
6419
6420 return img_picto($titlealt, 'delete', $other, 0, 0, 0, '', $morecss);
6421}
6422
6430function img_printer($titlealt = "default", $other = '')
6431{
6432 global $langs;
6433 if ($titlealt == "default") {
6434 $titlealt = $langs->trans("Print");
6435 }
6436 return img_picto($titlealt, 'printer', $other);
6437}
6438
6446function img_split($titlealt = 'default', $other = 'class="pictosplit"')
6447{
6448 global $langs;
6449
6450 if ($titlealt == 'default') {
6451 $titlealt = $langs->trans('Split');
6452 }
6453
6454 return img_picto($titlealt, 'split', $other);
6455}
6456
6464function img_help($usehelpcursor = 1, $usealttitle = 1)
6465{
6466 global $langs;
6467
6468 if ($usealttitle) {
6469 if (is_string($usealttitle)) {
6470 $usealttitle = dol_escape_htmltag($usealttitle);
6471 } else {
6472 $usealttitle = $langs->trans('Info');
6473 }
6474 }
6475
6476 return img_picto($usealttitle, 'info', 'style="vertical-align: middle;' . ($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')) . '"');
6477}
6478
6485function img_info($titlealt = 'default')
6486{
6487 global $langs;
6488
6489 if ($titlealt == 'default') {
6490 $titlealt = $langs->trans('Informations');
6491 }
6492
6493 return img_picto($titlealt, 'info', 'style="vertical-align: middle;"');
6494}
6495
6504function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6505{
6506 global $langs;
6507
6508 if ($titlealt == 'default') {
6509 $titlealt = $langs->trans('Warning');
6510 }
6511
6512 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6513 return img_picto($titlealt, 'warning', 'class="' . $morecss . '"' . ($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' ' . $moreatt) : ''));
6514}
6515
6522function img_error($titlealt = 'default')
6523{
6524 global $langs;
6525
6526 if ($titlealt == 'default') {
6527 $titlealt = $langs->trans('Error');
6528 }
6529
6530 return img_picto($titlealt, 'error');
6531}
6532
6540function img_next($titlealt = 'default', $moreatt = '')
6541{
6542 global $langs;
6543
6544 if ($titlealt == 'default') {
6545 $titlealt = $langs->trans('Next');
6546 }
6547
6548 //return img_picto($titlealt, 'next.png', $moreatt);
6549 return '<span class="fa fa-chevron-right paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6550}
6551
6559function img_previous($titlealt = 'default', $moreatt = '')
6560{
6561 global $langs;
6562
6563 if ($titlealt == 'default') {
6564 $titlealt = $langs->trans('Previous');
6565 }
6566
6567 //return img_picto($titlealt, 'previous.png', $moreatt);
6568 return '<span class="fa fa-chevron-left paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6569}
6570
6579function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6580{
6581 global $langs;
6582
6583 if ($titlealt == 'default') {
6584 $titlealt = $langs->trans('Down');
6585 }
6586
6587 return img_picto($titlealt, ($selected ? '1downarrow_selected' : '1downarrow'), 'class="imgdown' . ($moreclass ? " " . $moreclass : "") . '"');
6588}
6589
6598function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6599{
6600 global $langs;
6601
6602 if ($titlealt == 'default') {
6603 $titlealt = $langs->trans('Up');
6604 }
6605
6606 return img_picto($titlealt, ($selected ? '1uparrow_selected' : '1uparrow'), 'class="imgup' . ($moreclass ? " " . $moreclass : "") . '"');
6607}
6608
6617function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6618{
6619 global $langs;
6620
6621 if ($titlealt == 'default') {
6622 $titlealt = $langs->trans('Left');
6623 }
6624
6625 return img_picto($titlealt, ($selected ? '1leftarrow_selected' : '1leftarrow'), $moreatt);
6626}
6627
6636function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6637{
6638 global $langs;
6639
6640 if ($titlealt == 'default') {
6641 $titlealt = $langs->trans('Right');
6642 }
6643
6644 return img_picto($titlealt, ($selected ? '1rightarrow_selected' : '1rightarrow'), $moreatt);
6645}
6646
6654function img_allow($allow, $titlealt = 'default')
6655{
6656 global $langs;
6657
6658 if ($titlealt == 'default') {
6659 $titlealt = $langs->trans('Active');
6660 }
6661
6662 if ($allow == 1) {
6663 return img_picto($titlealt, 'tick');
6664 }
6665
6666 return '-';
6667}
6668
6676function img_credit_card($brand, $morecss = 'fa-2x inline-block valignmiddle')
6677{
6678 if (is_null($morecss)) {
6679 $morecss = 'fa-2x';
6680 }
6681
6682 if ($brand == 'visa' || $brand == 'Visa') {
6683 $brand = 'cc-visa';
6684 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6685 $brand = 'cc-mastercard';
6686 } elseif ($brand == 'amex' || $brand == 'American Express') {
6687 $brand = 'cc-amex';
6688 } elseif ($brand == 'discover' || $brand == 'Discover') {
6689 $brand = 'cc-discover';
6690 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6691 $brand = 'cc-jcb';
6692 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6693 $brand = 'cc-diners-club';
6694 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6695 $brand = 'credit-card';
6696 }
6697
6698 return '<span class="fa fa-' . $brand . ' fa-fw' . ($morecss ? ' ' . $morecss : '') . '"></span>';
6699}
6700
6709function img_mime($file, $titlealt = '', $morecss = '')
6710{
6711 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
6712
6713 $mimetype = dol_mimetype($file, '', 1);
6714 //$mimeimg = dol_mimetype($file, '', 2);
6715 $mimefa = dol_mimetype($file, '', 4);
6716
6717 if (empty($titlealt)) {
6718 $titlealt = 'Mime type: ' . $mimetype;
6719 }
6720
6721 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6722 return '<i class="fa fa-' . $mimefa . ' ' . (preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ') . ($morecss ? ' ' . $morecss : '') . '"' . ($titlealt ? ' title="' . dolPrintHTMLForAttribute($titlealt) . '"' : '') . '></i>';
6723}
6724
6725
6733function img_search($titlealt = 'default', $other = '')
6734{
6735 global $langs;
6736
6737 if ($titlealt == 'default') {
6738 $titlealt = $langs->trans('Search');
6739 }
6740
6741 $img = img_picto($titlealt, 'search', $other, 0, 1);
6742
6743 $input = '<input type="image" class="liste_titre" name="button_search" src="' . $img . '" ';
6744 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6745
6746 return $input;
6747}
6748
6756function img_searchclear($titlealt = 'default', $other = '')
6757{
6758 global $langs;
6759
6760 if ($titlealt == 'default') {
6761 $titlealt = $langs->trans('Search');
6762 }
6763
6764 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6765
6766 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="' . $img . '" ';
6767 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6768
6769 return $input;
6770}
6771
6784function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6785{
6786 global $conf, $langs;
6787
6788 if ($infoonimgalt) {
6789 $result = img_picto($text, 'info', 'class="' . ($morecss ? ' ' . $morecss : '') . '"');
6790 } else {
6791 if (empty($conf->use_javascript_ajax)) {
6792 $textfordropdown = '';
6793 }
6794
6795 $class = (empty($admin) ? 'undefined' : ((string) $admin == '1' ? 'info' : $admin));
6796 $fa = 'info-circle';
6797 if ($picto == 'warning') {
6798 $fa = 'exclamation-triangle';
6799 }
6800 $result = ($nodiv ? '' : '<div class="wordbreak ' . $class . ($morecss ? ' ' . $morecss : '') . ($textfordropdown ? ' hidden' : '') . '">');
6801 $result .= img_picto((string) $admin ? $langs->trans('InfoAdmin') : $langs->trans('Note'), $fa);
6802 $result .= ' ';
6803 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6804 $result .= ($nodiv ? '' : '</div>');
6805
6806 if ($textfordropdown) {
6807 $tmpresult = '<span class="' . $class . 'text opacitymedium cursorpointer">' . $langs->trans($textfordropdown) . ' ' . img_picto($langs->trans($textfordropdown), '1downarrow') . '</span>';
6808 $tmpresult .= '<script nonce="' . getNonce() . '" type="text/javascript">
6809 jQuery(document).ready(function() {
6810 jQuery(".' . $class . 'text").click(function() {
6811 console.log("toggle text");
6812 jQuery(".' . $class . '").toggle();
6813 });
6814 });
6815 </script>';
6816
6817 $result = $tmpresult . $result;
6818 }
6819 }
6820
6821 return $result;
6822}
6823
6824
6836function dol_print_error($db = null, $error = '', $errors = null)
6837{
6838 global $conf, $langs, $user, $argv;
6839 global $dolibarr_main_prod;
6840
6841 $out = '';
6842 $syslog = '';
6843
6844 // If error occurs before the $lang object was loaded
6845 if (!$langs) {
6846 require_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
6847 $langs = new Translate('', $conf);
6848 $langs->load("main");
6849 }
6850
6851 // Load translation files required by the error messages
6852 $langs->loadLangs(array('main', 'errors'));
6853
6854 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6855 $out .= $langs->trans("DolibarrHasDetectedError") . ".<br>\n";
6856 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6857 $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";
6858 }
6859 $out .= $langs->trans("InformationToHelpDiagnose") . ":<br>\n";
6860
6861 $out .= "<b>" . $langs->trans("Date") . ":</b> " . dol_print_date(time(), 'dayhourlog') . "<br>\n";
6862 $out .= "<b>" . $langs->trans("Dolibarr") . ":</b> " . DOL_VERSION . " - https://www.dolibarr.org<br>\n";
6863 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6864 $out .= "<b>" . $langs->trans("LevelOfFeature") . ":</b> " . getDolGlobalInt('MAIN_FEATURES_LEVEL') . "<br>\n";
6865 }
6866 if ($user instanceof User) {
6867 $out .= "<b>" . $langs->trans("Login") . ":</b> " . $user->login . "<br>\n";
6868 }
6869 if (function_exists("phpversion")) {
6870 $out .= "<b>" . $langs->trans("PHP") . ":</b> " . phpversion() . "<br>\n";
6871 }
6872 $out .= "<b>" . $langs->trans("Server") . ":</b> " . (isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '') . "<br>\n";
6873 if (function_exists("php_uname")) {
6874 $out .= "<b>" . $langs->trans("OS") . ":</b> " . php_uname() . "<br>\n";
6875 }
6876 $out .= "<b>" . $langs->trans("UserAgent") . ":</b> " . (isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '') . "<br>\n";
6877 $out .= "<br>\n";
6878 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6879 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6880 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6881 $out .= "<br>\n";
6882 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6883 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6884 } else { // Mode CLI
6885 $out .= '> ' . $langs->transnoentities("ErrorInternalErrorDetected") . ":\n" . $argv[0] . "\n";
6886 $syslog .= "pid=" . dol_getmypid();
6887 }
6888
6889 if (!empty($conf->modules)) {
6890 $out .= "<b>" . $langs->trans("Modules") . ":</b> " . implode(', ', $conf->modules) . "<br>\n";
6891 }
6892
6893 if (is_object($db)) {
6894 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6895 $out .= "<b>" . $langs->trans("DatabaseTypeManager") . ":</b> " . $db->type . "<br>\n";
6896 $lastqueryerror = $db->lastqueryerror();
6897 if (!utf8_check($lastqueryerror)) {
6898 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6899 }
6900 $out .= "<b>" . $langs->trans("RequestLastAccessInError") . ":</b> " . ($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6901 $out .= "<b>" . $langs->trans("ReturnCodeLastAccessInError") . ":</b> " . ($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6902 $out .= "<b>" . $langs->trans("InformationLastAccessInError") . ":</b> " . ($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6903 $out .= "<br>\n";
6904 } else { // Mode CLI
6905 // No dol_escape_htmltag for output, we are in CLI mode
6906 $out .= '> ' . $langs->transnoentities("DatabaseTypeManager") . ":\n" . $db->type . "\n";
6907 $out .= '> ' . $langs->transnoentities("RequestLastAccessInError") . ":\n" . ($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6908 $out .= '> ' . $langs->transnoentities("ReturnCodeLastAccessInError") . ":\n" . ($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6909 $out .= '> ' . $langs->transnoentities("InformationLastAccessInError") . ":\n" . ($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6910 }
6911 $syslog .= ", sql=" . $db->lastquery();
6912 $syslog .= ", db_error=" . $db->lasterror();
6913 }
6914
6915 if ($error || $errors) {
6916 // Merge all into $errors array
6917 if (is_array($error) && is_array($errors)) {
6918 $errors = array_merge($error, $errors);
6919 } elseif (is_array($error)) { // deprecated, use second parameters
6920 $errors = $error;
6921 } elseif (is_array($errors) && !empty($error)) {
6922 $errors = array_merge(array($error), $errors);
6923 } elseif (!empty($error)) {
6924 $errors = array_merge(array($error), array($errors));
6925 }
6926
6927 $langs->load("errors");
6928
6929 foreach ($errors as $msg) {
6930 if (empty($msg)) {
6931 continue;
6932 }
6933 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6934 $out .= "<b>" . $langs->trans("Message") . ":</b> " . dol_escape_htmltag($msg) . "<br>\n";
6935 } else { // Mode CLI
6936 $out .= '> ' . $langs->transnoentities("Message") . ":\n" . $msg . "\n";
6937 }
6938 $syslog .= ", msg=" . $msg;
6939 }
6940 }
6941 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6942 xdebug_print_function_stack();
6943 $out .= '<b>XDebug information:</b>' . "<br>\n";
6944 $out .= 'File: ' . xdebug_call_file() . "<br>\n";
6945 $out .= 'Line: ' . xdebug_call_line() . "<br>\n";
6946 $out .= 'Function: ' . xdebug_call_function() . "<br>\n";
6947 $out .= "<br>\n";
6948 }
6949
6950 // Return a http header with error code if possible
6951 if (!headers_sent()) {
6952 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6953 top_httphead();
6954 }
6955 //http_response_code(500); // If we use 500, message is not output with some command line tools
6956 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
6957 }
6958
6959 if (empty($dolibarr_main_prod)) {
6960 print $out;
6961 } else {
6962 if (empty($langs->defaultlang)) {
6963 $langs->setDefaultLang();
6964 }
6965 $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.
6966 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6967 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";
6968 print $langs->trans("DolibarrHasDetectedError") . '. ';
6969 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6970 if (!defined("MAIN_CORE_ERROR")) {
6971 define("MAIN_CORE_ERROR", 1);
6972 }
6973 }
6974
6975 dol_syslog("Error " . $syslog, LOG_ERR);
6976}
6977
6988function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6989{
6990 global $langs;
6991
6992 if (empty($email)) {
6993 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6994 }
6995
6996 $langs->load("errors");
6997 $now = dol_now();
6998
6999 print '<br><div class="center login_main_message"><div class="' . $morecss . '">';
7000 print $langs->trans("ErrorContactEMail", $email, $prefixcode . '-' . dol_print_date($now, '%Y%m%d%H%M%S'));
7001 if ($errormessage) {
7002 print '<br><br>' . $errormessage;
7003 }
7004 if (is_array($errormessages) && count($errormessages)) {
7005 foreach ($errormessages as $mesgtoshow) {
7006 print '<br><br>' . $mesgtoshow;
7007 }
7008 }
7009 print '</div></div>';
7010}
7011
7028function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
7029{
7030 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
7031}
7032
7051function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
7052{
7053 global $langs, $form;
7054 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
7055
7056 if ($moreattrib == 'class="right"') {
7057 $prefix .= 'right '; // For backward compatibility
7058 }
7059
7060 $tooltip = (string) $tooltip; // In case $tooltip is null
7061
7062 $sortorder = strtoupper((string) $sortorder);
7063 $out = '';
7064 $sortimg = '';
7065
7066 $tag = 'th';
7067 if ($thead == 2) {
7068 $tag = 'div';
7069 }
7070
7071 $tmpsortfield = explode(',', (string) $sortfield);
7072 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
7073 $tmpfield = explode(',', $field);
7074 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
7075
7076 if (strpos((string) $tooltip, ':') !== false) {
7077 $tmptooltip = explode(':', (string) $tooltip);
7078 } else {
7079 $tmptooltip = array($tooltip);
7080 }
7081
7082 $wrapcolumntitle = (empty($forcenowrapcolumntitle) || (!empty($tmptooltip[2]) && $tmptooltip[2] == '-1'));
7083
7084 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle) {
7085 $prefix = 'wrapcolumntitle ' . $prefix;
7086 }
7087
7088 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
7089 // If field is used as sort criteria we use a specific css class liste_titre_sel
7090 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
7091 $liste_titre = 'liste_titre';
7092 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
7093 $liste_titre = 'liste_titre_sel';
7094 }
7095
7096 $tagstart = '<' . $tag . ' class="' . $prefix . $liste_titre . '" ' . $moreattrib;
7097 //$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)).'"' : '');
7098 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle && !dol_textishtml($name)) ? ' title="' . dolPrintHTMLForAttribute($langs->trans($name)) . '"' : '';
7099 $tagstart .= '>';
7100
7101 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7102 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7103 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7104 $options = preg_replace('/&+/i', '&', $options);
7105 if (!preg_match('/^&/', $options)) {
7106 $options = '&' . $options;
7107 }
7108
7109 $sortordertouseinlink = '';
7110 if ($field1 != $sortfield1) { // We are on another field than current sorted field
7111 if (preg_match('/^DESC/i', $sortorder)) {
7112 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7113 } else { // We reverse the var $sortordertouseinlink
7114 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7115 }
7116 } else { // We are on field that is the first current sorting criteria
7117 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
7118 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7119 } else {
7120 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7121 }
7122 }
7123 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
7124 $out .= '<a class="reposition" href="' . dolBuildUrl($file, ['sortfield' => $field, 'sortorder' => $sortordertouseinlink, 'begin' => $begin]) . $options . '"';
7125 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
7126 $out .= '>';
7127 }
7128 if ($tooltip && $tmptooltip[0]) {
7129 // You can also use 'TranslationString:[keyfortooltiponclick]:[tooltipdirection]' for a tooltip on click or to change tooltip position.
7130 $out .= $form->textwithpicto($langs->trans((string) $name), $langs->trans((string) $tmptooltip[0]), (empty($tmptooltip[2]) ? '1' : $tmptooltip[2]), 'help', ((!empty($tmptooltip[2]) && $tmptooltip[2] == '-1') ? 'paddingrightonly' : ''), 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_' . str_replace('.', '_', $field) . '_' . $tmptooltip[1]));
7131 } else {
7132 $out .= $langs->trans((string) $name);
7133 }
7134
7135 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7136 $out .= '</a>';
7137 }
7138
7139 if (empty($thead) && $field) { // If this is a sort field
7140 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7141 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7142 $options = preg_replace('/&+/i', '&', $options);
7143 if (!preg_match('/^&/', $options)) {
7144 $options = '&' . $options;
7145 }
7146
7147 if (!$sortorder || ($field1 != $sortfield1)) {
7148 // Nothing
7149 } else {
7150 if (preg_match('/^DESC/', $sortorder)) {
7151 $sortimg .= '<span class="nowrap">' . img_up("Z-A", 0, 'paddingright') . '</span>';
7152 }
7153 if (preg_match('/^ASC/', $sortorder)) {
7154 $sortimg .= '<span class="nowrap">' . img_down("A-Z", 0, 'paddingright') . '</span>';
7155 }
7156 }
7157 }
7158
7159 $tagend = '</' . $tag . '>';
7160
7161 $out = $tagstart . $sortimg . $out . $tagend;
7162
7163 return $out;
7164}
7165
7174function print_titre($title)
7175{
7176 dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
7177
7178 print '<div class="titre">' . $title . '</div>';
7179}
7180
7192function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
7193{
7194 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
7195}
7196
7211function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '', $morecssonpicto = 'widthpictotitle')
7212{
7213 $return = '';
7214
7215 if ($picto == 'setup') {
7216 $picto = 'generic';
7217 }
7218
7219 $return .= "\n";
7220 $return .= '<table ' . ($id ? 'id="' . $id . '" ' : '') . 'class="centpercent notopnoleftnoright table-fiche-title' . ($morecssontable ? ' ' . $morecssontable : '') . '">'; // margin bottom must be same than into print_barre_list
7221 $return .= '<tr class="toptitle">';
7222 if ($picto) {
7223 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">' . img_picto('', $picto, 'class="valignmiddle pictotitle'.($morecssonpicto ? ' '.$morecssonpicto : '').'"', $pictoisfullpath) . '</td>';
7224 }
7225 $return .= '<td class="nobordernopadding valignmiddle col-title">';
7226 $return .= '<div class="titre inline-block">';
7227 $return .= '<span class="inline-block valignmiddle">' . $title . '</span>'; // $title is already HTML sanitized content
7228 $return .= '</div>';
7229 $return .= '</td>';
7230 if (dol_strlen($morehtmlcenter)) {
7231 $return .= '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7232 }
7233 if (dol_strlen($morehtmlright)) {
7234 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">' . $morehtmlright . '</td>';
7235 }
7236 $return .= '</tr></table>' . "\n";
7237
7238 return $return;
7239}
7240
7264function print_barre_liste($title, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $selectlimitsuffix = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
7265{
7266 global $conf, $langs;
7267
7268 $savlimit = $limit;
7269 $savtotalnboflines = $totalnboflines;
7270 if (is_numeric($totalnboflines)) {
7271 $totalnboflines = abs($totalnboflines);
7272 }
7273
7274 // Detect if there is a subtitle
7275 $subtitle = '';
7276 $tmparray = preg_split('/<br>/i', $title, 2);
7277 if (!empty($tmparray[1])) {
7278 $title = $tmparray[0];
7279 $subtitle = $tmparray[1];
7280 }
7281
7282 $page = (int) $page;
7283
7284 if ($picto == 'setup') {
7285 $picto = 'title_setup';
7286 }
7287 if (($conf->browser->name == 'ie') && $picto == 'generic') {
7288 $picto = 'title.gif';
7289 }
7290 if ($limit < 0) {
7291 $limit = $conf->liste_limit;
7292 }
7293
7294 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
7295 $nextpage = 1;
7296 } else {
7297 $nextpage = 0;
7298 }
7299 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
7300
7301 print "\n";
7302 print "<!-- Begin print_barre_liste -->\n";
7303 print '<table class="centpercent notopnoleftnoright table-fiche-title' . ($morecss ? ' ' . $morecss : '') . '">';
7304 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
7305
7306 // Left
7307
7308 if ($picto && $title) {
7309 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
7310 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
7311 print '</td>';
7312 }
7313
7314 print '<td class="nobordernopadding valignmiddle col-title">';
7315 print '<div class="titre inline-block">';
7316 print '<span class="inline-block valignmiddle print-barre-liste">' . $title . '</span>'; // $title may contains HTML like a combo list from page consumption.php, so we do not use dolPrintLabel here()
7317 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
7318 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="' . $langs->trans("NbRecordQualified") . '">(' . $totalnboflines . ')</span>';
7319 }
7320 print '</div>';
7321 if (!empty($subtitle)) {
7322 print '<br><div class="subtitle inline-block hideonsmartphone">' . $subtitle . '</div>';
7323 }
7324 print '</td>';
7325
7326 // Center
7327 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
7328 print '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7329 }
7330
7331 // Right
7332 print '<td class="nobordernopadding valignmiddle right col-right">';
7333 print '<input type="hidden" name="pageplusoneold" value="' . ((int) $page + 1) . '">';
7334 $query = [];
7335 parse_str($options, $query);
7336 if ($sortfield) {
7337 $query += ['sortfield' => $sortfield];
7338 }
7339 if ($sortorder) {
7340 $query += ['sortorder' => $sortorder];
7341 }
7342
7343 $options = '&' . http_build_query($query);
7344 if ($page) {
7345 $query = array_merge($query, ['page' => $page]);
7346 }
7347 // Show navigation bar
7348 $pagelist = '';
7349 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
7350 if ($totalnboflines) { // If we know total nb of lines
7351 // Define nb of extra page links before and after selected page + ... + first or last
7352 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
7353
7354 if ($limit > 0) {
7355 $nbpages = ceil($totalnboflines / $limit);
7356 } else {
7357 $nbpages = 1;
7358 }
7359 $cpt = ($page - $maxnbofpage);
7360 if ($cpt < 0) {
7361 $cpt = 0;
7362 }
7363
7364 if ($cpt >= 1) {
7365 if (empty($pagenavastextinput)) {
7366 $query['page'] = 0;
7367 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">1</a></li>';
7368 if ($cpt > 2) {
7369 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7370 } elseif ($cpt == 2) {
7371 $query['page'] = 0;
7372 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">2</a></li>';
7373 }
7374 }
7375 }
7376
7377 do {
7378 if ($pagenavastextinput) {
7379 if ($cpt == $page) {
7380 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="' . ($totalnboflines > 100 ? 'width40' : 'width25') . ' center pageplusone heightofcombo" name="pageplusone" value="' . ($page + 1) . '"></li>';
7381 $pagelist .= '/';
7382 }
7383 } else {
7384 if ($cpt == $page) {
7385 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . '</span></li>';
7386 } else {
7387 $query['page'] = $cpt;
7388 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($cpt + 1) . '</a></li>';
7389 }
7390 }
7391 $cpt++;
7392 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
7393
7394 if (empty($pagenavastextinput)) {
7395 if ($cpt < $nbpages) {
7396 if ($cpt < $nbpages - 2) {
7397 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7398 } elseif ($cpt == $nbpages - 2) {
7399 $query['page'] = ($nbpages - 2);
7400 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($nbpages - 1) . '</a></li>';
7401 }
7402 $query['page'] = ($nbpages - 1);
7403 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7404 }
7405 } else {
7406 $query['page'] = ($nbpages - 1);
7407 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7408 }
7409 } else {
7410 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . "</li>";
7411 }
7412 }
7413
7414 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
7415 print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $selectlimitsuffix, $morehtmlrightbeforearrow, $hidenavigation); // output the div and ul for previous/last completed with page numbers into $pagelist
7416 }
7417
7418 // js to autoselect page field on focus
7419 if ($pagenavastextinput) {
7420 print ajax_autoselect('.pageplusone');
7421 }
7422
7423 print '</td>';
7424 print '</tr>';
7425
7426 print "</table>\n";
7427
7428 // Center
7429 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
7430 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">' . $morehtmlcenter . '</div>';
7431 }
7432
7433 print "<!-- End title -->\n\n";
7434}
7435
7452function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
7453{
7454 global $conf, $langs;
7455
7456 print '<div class="pagination"><ul>';
7457 if ($beforearrows) {
7458 print '<li class="paginationbeforearrows">';
7459 print $beforearrows;
7460 print '</li>';
7461 }
7462
7463 if (empty($hidenavigation)) {
7464 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
7465 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
7466 $pagesizechoices .= ',5000:5000';
7467 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
7468 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
7469 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
7470 //$pagesizechoices .= ',2:2';
7471 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
7472 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
7473 }
7474
7475 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7476 print '<li class="pagination">';
7477 print '<input onfocus="this.value=null;" onchange="this.blur();" class="flat selectlimit nopadding maxwidth75 right pageplusone" id="limit" name="limit" list="limitlist" title="' . dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")) . '" value="' . $limit . '">';
7478 print '<datalist id="limitlist">';
7479 } else {
7480 print '<li class="paginationcombolimit valignmiddle">';
7481 print '<select id="limit' . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix) . '" name="limit" class="flat selectlimit nopadding maxwidth75 center' . (is_numeric($selectlimitsuffix) ? '' : ' ' . $selectlimitsuffix) . '" title="' . dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")) . '">';
7482 }
7483 $tmpchoice = explode(',', $pagesizechoices);
7484 $tmpkey = $limit . ':' . $limit;
7485 if (!in_array($tmpkey, $tmpchoice)) {
7486 $tmpchoice[$tmpkey] = $tmpkey;
7487 }
7488 $tmpkey = $conf->liste_limit . ':' . $conf->liste_limit;
7489 if (!in_array($tmpkey, $tmpchoice)) {
7490 $tmpchoice[$tmpkey] = $tmpkey;
7491 }
7492 asort($tmpchoice, SORT_NUMERIC);
7493 foreach ($tmpchoice as $val) {
7494 $selected = '';
7495 $tmp = explode(':', $val);
7496 $key = $tmp[0];
7497 $val = $tmp[1];
7498 if ($key != '' && $val != '') {
7499 if ((int) $key == (int) $limit) {
7500 $selected = ' selected="selected"';
7501 }
7502 print '<option name="' . $key . '"' . $selected . '>' . dol_escape_htmltag($val) . '</option>' . "\n";
7503 }
7504 }
7505 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7506 print '</datalist>';
7507 } else {
7508 print '</select>';
7509 print ajax_combobox("limit" . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
7510 //print ajax_combobox("limit");
7511 }
7512
7513 if ($conf->use_javascript_ajax) {
7514 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
7515 <script>
7516 jQuery(document).ready(function () {
7517 jQuery(".selectlimit").change(function() {
7518 console.log("We change limit so we submit the form");
7519 $(this).parents(\'form:first\').submit();
7520 });
7521 });
7522 </script>
7523 ';
7524 }
7525 print '</li>';
7526 }
7527 if ($page > 0) {
7528 print '<li class="pagination paginationpage paginationpageleft"><a class="paginationprevious reposition" href="' . $file . '?page=' . ($page - 1) . $options . '"><i class="fa fa-chevron-left" title="' . dol_escape_htmltag($langs->trans("Previous")) . '"></i></a></li>';
7529 }
7530 if ($betweenarrows) {
7531 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7532 print $betweenarrows;
7533 print '<!--</div>-->';
7534 }
7535 if ($nextpage > 0) {
7536 print '<li class="pagination paginationpage paginationpageright"><a class="paginationnext reposition" href="' . $file . '?page=' . ($page + 1) . $options . '"><i class="fa fa-chevron-right" title="' . dol_escape_htmltag($langs->trans("Next")) . '"></i></a></li>';
7537 }
7538 if ($afterarrows) {
7539 print '<li class="paginationafterarrows">';
7540 print $afterarrows;
7541 print '</li>';
7542 }
7543 }
7544 print '</ul></div>' . "\n";
7545}
7546
7547
7559function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7560{
7561 $morelabel = '';
7562
7563 if (preg_match('/%/', $rate)) {
7564 $rate = str_replace('%', '', $rate);
7565 $addpercent = true;
7566 }
7567 $reg = array();
7568 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7569 $morelabel = ' (' . $reg[1] . ')';
7570 $rate = preg_replace('/\s*' . preg_quote($morelabel, '/') . '/', '', $rate);
7571 $morelabel = ' ' . ($html ? '<span class="opacitymedium">' : '') . '(' . $reg[1] . ')' . ($html ? '</span>' : '');
7572 }
7573 if (preg_match('/\*/', $rate)) {
7574 $rate = str_replace('*', '', $rate);
7575 $info_bits |= 1;
7576 }
7577
7578 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7579 if (!preg_match('/\//', $rate)) {
7580 $ret = price($rate, 0, '', 0, 0) . ($addpercent ? '%' : '');
7581 } else {
7582 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7583 $ret = $rate . ($addpercent ? '%' : '');
7584 }
7585 if (($info_bits & 1) && $usestarfornpr >= 0) {
7586 $ret .= ' *';
7587 }
7588 $ret .= $morelabel;
7589 return $ret;
7590}
7591
7592
7608function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7609{
7610 global $langs, $conf;
7611
7612 // Clean parameters
7613 if (empty($amount)) {
7614 $amount = 0; // To have a numeric value if amount not defined or = ''
7615 }
7616 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7617 if ($rounding == -1) {
7618 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7619 }
7620 $nbdecimal = $rounding;
7621
7622 if ($outlangs === 'none') {
7623 // Use international separators
7624 $dec = '.';
7625 $thousand = '';
7626 } else {
7627 // Output separators by default (french)
7628 $dec = ',';
7629 $thousand = ' ';
7630
7631 // If $outlangs not forced, we use use language
7632 if (!($outlangs instanceof Translate)) {
7633 $outlangs = $langs;
7634 }
7635
7636 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7637 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7638 }
7639 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7640 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7641 }
7642 if ($thousand == 'None') {
7643 $thousand = '';
7644 } elseif ($thousand == 'Space') {
7645 $thousand = ' ';
7646 }
7647 }
7648 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7649
7650 //print "amount=".$amount."-";
7651 $amount = str_replace(',', '.', $amount); // should be useless
7652 //print $amount."-";
7653 $data = explode('.', $amount);
7654 $decpart = isset($data[1]) ? $data[1] : '';
7655 $decpart = preg_replace('/0+$/i', '', $decpart); // Remove 0 at end of decimal part
7656 //print "decpart=".$decpart."<br>";
7657 $end = '';
7658
7659 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7660 if (dol_strlen($decpart) > $nbdecimal) {
7661 $nbdecimal = dol_strlen($decpart);
7662 }
7663
7664 // If nbdecimal is higher than max to show
7665 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7666 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7667 $nbdecimal = $nbdecimalmaxshown;
7668 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7669 // If output is truncated, we show ...
7670 $end = '...';
7671 }
7672 }
7673
7674 // If force rounding
7675 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7676 if ($forcerounding === 'MU') {
7677 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7678 } elseif ($forcerounding === 'MT') {
7679 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7680 } elseif ($forcerounding >= 0) {
7681 $nbdecimal = (int) $forcerounding;
7682 }
7683 }
7684
7685 // Format number
7686 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7687 // Add symbol of currency if requested
7688 $cursymbolbefore = $cursymbolafter = '';
7689 if ($currency_code && is_object($outlangs)) {
7690 if ($currency_code == 'auto') {
7691 $currency_code = $conf->currency;
7692 }
7693
7694 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7695 $listoflanguagesbefore = array('nl_NL');
7696 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7697 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7698 } else {
7699 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7700 $cursymbolafter .= ($tmpcur == $currency_code ? ' ' . $tmpcur : $tmpcur);
7701 }
7702 }
7703 $output = $cursymbolbefore . $output . $end . ($cursymbolafter ? ' ' : '') . $cursymbolafter;
7704 if ($form) {
7705 $output = preg_replace('/\s/', '&nbsp;', $output);
7706 $output = preg_replace('/\'/', '&#039;', $output);
7707 }
7708
7709 return $output;
7710}
7711
7737function price2num($amount, $rounding = '', $option = 0)
7738{
7739 global $langs;
7740
7741 // Clean parameters
7742 if (is_null($amount)) {
7743 $amount = '';
7744 }
7745
7746 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7747 // Numbers must be '1234.56'
7748 // Decimal delimiter for PHP and database SQL requests must be '.'
7749 $dec = ',';
7750 $thousand = ' ';
7751 if (is_null($langs)) { // $langs is not defined, we use english values.
7752 $dec = '.';
7753 $thousand = ',';
7754 } else {
7755 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7756 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7757 }
7758 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7759 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7760 }
7761 }
7762 if ($thousand == 'None') {
7763 $thousand = '';
7764 } elseif ($thousand == 'Space') {
7765 $thousand = ' ';
7766 }
7767 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7768
7769 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7770 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7771 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7772 if (!is_numeric($amount)) {
7773 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7774 }
7775
7776 if ($option == 2 && $thousand == '.' && preg_match('/\.(\d\d\d)$/', (string) $amount)) { // It means the . is used as a thousand separator and string come from input data, so 1.123 is 1123
7777 $amount = str_replace($thousand, '', $amount);
7778 }
7779
7780 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7781 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7782 // So if number was already a good number, it is converted into local Dolibarr setup.
7783 if (is_numeric($amount)) {
7784 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7785 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7786 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7787 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7788 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7789 }
7790 //print "QQ".$amount."<br>\n";
7791
7792 // Now make replaceents (the main goal of function)
7793
7794 if ($thousand != ',' && $thousand != '.') {
7795 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7796
7797 // REGEX: Find the integral and decimal parts.
7798 //
7799 // We require that the decimal point only appears once in $amount.
7800 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7801 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7802 // It covers two cases:
7803 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7804 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7805 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7806 $matches = array();
7807 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7808 $intPart = $matches['int'];
7809 $decPart = $matches['dec'];
7810
7811 // Remove all commas and dots from intPart
7812 $intPart = str_replace(['.', ','], '', $intPart);
7813
7814 // Combine intPart and decPart with a dot
7815 $amount = $intPart . $dec . $decPart;
7816 }
7817 }
7818
7819 $amount = str_replace(' ', '', $amount); // To avoid spaces
7820 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7821 $amount = str_replace($dec, '.', $amount);
7822
7823 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7824 }
7825 //print ' XX'.$amount.' '.$rounding;
7826
7827 // Now, $amount is a real PHP float number. We make a rounding if required.
7828 if ($rounding) {
7829 $nbofdectoround = '';
7830 if ($rounding == 'MU') {
7831 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7832 } elseif ($rounding == 'MT') {
7833 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7834 } elseif ($rounding == 'MS') {
7835 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK', 5);
7836 } elseif ($rounding == 'CU') {
7837 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7838 } elseif ($rounding == 'CT') {
7839 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7840 } elseif (is_numeric($rounding)) {
7841 $nbofdectoround = (int) $rounding;
7842 }
7843
7844 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7845 if (dol_strlen($nbofdectoround)) {
7846 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7847 } else {
7848 return 'ErrorBadParameterProvidedToFunction';
7849 }
7850 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7851
7852 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7853 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7854 if (is_numeric($amount)) {
7855 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7856 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7857 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7858 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7859 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7860 }
7861 //print "TT".$amount.'<br>';
7862
7863 // Always make replace because each math function (like round) replace
7864 // with local values and we want a number that has a SQL string format x.y
7865 if ($thousand != ',' && $thousand != '.') {
7866 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7867 }
7868
7869 $amount = str_replace(' ', '', $amount); // To avoid spaces
7870 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7871 $amount = str_replace($dec, '.', $amount);
7872
7873 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7874 }
7875
7876 return $amount;
7877}
7878
7891function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7892{
7893 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
7894
7895 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7896 $dimension *= 1000000;
7897 $unit -= 6;
7898 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7899 $dimension *= 1000;
7900 $unit -= 3;
7901 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7902 $dimension /= 1000000;
7903 $unit += 6;
7904 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7905 $dimension /= 1000;
7906 $unit += 3;
7907 }
7908 // Special case when we want output unit into pound or ounce
7909 /* TODO
7910 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7911 {
7912 $dimension = // convert dimension from standard unit into ounce or pound
7913 $unit = $forceunitoutput;
7914 }
7915 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7916 {
7917 $dimension = // convert dimension from standard unit into ounce or pound
7918 $unit = $forceunitoutput;
7919 }*/
7920
7921 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7922 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7923 $ret .= ' ' . measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
7924
7925 return $ret;
7926}
7927
7928
7941function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7942{
7943 global $db, $conf, $mysoc;
7944
7945 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7946 $thirdparty_seller = $mysoc;
7947 }
7948
7949 dol_syslog("get_localtax tva=" . $vatrate . " local=" . $local . " thirdparty_buyer id=" . (is_object($thirdparty_buyer) ? $thirdparty_buyer->id : '') . "/country_code=" . (is_object($thirdparty_buyer) ? $thirdparty_buyer->country_code : '') . " thirdparty_seller id=" . $thirdparty_seller->id . "/country_code=" . $thirdparty_seller->country_code . " thirdparty_seller localtax1_assuj=" . $thirdparty_seller->localtax1_assuj . " thirdparty_seller localtax2_assuj=" . $thirdparty_seller->localtax2_assuj);
7950
7951 $vatratecleaned = $vatrate;
7952 $reg = array();
7953 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7954 $vatratecleaned = trim($reg[1]);
7955 $vatratecode = $reg[2];
7956 }
7957
7958 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7959 {
7960 return 0;
7961 }*/
7962
7963 // Some test to guess with no need to make database access
7964 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7965 if ($local == 1) {
7966 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7967 return 0;
7968 }
7969 if ($thirdparty_seller->id == $mysoc->id) {
7970 if (!$thirdparty_buyer->localtax1_assuj) {
7971 return 0;
7972 }
7973 } else {
7974 if (!$thirdparty_seller->localtax1_assuj) {
7975 return 0;
7976 }
7977 }
7978 }
7979
7980 if ($local == 2) {
7981 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7982 if (!$mysoc->localtax2_assuj) {
7983 return 0; // If main vat is 0, IRPF may be different than 0.
7984 }
7985 if ($thirdparty_seller->id == $mysoc->id) {
7986 if (!$thirdparty_buyer->localtax2_assuj) {
7987 return 0;
7988 }
7989 } else {
7990 if (!$thirdparty_seller->localtax2_assuj) {
7991 return 0;
7992 }
7993 }
7994 }
7995 } else {
7996 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7997 return 0;
7998 }
7999 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
8000 return 0;
8001 }
8002 }
8003
8004 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
8005 if (in_array($mysoc->country_code, array('ES'))) {
8006 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
8007 }
8008
8009 // Search local taxes
8010 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
8011 if ($local == 1) {
8012 if ($thirdparty_seller != $mysoc) {
8013 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
8014 return $thirdparty_seller->localtax1_value;
8015 }
8016 } else { // i am the seller
8017 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
8018 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
8019 }
8020 }
8021 }
8022 if ($local == 2) {
8023 if ($thirdparty_seller != $mysoc) {
8024 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
8025 // TODO We should also return value defined on thirdparty only if defined
8026 return $thirdparty_seller->localtax2_value;
8027 }
8028 } else { // i am the seller
8029 if (in_array($mysoc->country_code, array('ES'))) {
8030 return $thirdparty_buyer->localtax2_value;
8031 } else {
8032 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
8033 }
8034 }
8035 }
8036 }
8037
8038 // By default, search value of local tax on line of common tax
8039 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
8040 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8041 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdparty_seller->country_code) . "'";
8042 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8043 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8044 if (!empty($vatratecode)) {
8045 $sql .= " AND t.code ='" . $db->escape($vatratecode) . "'"; // If we have the code, we use it in priority
8046 } else {
8047 $sql .= " AND t.recuperableonly = '" . $db->escape((string) $vatnpr) . "'";
8048 }
8049
8050 $resql = $db->query($sql);
8051
8052 if ($resql) {
8053 $obj = $db->fetch_object($resql);
8054 if ($obj) {
8055 if ($local == 1) {
8056 return $obj->localtax1;
8057 } elseif ($local == 2) {
8058 return $obj->localtax2;
8059 }
8060 }
8061 }
8062
8063 return 0;
8064}
8065
8066
8075function isOnlyOneLocalTax($local)
8076{
8077 $tax = get_localtax_by_third($local);
8078
8079 $valors = explode(":", $tax);
8080
8081 if (count($valors) > 1) {
8082 return false;
8083 } else {
8084 return true;
8085 }
8086}
8087
8094function get_localtax_by_third($local)
8095{
8096 global $db, $mysoc;
8097
8098 $sql = " SELECT t.localtax" . $local . " as localtax";
8099 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t INNER JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = t.fk_pays";
8100 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.active = 1 AND t.entity IN (" . getEntity('c_tva') . ") AND t.taux = (";
8101 $sql .= "SELECT MAX(tt.taux) FROM " . MAIN_DB_PREFIX . "c_tva as tt INNER JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = tt.fk_pays";
8102 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.entity IN (" . getEntity('c_tva') . ") AND tt.active = 1)";
8103 $sql .= " AND t.localtax" . $local . "_type <> '0'";
8104 $sql .= " ORDER BY t.rowid DESC";
8105
8106 $resql = $db->query($sql);
8107 if ($resql) {
8108 $obj = $db->fetch_object($resql);
8109 if ($obj) {
8110 return $obj->localtax;
8111 } else {
8112 return '0';
8113 }
8114 }
8115
8116 return 'Error';
8117}
8118
8119
8131function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
8132{
8133 global $db;
8134
8135 dol_syslog("getTaxesFromId vat id or rate = " . $vatrate);
8136
8137 // Search local taxes
8138 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
8139 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
8140 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8141 if ($firstparamisid) {
8142 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8143 } else {
8144 $vatratecleaned = $vatrate;
8145 $vatratecode = '';
8146 $reg = array();
8147 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
8148 $vatratecleaned = $reg[1];
8149 $vatratecode = $reg[2];
8150 }
8151
8152 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8153 /*if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // vat in spain use the buyer country ??
8154 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
8155 $sql .= " WHERE t.fk_pays = c.rowid";
8156 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8157 $sql .= " AND c.code = '" . $db->escape($buyer->country_code) . "'";
8158 } else {
8159 $sql .= " AND c.code = '" . $db->escape($seller->country_code) . "'";
8160 }
8161 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8162 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8163 if ($vatratecode) {
8164 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8165 }
8166 }
8167
8168 $resql = $db->query($sql);
8169 if ($resql) {
8170 $obj = $db->fetch_object($resql);
8171 if ($obj) {
8172 return array(
8173 'rowid' => $obj->rowid,
8174 'code' => $obj->code,
8175 'rate' => $obj->rate,
8176 'localtax1' => $obj->localtax1,
8177 'localtax1_type' => $obj->localtax1_type,
8178 'localtax2' => $obj->localtax2,
8179 'localtax2_type' => $obj->localtax2_type,
8180 'npr' => $obj->npr,
8181 'accountancy_code_sell' => $obj->accountancy_code_sell,
8182 'accountancy_code_buy' => $obj->accountancy_code_buy
8183 );
8184 } else {
8185 return array();
8186 }
8187 } else {
8188 dol_print_error($db);
8189 }
8190
8191 return array();
8192}
8193
8210function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
8211{
8212 global $db, $mysoc;
8213
8214 dol_syslog("getLocalTaxesFromRate vatrate=" . $vatrate . " local=" . $local);
8215
8216 // Search local taxes
8217 $sql = "SELECT t.taux as rate, t.code, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
8218 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8219 if ($firstparamisid) {
8220 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8221 } else {
8222 $vatratecleaned = $vatrate;
8223 $vatratecode = '';
8224 $reg = array();
8225 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
8226 $vatratecleaned = $reg[1];
8227 $vatratecode = $reg[2];
8228 }
8229
8230 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8231 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
8232 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
8233 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'"; // local tax in spain use the buyer country ??
8234 } else {
8235 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
8236 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'";
8237 }
8238 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8239 if ($vatratecode) {
8240 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8241 }
8242 }
8243
8244 $resql = $db->query($sql);
8245 if ($resql) {
8246 $obj = $db->fetch_object($resql);
8247
8248 if ($obj) {
8249 $vateratestring = $obj->rate . ($obj->code ? ' (' . $obj->code . ')' : '');
8250
8251 if ($local == 1) {
8252 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8253 } elseif ($local == 2) {
8254 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8255 } else {
8256 return array($obj->localtax1_type, get_localtax($vateratestring, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vateratestring, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8257 }
8258 }
8259 }
8260
8261 return array();
8262}
8263
8274function get_product_vat_for_country($idprod, $thirdpartytouseforcountry, $idprodfournprice = 0)
8275{
8276 global $db, $mysoc;
8277
8278 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8279
8280 $ret = 0;
8281 $found = 0;
8282
8283 if ($idprod > 0) {
8284 // Load product
8285 $product = new Product($db);
8286 $product->fetch($idprod);
8287
8288 if (($mysoc->country_code == $thirdpartytouseforcountry->country_code)
8289 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouseforcountry->country_code, array('FR', 'MC')))
8290 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouseforcountry->country_code, array('MQ', 'GP')))
8291 ) {
8292 // If country of thirdparty to consider is ours
8293 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
8294 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
8295 if ($result > 0) {
8296 $ret = $product->vatrate_supplier;
8297 if ($product->default_vat_code_supplier) {
8298 $ret .= ' (' . $product->default_vat_code_supplier . ')';
8299 }
8300 $found = 1;
8301 }
8302 }
8303 if (!$found) {
8304 $ret = $product->tva_tx; // Default sales vat of product
8305 if ($product->default_vat_code) {
8306 $ret .= ' (' . $product->default_vat_code . ')';
8307 }
8308 $found = 1;
8309 }
8310 } else {
8311 // TODO Read default product vat according to product and an other countrycode.
8312 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8313 }
8314 }
8315
8316 if (!$found) {
8317 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8318 // If vat of product for the country not found or not defined, we return the first rate found (sorting on use_default, then on higher vat of country).
8319 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
8320 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8321 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8322 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8323 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8324 $sql .= $db->plimit(1);
8325
8326 $resql = $db->query($sql);
8327 if ($resql) {
8328 $obj = $db->fetch_object($resql);
8329 if ($obj) {
8330 $ret = $obj->vat_rate;
8331 if ($obj->default_vat_code) {
8332 $ret .= ' (' . $obj->default_vat_code . ')';
8333 }
8334 }
8335 $db->free($resql);
8336 } else {
8337 dol_print_error($db);
8338 }
8339 } else {
8340 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
8341 // '1.23'
8342 // or '1.23 (CODE)'
8343 $defaulttx = '';
8344 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8345 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8346 }
8347 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8348 $defaultcode = $reg[1];
8349 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8350 }*/
8351
8352 $ret = $defaulttx;
8353 }
8354 }
8355
8356 dol_syslog("get_product_vat_for_country: ret=" . $ret);
8357
8358 return $ret;
8359}
8360
8370function get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
8371{
8372 global $db, $mysoc;
8373
8374 if (!class_exists('Product')) {
8375 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8376 }
8377
8378 $ret = 0;
8379 $found = 0;
8380
8381 if ($idprod > 0) {
8382 // Load product
8383 $product = new Product($db);
8384 $result = $product->fetch($idprod);
8385
8386 if ($mysoc->country_code == $thirdpartytouseforcountry->country_code) { // If selling country is ours
8387 /* Not defined yet, so we don't use this
8388 if ($local==1) $ret=$product->localtax1_tx;
8389 elseif ($local==2) $ret=$product->localtax2_tx;
8390 $found=1;
8391 */
8392 } else {
8393 // TODO Read default product vat according to product and another countrycode.
8394 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8395 }
8396 }
8397
8398 if (!$found) {
8399 // If vat of product for the country not found or not defined, we return higher vat of country.
8400 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
8401 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8402 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8403 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8404 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
8405 $sql .= $db->plimit(1);
8406
8407 $resql = $db->query($sql);
8408 if ($resql) {
8409 $obj = $db->fetch_object($resql);
8410 if ($obj) {
8411 if ($local == 1) {
8412 $ret = $obj->localtax1;
8413 } elseif ($local == 2) {
8414 $ret = $obj->localtax2;
8415 }
8416 }
8417 } else {
8418 dol_print_error($db);
8419 }
8420 }
8421
8422 dol_syslog("get_product_localtax_for_country: ret=" . $ret);
8423 return $ret;
8424}
8425
8444function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8445{
8446 global $mysoc, $db, $hookmanager;
8447
8448 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8449
8450 // Note: possible values for tva_assuj are 0/1 or franchise/reel
8451 $seller_use_vat = ((is_numeric($thirdparty_seller->tva_assuj) && !$thirdparty_seller->tva_assuj) || (!is_numeric($thirdparty_seller->tva_assuj) && $thirdparty_seller->tva_assuj == 'franchise')) ? 0 : 1;
8452
8453 if (empty($thirdparty_seller->country_code)) {
8454 $thirdparty_seller->country_code = $mysoc->country_code;
8455 }
8456 $seller_country_code = $thirdparty_seller->country_code;
8457 $seller_in_cee = isInEEC($thirdparty_seller);
8458
8459 if (empty($thirdparty_buyer->country_code)) {
8460 $thirdparty_buyer->country_code = $mysoc->country_code;
8461 }
8462 $buyer_country_code = $thirdparty_buyer->country_code;
8463 $buyer_in_cee = isInEEC($thirdparty_buyer);
8464
8465 dol_syslog("get_default_tva: seller use vat=" . $seller_use_vat . ", seller country=" . $seller_country_code . ", seller in cee=" . ((string) (int) $seller_in_cee) . ", buyer vat number=" . $thirdparty_buyer->tva_intra . " buyer country=" . $buyer_country_code . ", buyer state=" . $thirdparty_buyer->state_id . " buyer in cee=" . ((string) (int) $buyer_in_cee) . ", idprod=" . $idprod . ", idprodfournprice=" . $idprodfournprice . ", SERVICE_ARE_ECOMMERCE_200238EC=" . getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC'));
8466
8467 $vatvalue = 0;
8468 $vatrule = '';
8469
8470 // If services are eServices according to EU Council Directive 2002/38/EC (http://ec.europa.eu/taxation_customs/taxation/vat/traders/e-commerce/article_1610_en.htm)
8471 // we use the buyer VAT.
8472 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8473 if ($seller_in_cee && $buyer_in_cee) {
8474 $isacompany = $thirdparty_buyer->isACompany();
8475 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8476 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8477 if (!isValidVATID($thirdparty_buyer)) {
8478 $isacompany = 0;
8479 }
8480 }
8481
8482 if (!$isacompany) {
8483 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
8484 $vatrule = 'VATRULE 0';
8485 }
8486 }
8487 }
8488
8489 // If seller does not use VAT, default VAT is 0. End of rule.
8490 if (empty($vatrule) && !$seller_use_vat) {
8491 //print 'VATRULE 1';
8492 // TODO get the VAT Code of exemption asked into setup if country isInEEC (from an array list of possible
8493 // values like VATEX-EU-132-*, VATEX-FR-FRANCHISE, VATEX-EU-AE...
8494 // When we had recorded it, we also added a corresponding entry into table of vat code if it does not exists yet.
8495 // Here we test if entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-132-xx)'
8496 // If not, we add it and we return '0 (VATEX-EU-132-xx)'
8497 $vatvalue = 0;
8498 $vatrule = 'VATRULE 1';
8499 }
8500
8501 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
8502 if (empty($vatrule) && !empty($thirdparty_buyer->state_id)) {
8503 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
8504 $sql .= " FROM " . $db->prefix() . "c_tva as t";
8505 $sql .= " INNER JOIN " . $db->prefix() . "c_departements as d ON t.fk_department_buyer = d.rowid";
8506 $sql .= " WHERE d.rowid = " . ((int) $thirdparty_buyer->state_id);
8507 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8508
8509 $res = $db->query($sql);
8510 if ($res) {
8511 if ($db->num_rows($res)) {
8512 $obj = $db->fetch_object($res);
8513
8514 $vatvalue = $obj->vat_default_rate . ' (' . $obj->vat_default_code . ')';
8515 $vatrule = 'VATRULE 2';
8516 }
8517 $db->free($res);
8518 }
8519 }
8520
8521 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
8522 if (empty($vatrule) && (
8523 ($seller_country_code == $buyer_country_code)
8524 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
8525 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP'))) // We should be able to manage the case of MQ, GP, ... with a deicated vat rate at previous step.
8526 )) { // Warning ->country_code not always defined
8527 //print 'VATRULE 3';
8528 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8529
8530 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
8531 // Special case for india.
8532 //print 'VATRULE 3b';
8533 $reg = array();
8534 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
8535 // we must revert the C+S into I
8536 $tmpvat = str_replace("C+S", "I", $tmpvat);
8537 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8538 // we must revert the I into C+S
8539 $tmpvat = str_replace("I", "C+S", $tmpvat);
8540 }
8541 }
8542
8543 $vatvalue = $tmpvat;
8544 $vatrule = 'VATRULE 3b';
8545 }
8546
8547 // If (seller and buyer in the European Community) and (property sold = new means of transport such as car, boat, plane) then VAT by default = 0 (VAT must be paid by the buyer to the tax center of his country and not to the seller). End of rule.
8548 // 'VATRULE 4' - Not supported
8549
8550 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8551 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8552 if (empty($vatrule) && ($seller_in_cee && $buyer_in_cee)) {
8553 $isacompany = $thirdparty_buyer->isACompany();
8554 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8555 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8556 if (!isValidVATID($thirdparty_buyer)) {
8557 $isacompany = 0;
8558 }
8559 }
8560
8561 if (!$isacompany) {
8562 //print 'VATRULE 5';
8563 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8564 $vatrule = 'VATRULE 5';
8565 } else {
8566 //print 'VATRULE 6';
8567 // TODO This is the case of VAT exemption 'VATEX-EU-IC'
8568 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-IC)'
8569 // If not, we add it and we return '0 (VATEX-EU-IC)'
8570 $vatvalue = 0;
8571 $vatrule = 'VATRULE 6';
8572 }
8573 }
8574
8575 // If (seller in the European Community and buyer outside the European Community and private buyer) then VAT by default = VAT of the product sold. End of rule
8576 // I don't see any use case that need this rule, this case is on only if MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC set
8577 if (empty($vatrule) && getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8578 $isacompany = $thirdparty_buyer->isACompany();
8579 if (!$isacompany) {
8580 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8581 $vatrule = 'VATRULE extra';
8582 //print 'VATRULE extra';
8583 }
8584 }
8585
8586 // Otherwise the VAT proposed by default=0. End of rule.
8587 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8588 //print 'VATRULE 7';
8589 // TODO This is the case of VAT exemption 'VATEX-EU-G'
8590 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-xxx)'
8591 // If not, we add it and we return '0 (VATEX-xxx)'
8592
8593 // Allow an external module to bypass the calculation of prices
8594 $parameters = array('vatvalue' => $vatvalue, 'vatrule' => $vatrule);
8595 $tmpobject = null;
8596 $tmpaction = '';
8597 // @phan-suppress-next-line PhanPluginConstantVariableNull
8598 $reshook = $hookmanager->executeHooks('get_default_tva', $parameters, $tmpobject, $tmpaction); // @phan-suppress-current-line PhanPluginConstantVariableNull
8599 if ($reshook > 0 && !empty($hookmanager->resArray['vatvalue'])) {
8600 $vatvalue = $hookmanager->resArray['vatvalue'];
8601 $vatrule = $hookmanager->resArray['vatrule']; // For information
8602 }
8603
8604 return $vatvalue;
8605}
8606
8607
8618function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8619{
8620 global $db;
8621
8622 if ($idprodfournprice > 0) {
8623 if (!class_exists('ProductFournisseur')) {
8624 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
8625 }
8626 $prodprice = new ProductFournisseur($db);
8627 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8628 return $prodprice->fourn_tva_npr;
8629 } elseif ($idprod > 0) {
8630 if (!class_exists('Product')) {
8631 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8632 }
8633 $prod = new Product($db);
8634 $prod->fetch($idprod);
8635 return $prod->tva_npr;
8636 }
8637
8638 return 0;
8639}
8640
8654function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8655{
8656 global $mysoc;
8657
8658 if (!is_object($thirdparty_seller)) {
8659 return -1;
8660 }
8661 if (!is_object($thirdparty_buyer)) {
8662 return -1;
8663 }
8664
8665 if (empty($thirdparty_seller->country_code)) {
8666 $thirdparty_seller->country_code = $mysoc->country_code;
8667 }
8668 $seller_country_code = $thirdparty_seller->country_code;
8669 //$seller_in_cee = isInEEC($thirdparty_seller);
8670
8671 if (empty($thirdparty_buyer->country_code)) {
8672 $thirdparty_buyer->country_code = $mysoc->country_code;
8673 }
8674 $buyer_country_code = $thirdparty_buyer->country_code;
8675 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8676
8677 if ($local == 1) { // Localtax 1
8678 if ($mysoc->country_code == 'ES') {
8679 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8680 return 0;
8681 }
8682 } else {
8683 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8684 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8685 return 0;
8686 }
8687 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8688 return 0;
8689 }
8690 }
8691 } elseif ($local == 2) { //I Localtax 2
8692 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8693 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8694 return 0;
8695 }
8696 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8697 return 0;
8698 }
8699 }
8700
8701 if ($seller_country_code == $buyer_country_code) {
8702 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8703 }
8704
8705 return 0;
8706}
8707
8716function yn($yesno, $format = 1, $color = 0)
8717{
8718 global $langs;
8719
8720 $result = 'unknown';
8721 $classname = '';
8722 if ($yesno === true || (int) $yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
8723 $result = $langs->trans('yes');
8724 if ($format == 1 || $format == 3) {
8725 $result = $langs->trans("Yes");
8726 }
8727 if ($format == 2) {
8728 $result = '<input type="checkbox" value="1" checked disabled>';
8729 }
8730 if ($format == 3) {
8731 $result = '<input type="checkbox" value="1" checked disabled> ' . $result;
8732 }
8733 if ($format == 4 || !is_numeric($format)) {
8734 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8735 }
8736
8737 $classname = 'ok';
8738 } else {
8739 $result = $langs->trans("no");
8740 if ($format == 1 || $format == 3) {
8741 $result = $langs->trans("No");
8742 }
8743 if ($format == 2) {
8744 $result = '<input type="checkbox" value="0" disabled>';
8745 }
8746 if ($format == 3) {
8747 $result = '<input type="checkbox" value="0" disabled> ' . $result;
8748 }
8749 if ($format == 4 || !is_numeric($format)) {
8750 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8751 }
8752
8753 if ($color == 2) {
8754 $classname = 'ok';
8755 } else {
8756 $classname = 'error';
8757 }
8758 }
8759 if ($color) {
8760 return '<span class="' . $classname . '">' . $result . '</span>';
8761 }
8762 return $result;
8763}
8764
8783function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8784{
8785 if (empty($modulepart) && is_object($object)) {
8786 if (!empty($object->module)) {
8787 $modulepart = $object->module;
8788 } elseif (!empty($object->element)) {
8789 $modulepart = $object->element;
8790 }
8791 }
8792
8793 $path = '';
8794
8795 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8796 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8797 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8798 $arrayforoldpath['product'] = 2;
8799 }
8800
8801 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8802 $level = $arrayforoldpath[$modulepart];
8803 }
8804 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8805 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8806 if (empty($num) && is_object($object)) {
8807 $num = $object->id;
8808 }
8809 if (empty($alpha)) {
8810 $num = preg_replace('/([^0-9])/i', '', $num);
8811 } else {
8812 $num = preg_replace('/^.*\-/i', '', $num);
8813 }
8814 $num = substr("000" . $num, -$level);
8815 if ($level == 1) {
8816 $path = substr($num, 0, 1);
8817 }
8818 if ($level == 2) {
8819 $path = substr($num, 1, 1) . '/' . substr($num, 0, 1);
8820 }
8821 if ($level == 3) {
8822 $path = substr($num, 2, 1) . '/' . substr($num, 1, 1) . '/' . substr($num, 0, 1);
8823 }
8824 } else {
8825 // We will enhance here a common way of forging path for document storage.
8826 // In a future, we may distribute directories on several levels depending on setup and object.
8827 // Here, $object->id, $object->ref and $modulepart are required.
8828 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceof Societe) {
8829 // Special case for thirdparty, where the ref is a company name that is not unique so path on disk is using the ID instead of the ref
8830 $path = dol_sanitizeFileName((string) $object->id);
8831 } else {
8832 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8833 }
8834 }
8835
8836 if (empty($withoutslash) && !empty($path)) {
8837 $path .= '/';
8838 }
8839
8840 return $path;
8841}
8842
8851function dol_mkdir($dir, $dataroot = '', $newmask = '')
8852{
8853 dol_syslog("functions.lib::dol_mkdir: dir=" . $dir, LOG_INFO);
8854
8855 $dir = dol_sanitizePathName($dir, '_', 0);
8856
8857 $dir_osencoded = dol_osencode($dir);
8858 if (@is_dir($dir_osencoded)) {
8859 return 0;
8860 }
8861
8862 $nberr = 0;
8863 $nbcreated = 0;
8864
8865 $ccdir = '';
8866 if (!empty($dataroot)) {
8867 // Remove data root from loop
8868 $dir = str_replace($dataroot . '/', '', $dir);
8869 $ccdir = $dataroot . '/';
8870 }
8871
8872 $cdir = explode("/", $dir);
8873 $num = count($cdir);
8874 for ($i = 0; $i < $num; $i++) {
8875 if ($i > 0) {
8876 $ccdir .= '/' . $cdir[$i];
8877 } else {
8878 $ccdir .= $cdir[$i];
8879 }
8880 $regs = array();
8881 if (preg_match("/^.:$/", $ccdir, $regs)) {
8882 continue; // If the Windows path is incomplete, continue with next directory
8883 }
8884
8885 // Attention, is_dir() can fail event if the directory exists
8886 // (i.e. according the open_basedir configuration)
8887 if ($ccdir) {
8888 $ccdir_osencoded = dol_osencode($ccdir);
8889 if (!@is_dir($ccdir_osencoded)) {
8890 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8891
8892 umask(0);
8893 $dirmaskdec = octdec((string) $newmask);
8894 if (empty($newmask)) {
8895 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8896 }
8897 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8898 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8899 // If the is_dir has returned a false information, we arrive here
8900 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '" . $ccdir . "' (no permission to write into parent or directory already exists).", LOG_WARNING);
8901 $nberr++;
8902 } else {
8903 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' created", LOG_DEBUG);
8904 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8905 $nbcreated++;
8906 }
8907 } else {
8908 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8909 }
8910 }
8911 }
8912 return ($nberr ? -$nberr : $nbcreated);
8913}
8914
8915
8923function dolChmod($filepath, $newmask = '')
8924{
8925 if (!empty($newmask)) {
8926 @chmod($filepath, octdec($newmask));
8927 } elseif (getDolGlobalString('MAIN_UMASK')) {
8928 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8929 }
8930}
8931
8932
8938function picto_required()
8939{
8940 return '<span class="fieldrequired">*</span>';
8941}
8942
8943
8960function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8961{
8962 if (is_null($stringtoclean)) {
8963 return '';
8964 }
8965
8966 if ($removelinefeed == 2) {
8967 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8968 }
8969 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8970
8971 // We remove entities BEFORE stripping (in case of an open separator char that is entity encoded and not the closing other, the strip will fails)
8972 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8973
8974 $temp = str_replace('< ', '__ltspace__', $temp);
8975 $temp = str_replace('<:', '__lttwopoints__', $temp);
8976
8977 if ($strip_tags) {
8978 $temp = strip_tags($temp);
8979 } else {
8980 // Remove '<' into remaining, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
8981 $pattern = "/<[^<>]+>/";
8982 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8983 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8984 // pass 2 - $temp after pass 2: 0000-021
8985 $tempbis = $temp;
8986 do {
8987 $temp = $tempbis;
8988 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8989 $tempbis = preg_replace($pattern, '', $tempbis);
8990 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8991 } while ($tempbis != $temp);
8992
8993 $temp = $tempbis;
8994
8995 // Remove '<' into remaining, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
8996 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8997 }
8998
8999 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
9000
9001 // Remove also carriage returns
9002 if ($removelinefeed == 1) {
9003 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
9004 }
9005
9006 // And double quotes
9007 if ($removedoublespaces) {
9008 while (strpos($temp, " ")) {
9009 $temp = str_replace(" ", " ", $temp);
9010 }
9011 }
9012
9013 $temp = str_replace('__ltspace__', '< ', $temp);
9014 $temp = str_replace('__lttwopoints__', '<:', $temp);
9015
9016 return trim($temp);
9017}
9018
9038function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
9039{
9040 if (empty($allowed_tags)) {
9041 $allowed_tags = array(
9042 // HTML 4
9043 "html",
9044 "head",
9045 "body",
9046 "article",
9047 "a",
9048 "abbr",
9049 "b",
9050 "blockquote",
9051 "br",
9052 "cite",
9053 "div",
9054 "dl",
9055 "dd",
9056 "dt",
9057 "em",
9058 "font",
9059 "img",
9060 "ins",
9061 "hr",
9062 "i",
9063 "li",
9064 "ol",
9065 "p",
9066 "q",
9067 "s",
9068 "span",
9069 "strike",
9070 "strong",
9071 "title",
9072 "table",
9073 "tr",
9074 "th",
9075 "td",
9076 "u",
9077 "ul",
9078 "sup",
9079 "sub",
9080 "blockquote",
9081 "pre",
9082 "h1",
9083 "h2",
9084 "h3",
9085 "h4",
9086 "h5",
9087 "h6",
9088
9089 // HTML 5
9090 "footer",
9091 "header",
9092 "menu",
9093 "menuitem",
9094 "nav",
9095 "section"
9096 );
9097 }
9098 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
9099 if ($allowiframe) {
9100 if (!in_array('iframe', $allowed_tags)) {
9101 $allowed_tags[] = "iframe";
9102 }
9103 }
9104 if ($allowlink) {
9105 if (!in_array('link', $allowed_tags)) {
9106 $allowed_tags[] = "link";
9107 }
9108 if (!in_array('meta', $allowed_tags)) {
9109 $allowed_tags[] = "meta";
9110 }
9111 }
9112 if ($allowscript) {
9113 if (!in_array('script', $allowed_tags)) {
9114 $allowed_tags[] = "script";
9115 }
9116 }
9117 if ($allowstyle) {
9118 if (!in_array('style', $allowed_tags)) {
9119 $allowed_tags[] = "style";
9120 }
9121 }
9122
9123 $allowed_tags_string = implode("><", $allowed_tags);
9124 $allowed_tags_string = '<' . $allowed_tags_string . '>';
9125
9126 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
9127
9128 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
9129
9130 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
9131 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
9132
9133 if ($allowphp) {
9134 $allowed_tags[] = "commentphp";
9135 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
9136 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
9137 }
9138
9139 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
9140 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
9141
9142 // Remove all HTML tags
9143 $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired </>, so may changes string obfuscated with </> that pass the injection detection into a harmfull string
9144
9145 if ($cleanalsosomestyles) { // Clean for remaining html tags
9146 $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/i', '', $temp); // Note: If hacker try to introduce css comment into string to bypass this regex, the string must also be encoded by the dol_htmlentitiesbr during output so it become harmless
9147 }
9148 if ($removeclassattribute) { // Clean for remaining html tags
9149 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
9150 }
9151
9152 // Remove 'javascript:' that we should not find into a text
9153 // Warning: This is not reliable to fight against obfuscated javascript, there is a lot of other solution to include js into a common html tag (only filtered by a GETPOST(.., powerfullfilter)).
9154 if ($cleanalsojavascript) {
9155 $temp = preg_replace('/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*:/i', '', $temp);
9156 }
9157
9158 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
9159
9160 if ($allowphp) {
9161 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
9162 }
9163
9164 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
9165
9166
9167 return $temp;
9168}
9169
9170
9183function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
9184{
9185 if (is_null($allowed_attributes)) {
9186 $allowed_attributes = array(
9187 // HTML 4
9188 "allow",
9189 "allowfullscreen",
9190 "alt",
9191 "async",
9192 "class",
9193 "contenteditable",
9194 "crossorigin",
9195 "data-html",
9196 "frameborder",
9197 "height",
9198 "href",
9199 "id",
9200 "name",
9201 "property",
9202 "rel",
9203 "src",
9204 "style",
9205 "target",
9206 "title",
9207 "type",
9208 "width",
9209
9210 // HTML5
9211 "footer",
9212 "header",
9213 "menu",
9214 "menuitem",
9215 "nav",
9216 "section"
9217 );
9218 }
9219 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
9220 if (!in_array("content", $allowed_attributes)) {
9221 $allowed_attributes[] = "content";
9222 }
9223 if (!in_array("http-equiv", $allowed_attributes)) {
9224 $allowed_attributes[] = "http-equiv";
9225 }
9226
9227 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
9228 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
9229 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . $stringtoclean . '</body></html>';
9230
9231 // Warning: loadHTML does not support HTML5 on old libxml versions.
9232 $dom = new DOMDocument('', 'UTF-8');
9233 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
9234 $savwarning = error_reporting();
9235 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
9236 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
9237 error_reporting($savwarning);
9238
9239 if ($dom instanceof DOMDocument) {
9240 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
9241 $el = $els->item($i);
9242 if (!$el instanceof DOMElement) {
9243 continue;
9244 }
9245 $attrs = $el->attributes;
9246 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
9247 //var_dump($attrs->item($ii));
9248 if (!empty($attrs->item($ii)->name)) {
9249 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
9250 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
9251 $els->item($i)->removeAttribute($attrs->item($ii)->name);
9252 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
9253 // If attribute is 'style'
9254 $valuetoclean = $attrs->item($ii)->value;
9255
9256 if (isset($valuetoclean)) {
9257 do {
9258 $oldvaluetoclean = $valuetoclean;
9259 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
9260 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
9261 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
9262 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
9263 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
9264 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
9265 }
9266
9267 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
9268 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
9269 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
9270 } while ($oldvaluetoclean != $valuetoclean);
9271 }
9272
9273 $attrs->item($ii)->value = $valuetoclean;
9274 }
9275 }
9276 }
9277 }
9278 }
9279
9280 $dom->encoding = 'UTF-8';
9281
9282 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
9283 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
9284
9285 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
9286 $return = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]*' . preg_quote('></head><body>', '/') . '/', '', $return);
9287 $return = preg_replace('/' . preg_quote('</body></html>', '/') . '$/', '', trim($return));
9288
9289 return trim($return);
9290 } else {
9291 return $stringtoclean;
9292 }
9293}
9294
9306function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
9307{
9308 $temp = $stringtoclean;
9309 foreach ($disallowed_tags as $tagtoremove) {
9310 $temp = preg_replace('/<\/?' . $tagtoremove . '>/', '', $temp);
9311 $temp = preg_replace('/<\/?' . $tagtoremove . '\s+[^>]*>/', '', $temp);
9312 }
9313
9314 if ($cleanalsosomestyles) {
9315 $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/', '', $temp); // Note: If hacker try to introduce css comment into string to avoid this, string should be encoded by the dol_htmlentitiesbr so be harmless
9316 }
9317
9318 return $temp;
9319}
9320
9321
9331function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
9332{
9333 if ($nboflines == 1) {
9334 if (dol_textishtml($text)) {
9335 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
9336 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
9337 } else {
9338 if (isset($text)) {
9339 $firstline = preg_replace('/[\n\r].*/', '', $text);
9340 } else {
9341 $firstline = '';
9342 }
9343 }
9344 return $firstline . (isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
9345 } else {
9346 $ishtml = 0;
9347 if (dol_textishtml($text)) {
9348 $text = preg_replace('/\n/', '', $text);
9349 $ishtml = 1;
9350 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9351 } else {
9352 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9353 }
9354
9355 $text = strtr($text, $repTable);
9356 if ($charset == 'UTF-8') {
9357 $pattern = '/(<br[^>]*>)/Uu';
9358 } else {
9359 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9360 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9361 }
9362 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9363
9364 $firstline = '';
9365 $i = 0;
9366 $countline = 0;
9367 $lastaddediscontent = 1;
9368 while ($countline < $nboflines && isset($a[$i])) {
9369 if (preg_match('/<br[^>]*>/', $a[$i])) {
9370 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
9371 $firstline .= ($ishtml ? "<br>\n" : "\n");
9372 // Is it a br for a new line of after a printed line ?
9373 if (!$lastaddediscontent) {
9374 $countline++;
9375 }
9376 $lastaddediscontent = 0;
9377 }
9378 } else {
9379 $firstline .= $a[$i];
9380 $lastaddediscontent = 1;
9381 $countline++;
9382 }
9383 $i++;
9384 }
9385
9386 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
9387 //unset($a);
9388 $ret = $firstline . ($adddots ? '...' : '');
9389 //exit;
9390 return $ret;
9391 }
9392}
9393
9394
9406function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
9407{
9408 if (is_null($stringtoencode)) {
9409 return '';
9410 }
9411
9412 if (!$nl2brmode) {
9413 return nl2br($stringtoencode, $forxml);
9414 } else {
9415 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
9416 return $ret;
9417 }
9418}
9419
9429function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
9430{
9431 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
9432 // TODO using sandbox on inline html content is not possible yet with current browsers
9433 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
9434 //$s .= $stringtoencode;
9435 //$s .= '</body></html></iframe>';
9436 return $stringtoencode;
9437 } else {
9438 $out = $stringtoencode;
9439
9440 // First clean HTML content
9441 do {
9442 $oldstringtoclean = $out;
9443
9444 $outishtml = 0;
9445 if (dol_textishtml($out)) {
9446 $outishtml = 1;
9447 }
9448
9449 // HTML sanitizer by DOMDocument
9450 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
9451 try {
9452 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
9453 if (LIBXML_VERSION < 20900) {
9454 // Avoid load of external entities (security problem).
9455 // Required only if LIBXML_VERSION < 20900
9456 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
9457 libxml_disable_entity_loader(true);
9458 }
9459
9460 $dom = new DOMDocument();
9461 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
9462 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
9463 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
9464 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
9465 // I don't know what the xml encoding is the trick for
9466 if ($outishtml) {
9467 //$out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
9468 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">' . $out . '</div></body></html>';
9469 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
9470 } else {
9471 //$out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
9472 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">' . dol_nl2br($out) . '</div></body></html>';
9473 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
9474 }
9475
9476 // Note: <a href="https://__[aaa]__/aaa.html"> is transformed into <a href="https://__[aaa]__/aaa.html">
9477 // We don't want that, so we protect __[xxx]__ by replacing [ and ] before loadHTML and restore them after saveHTML
9478 $out = preg_replace_callback(
9479 '/__\[([0-9a-zA-Z_]+)\]__/',
9484 function ($m) {
9485 return '__BRACKETSTART' . $m[1] . 'BRACKETEND__';
9486 },
9487 $out
9488 );
9489
9490 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
9491
9492 $dom->encoding = 'UTF-8';
9493
9494 $out = trim($dom->saveHTML());
9495
9496 // Restore [ and ] that were protected before loadHTML
9497 $out = preg_replace_callback(
9498 '/__BRACKETSTART([0-9a-zA-Z_]+)BRACKETEND__/',
9503 function ($m) {
9504 return '__[' . $m[1] . ']__';
9505 },
9506 $out
9507 );
9508
9509 // Remove the trick added to solve pb with text in utf8 and text without parent tag
9510 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
9511 $out = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]+' . preg_quote('></head><body><div class="tricktoremove">', '/') . '/', '', $out);
9512 $out = preg_replace('/' . preg_quote('</div></body></html>', '/') . '$/', '', trim($out));
9513 //$out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
9514 //$out = preg_replace('/<\/div>$/', '', $out);
9515
9516 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
9517 $out = str_replace('<br>', '', $out);
9518 }
9519 } catch (Exception $e) {
9520 // If error, invalid HTML string with no way to clean it
9521 //print $e->getMessage();
9522 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9523 }
9524 }
9525
9526 // HTML sanitizer by Tidy
9527 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
9528 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
9529 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
9530 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
9531 try {
9532 //var_dump($out);
9533
9534 // Try cleaning using tidy
9535 if (extension_loaded('tidy') && class_exists("tidy")) {
9536 //print "aaa".$out."\n";
9537
9538 // See options at https://tidy.sourceforge.net/docs/quickref.html
9539 $config = array(
9540 'clean' => false,
9541 // Best will be to set 'quote-marks' to false to not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
9542 'quote-marks' => false,
9543 'doctype' => 'strict',
9544 'show-body-only' => true,
9545 "indent-attributes" => false,
9546 "vertical-space" => false,
9547 //'ident' => false, // Not always supported
9548 "wrap" => 0,
9549 'preserve-entities' => true
9550 // HTML5 tags
9551 //'new-blocklevel-tags' => 'article aside audio bdi canvas details dialog figcaption figure footer header hgroup main menu menuitem nav section source summary template track video',
9552 //'new-blocklevel-tags' => 'footer header section menu menuitem'
9553 //'new-empty-tags' => 'command embed keygen source track wbr',
9554 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
9555 );
9556
9557 // Tidy
9558 $tidy = new tidy();
9559 $out = $tidy->repairString($out, $config, 'utf8');
9560
9561 //print "xxx".$out;exit;
9562 }
9563
9564 //var_dump($out);
9565 } catch (Exception $e) {
9566 // If error, invalid HTML string with no way to clean it
9567 //print $e->getMessage();
9568 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9569 }
9570 }
9571
9572 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
9573 // TODO $out = preg_replace('/[\x{2000}-\x{200D}\x{FEFF}]/u', ' ', $out);
9574 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
9575
9576 // Clean some html entities that are useless so text is cleaner
9577 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
9578
9579 // Ckeditor uses the numeric entity for apostrophe, so we force it to
9580 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
9581 $out = preg_replace('/&#39;/i', '&apos;', $out);
9582
9583 // We replace chars from a/A to z/Z encoded with numeric HTML entities with the real char so we won't loose the chars at the next step (preg_replace).
9584 // No need to use a loop here, this step is not to sanitize (this is done at next step, this is to try to save chars, even if they are
9585 // using a non conventionnal way to be encoded, to not have them sanitized just after)
9586 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
9587 $out = preg_replace_callback(
9588 '/&#(x?[0-9][0-9a-f]+;?)/i',
9593 static function ($m) {
9594 return realCharForNumericEntities($m);
9595 },
9596 $out
9597 );
9598 }
9599
9600 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
9601 $out = preg_replace('/&#x?[0-9]+/i', '', $out); // For example if we have j&#x61vascript with an entities without the ; to hide the 'a' of 'javascript'.
9602
9603 // Keep only some html tags and remove also some 'javascript:' strings
9604 if ($check == 'restricthtmlallowlinkscript') {
9605 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
9606 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
9607 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
9608 } elseif ($check == 'restricthtmlallowiframe') {
9609 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
9610 } else {
9611 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
9612 }
9613
9614 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
9615 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
9617 }
9618
9619 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
9620 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
9621 $out = preg_replace('/&apos;/i', "&#39;", $out);
9622
9623 // Now remove js
9624 // List of dom events is on https://www.w3schools.com/jsref/dom_obj_event.asp and https://developer.mozilla.org/en-US/docs/Web/Events
9625 $out = preg_replace('/on(mouse|drag|key|load|touch|pointer|select|transition)[a-z]*\s*=/i', '', $out); // onmousexxx can be set on img or any html tag like <img title='...' onmouseover=alert(1)>
9626 $out = preg_replace('/on(abort|after|animation|auxclick|before|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', '', $out);
9627 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
9628 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
9629 $out = preg_replace('/on(paste|pause|play|playing|progress|ratechange|reset|resize|scroll|search|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', '', $out);
9630 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
9631 // More not into the previous list
9632 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
9633 } while ($oldstringtoclean != $out);
9634
9635 // Check the limit of external links that are automatically executed in a Rich text content. We count:
9636 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
9637 // 'url(' to avoid inline style like background: url(http...
9638 // '<link' to avoid <link href="http...">
9639 $reg = array();
9640 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
9641 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
9642 $nblinks = count($reg[0]);
9643 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
9644 $out = 'ErrorTooManyLinksIntoHTMLString';
9645 }
9646
9647 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
9648 if ($nblinks > 0) {
9649 $out = 'ErrorHTMLLinksNotAllowed';
9650 }
9651 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
9652 // Refuse any links except it they are to the wrapper document.php or viewimage.php
9653 $nblinks = 0;
9654
9655 // Loop on each url in src= and url(
9656 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
9657
9659
9660 $matches = array();
9661 if (preg_match_all($pattern, $out, $matches)) {
9662 // URLs are into $matches[1] or $matches[2]
9663 $urls = array();
9664 foreach ($matches[1] as $tmpval) {
9665 if (!empty($tmpval)) {
9666 $urls[] = $tmpval;
9667 }
9668 }
9669 foreach ($matches[2] as $tmpval) {
9670 if (!empty($tmpval)) {
9671 $urls[] = $tmpval;
9672 }
9673 }
9674
9675 // Show URLs
9676 $firstexturl = '';
9677 $secondexturl = '';
9678 foreach ($urls as $url) {
9679 $urlok = 0;
9680 $parsedurl = parse_url($url);
9681 if (!empty($parsedurl)) {
9682 if (preg_match('/'.preg_quote($dolibarr_main_url_root, '/').'/', $url)
9683 //&& preg_match('/(document|viewimage)\.php$/', $parsedurl['path']) && preg_match('/modulepart=(media|mycompany)/', $parsedurl['query'])
9684 ) {
9685 $urlok = 1;
9686 }
9687 }
9688 if (!$urlok) {
9689 $nblinks++;
9690 if (empty($firstexturl)) {
9691 $firstexturl = $url;
9692 } elseif (empty($secondexturl)) {
9693 $secondexturl = $url;
9694 }
9695 //echo "Found url = ".$url . "\n";
9696 }
9697 }
9698 if ($nblinks > 0) {
9699 $out = 'ErrorHTMLExternalLinksNotAllowed (Example: '.$firstexturl.($secondexturl ? ' '.$secondexturl : '').')';
9700 }
9701 }
9702 }
9703
9704 return $out;
9705 }
9706}
9707
9728function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9729{
9730 if (is_null($stringtoencode)) {
9731 return '';
9732 }
9733
9734 $newstring = $stringtoencode;
9735 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9736 $newstring = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', '<br>', $newstring); // Replace "<br type="_moz" />" by "<br>". It's same and avoid pb with FPDF.
9737 if ($removelasteolbr) {
9738 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9739 }
9740 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9741 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9742 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9743 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9744 } else {
9745 if ($removelasteolbr) {
9746 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9747 }
9748 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9749 }
9750 // Other substitutions that htmlentities does not do
9751 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9752 return $newstring;
9753}
9754
9762function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9763{
9764 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9765 $ret = preg_replace('/' . "\r\n" . '<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9766 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\r\n" . '/i', "\r\n", $ret);
9767 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\n" . '/i', "\n", $ret);
9768 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9769 return $ret;
9770}
9771
9778function dol_htmlcleanlastbr($stringtodecode)
9779{
9780 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9781 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|' . "\n" . '|' . "\r" . ')+$/i', "", $ret);
9782 return $ret;
9783}
9784
9794function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9795{
9796 $newstring = $a;
9797 if ($keepsomeentities) {
9798 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9799 }
9800 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9801 if ($keepsomeentities) {
9802 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9803 }
9804 return $newstring;
9805}
9806
9818function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9819{
9820 return htmlentities($string, $flags, $encoding, $double_encode);
9821}
9822
9834function dol_string_is_good_iso($s, $clean = 0)
9835{
9836 $len = dol_strlen($s);
9837 $out = '';
9838 $ok = 1;
9839 for ($scursor = 0; $scursor < $len; $scursor++) {
9840 $ordchar = ord($s[$scursor]);
9841 //print $scursor.'-'.$ordchar.'<br>';
9842 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9843 $ok = 0;
9844 break;
9845 } elseif ($ordchar > 126 && $ordchar < 160) {
9846 $ok = 0;
9847 break;
9848 } elseif ($clean) {
9849 $out .= $s[$scursor];
9850 }
9851 }
9852 if ($clean) {
9853 return $out;
9854 }
9855 return $ok;
9856}
9857
9866function dol_nboflines($s, $maxchar = 0)
9867{
9868 if ($s == '') {
9869 return 0;
9870 }
9871 $arraystring = explode("\n", $s);
9872 $nb = count($arraystring);
9873
9874 return $nb;
9875}
9876
9877
9887function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9888{
9889 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9890 if (dol_textishtml($text)) {
9891 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9892 }
9893
9894 $text = strtr($text, $repTable);
9895 if ($charset == 'UTF-8') {
9896 $pattern = '/(<br[^>]*>)/Uu';
9897 } else {
9898 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9899 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9900 }
9901 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9902
9903 $nblines = (int) floor((count($a) + 1) / 2);
9904 // count possible auto line breaks
9905 if ($maxlinesize) {
9906 foreach ($a as $line) {
9907 if (dol_strlen($line) > $maxlinesize) {
9908 //$line_dec = html_entity_decode(strip_tags($line));
9909 $line_dec = html_entity_decode($line);
9910 if (dol_strlen($line_dec) > $maxlinesize) {
9911 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9912 $nblines += substr_count($line_dec, '\n');
9913 }
9914 }
9915 }
9916 }
9917
9918 unset($a);
9919 return $nblines;
9920}
9921
9930function dol_textishtml($msg, $option = 0)
9931{
9932 if (is_null($msg)) {
9933 return false;
9934 }
9935
9936 if ($option == 1) {
9937 if (preg_match('/<(html|link|script)/i', $msg)) {
9938 return true;
9939 } elseif (preg_match('/<body/i', $msg)) {
9940 return true;
9941 } elseif (preg_match('/<\/textarea/i', $msg)) {
9942 return true;
9943 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9944 return true;
9945 } elseif (preg_match('/<br/i', $msg)) {
9946 return true;
9947 }
9948 return false;
9949 } else {
9950 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9951 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9952 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9953 return true;
9954 } elseif (preg_match('/<\/textarea/i', $msg)) {
9955 return true;
9956 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9957 return true;
9958 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9959 return true;
9960 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9961 return true;
9962 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9963 return true;
9964 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9965 return true; // must accept <img src="http://example.com/aaa.png" />
9966 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9967 return true; // must accept <a href="http://example.com/aaa.png" />
9968 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9969 return true;
9970 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9971 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9972 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9973 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9974 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9975 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
9976 return true; // Html entities numbers in hexa
9977 }
9978
9979 return false;
9980 }
9981}
9982
9997function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
9998{
9999 if (!empty($invert)) {
10000 $tmp = $text1;
10001 $text1 = $text2;
10002 $text2 = $tmp;
10003 }
10004
10005 $ret = '';
10006 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
10007 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
10008 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
10009 return $ret;
10010}
10011
10020function dol_concat($text1, $text2)
10021{
10022 return $text1.$text2;
10023}
10024
10038function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
10039{
10040 global $db, $conf, $mysoc, $user, $extrafields;
10041
10042 $substitutionarray = array();
10043
10044 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
10045 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
10046 // this will include signature content first and then replace var found into content of signature
10047 //var_dump($onlykey);
10048 $emailsendersignature = $user->signature; // By default, we use the signature of current user. We must complete substitution with signature in c_email_senderprofile of array after calling getCommonSubstitutionArray()
10049 $usersignature = $user->signature;
10050 $substitutionarray = array_merge($substitutionarray, array(
10051 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
10052 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
10053 ));
10054
10055 if (is_object($user) && ($user instanceof User)) {
10056 $substitutionarray = array_merge($substitutionarray, array(
10057 '__USER_ID__' => (string) $user->id,
10058 '__USER_LOGIN__' => (string) $user->login,
10059 '__USER_EMAIL__' => (string) $user->email,
10060 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
10061 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
10062 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
10063 '__USER_FAX__' => (string) $user->office_fax,
10064 '__USER_LASTNAME__' => (string) $user->lastname,
10065 '__USER_FIRSTNAME__' => (string) $user->firstname,
10066 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
10067 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
10068 '__USER_JOB__' => (string) $user->job,
10069 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
10070 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
10071 ));
10072 }
10073 }
10074 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
10075 $substitutionarray = array_merge($substitutionarray, array(
10076 '__MYCOMPANY_NAME__' => $mysoc->name,
10077 '__MYCOMPANY_EMAIL__' => $mysoc->email,
10078 '__MYCOMPANY_URL__' => $mysoc->url,
10079 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
10080 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
10081 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
10082 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
10083 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
10084 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
10085 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
10086 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
10087 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
10088 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
10089 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
10090 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
10091 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
10092 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
10093 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
10094 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
10095 '__MYCOMPANY_VATNUMBER__' => $mysoc->tva_intra,
10096 '__MYCOMPANY_ZIP__' => $mysoc->zip,
10097 '__MYCOMPANY_TOWN__' => $mysoc->town,
10098 '__MYCOMPANY_STATE__' => $mysoc->state,
10099 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
10100 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
10101 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
10102 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
10103 ));
10104 }
10105
10106 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
10107 if ($onlykey) {
10108 $substitutionarray['__ID__'] = '__ID__';
10109 $substitutionarray['__REF__'] = '__REF__';
10110 $substitutionarray['__NEWREF__'] = '__NEWREF__';
10111 $substitutionarray['__LABEL__'] = '__LABEL__';
10112 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
10113 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
10114 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
10115 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
10116 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
10117
10118 if (isModEnabled("societe")) { // Most objects are concerned
10119 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
10120 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
10121 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
10122 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
10123 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
10124 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
10125 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
10126 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
10127 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
10128 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
10129 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
10130 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
10131 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
10132 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
10133 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
10134 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
10135 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
10136 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
10137 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
10138 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
10139 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
10140 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
10141 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
10142 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
10143 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
10144 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
10145 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
10146 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
10147 }
10148 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
10149 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
10150 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
10151 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
10152 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
10153 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
10154 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
10155 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
10156 }
10157 // add substitution variables for ticket
10158 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
10159 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
10160 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
10161 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
10162 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
10163 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
10164 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
10165 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
10166 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
10167 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
10168 }
10169
10170 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
10171 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
10172 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
10173 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
10174 }
10175 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
10176 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
10177 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
10178 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
10179 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
10180 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
10181 }
10182 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
10183 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
10184 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
10185 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
10186 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
10187 }
10188 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
10189 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
10190 }
10191 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
10192 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
10193 }
10194 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
10195 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
10196 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
10197 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
10198 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
10199 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
10200 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
10201
10202 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
10203 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
10204 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
10205 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
10206 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
10207
10208 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
10209 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
10210 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
10211 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
10212 }
10213 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
10214 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
10215 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
10216 }
10217 } else {
10218 '@phan-var-force Adherent|Delivery $object';
10220 $substitutionarray['__ID__'] = $object->id;
10221 $substitutionarray['__REF__'] = $object->ref;
10222 $substitutionarray['__NEWREF__'] = $object->newref;
10223 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
10224 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10225 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10226 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
10227 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
10228
10229 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
10230 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
10231 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
10232
10233 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
10234 $date_delivery = null;
10235 if (property_exists($object, 'date_delivery')) {
10236 $date_delivery = $object->date_delivery;
10237 } elseif (property_exists($object, 'delivery_date')) {
10238 $date_delivery = $object->delivery_date;
10239 }
10240 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10241 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
10242 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
10243 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
10244 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
10245 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
10246 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
10247 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
10248 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
10249
10250 // For backward compatibility (deprecated)
10251 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10252 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10253
10254 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10255 $substitutionarray['__SUPPLIER_ORDER_DELAY_DELIVERY__'] = (isset($object->availability_code) ? ($outputlangs->transnoentities("AvailabilityType" . $object->availability_code) != 'AvailabilityType' . $object->availability_code ? $outputlangs->transnoentities("AvailabilityType" . $object->availability_code) : $outputlangs->convToOutputCharset(isset($object->availability) ? $object->availability : '')) : '');
10256 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
10257
10258 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
10259 '@phan-var-force Adherent $object';
10261 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
10262
10263 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
10264 if (method_exists($object, 'getCivilityLabel')) {
10265 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
10266 }
10267 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
10268 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
10269 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
10270 if (method_exists($object, 'getFullName')) {
10271 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
10272 }
10273 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
10274 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
10275 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
10276 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
10277 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
10278 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
10279 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
10280 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
10281 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
10282 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
10283 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
10284 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
10285 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
10286 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
10287 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
10288 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
10289
10290 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
10291 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
10292 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
10293 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
10294 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
10295 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
10296 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
10297 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
10298 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
10299 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
10300 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
10301 }
10302
10303 if (is_object($object) && $object->element == 'societe') {
10305 '@phan-var-force Societe $object';
10306 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
10307 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
10308 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
10309 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
10310 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
10311 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
10312 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
10313 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
10314 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
10315 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
10316 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
10317 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
10318 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
10319 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
10320 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
10321 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ?: '');
10322 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
10323 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
10324 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
10325 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
10326 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
10327 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
10328 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
10329 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
10330 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
10331 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
10332 } elseif (is_object($object) && is_object($object->thirdparty)) {
10333 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
10334 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
10335 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
10336 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
10337 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
10338 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
10339 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
10340 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
10341 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
10342 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
10343 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
10344 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
10345 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
10346 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ?: '');
10347 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
10348 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
10349 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
10350 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
10351 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
10352 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
10353 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
10354 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
10355 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
10356 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
10357 }
10358
10359 if (is_object($object) && $object->element == 'recruitmentcandidature') {
10360 '@phan-var-force RecruitmentCandidature $object';
10362 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
10363 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10364 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10365 }
10366 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
10367 '@phan-var-force ConferenceOrBoothAttendee $object';
10369 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
10370 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10371 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10372 }
10373
10374 if (is_object($object) && $object->element == 'project') {
10375 '@phan-var-force Project $object';
10377 $substitutionarray['__PROJECT_ID__'] = $object->id;
10378 $substitutionarray['__PROJECT_REF__'] = $object->ref;
10379 $substitutionarray['__PROJECT_NAME__'] = $object->title;
10380 } elseif (is_object($object)) {
10381 $project = null;
10382 if (!empty($object->project)) {
10383 $project = $object->project;
10384 }
10385 if (!is_null($project) && is_object($project)) {
10386 $substitutionarray['__PROJECT_ID__'] = $project->id;
10387 $substitutionarray['__PROJECT_REF__'] = $project->ref;
10388 $substitutionarray['__PROJECT_NAME__'] = $project->title;
10389 } else {
10390 // can substitute variables for project : uses lazy load in "make_substitutions" method
10391 $project_id = 0;
10392 if (!empty($object->fk_project) && $object->fk_project > 0) {
10393 $project_id = $object->fk_project;
10394 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
10395 $project_id = $object->fk_project;
10396 }
10397 if ($project_id > 0) {
10398 // path:class:method:id
10399 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10400 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10401 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10402 }
10403 }
10404 }
10405
10406 if (is_object($object) && $object->element == 'facture') {
10407 '@phan-var-force Facture $object';
10409 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
10410 }
10411 if (is_object($object) && $object->element == 'shipping') {
10412 '@phan-var-force Expedition $object';
10414 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
10415 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
10416 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
10417 }
10418 if (is_object($object) && $object->element == 'reception') {
10419 '@phan-var-force Reception $object';
10421 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
10422 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
10423 }
10424
10425 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
10426 '@phan-var-force Contrat $object';
10428 $dateplannedstart = '';
10429 $datenextexpiration = '';
10430 foreach ($object->lines as $line) {
10431 if ($line->date_start > $dateplannedstart) {
10432 $dateplannedstart = $line->date_start;
10433 }
10434 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
10435 $datenextexpiration = $line->date_end;
10436 }
10437 }
10438 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
10439 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
10440 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
10441
10442 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
10443 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
10444 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
10445 }
10446 // add substitution variables for ticket
10447 if (is_object($object) && $object->element == 'ticket') {
10448 '@phan-var-force Ticket $object';
10450 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
10451 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
10452 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
10453 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
10454 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
10455 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
10456 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
10457 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
10458 $userstat = new User($db);
10459 if ($object->fk_user_assign > 0) {
10460 $userstat->fetch($object->fk_user_assign);
10461 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10462 }
10463
10464 if ($object->fk_user_create > 0) {
10465 $userstat->fetch($object->fk_user_create);
10466 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10467 }
10468 }
10469
10470 // Create dynamic tags for __EXTRAFIELD_FIELD__
10471 if ($object->table_element && $object->id > 0) {
10472 if (!is_object($extrafields)) {
10473 $extrafields = new ExtraFields($db);
10474 }
10475 $extrafields->fetch_name_optionals_label($object->table_element, true);
10476
10477 if ($object->fetch_optionals() > 0) {
10478 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
10479 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
10480 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
10481 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_date($object->array_options['options_' . $key], 'day');
10482 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = dol_print_date($object->array_options['options_' . $key], 'day', 'tzserver', $outputlangs);
10483 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = dol_print_date($object->array_options['options_' . $key], 'dayrfc');
10484 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
10485 $datetime = $object->array_options['options_' . $key];
10486 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
10487 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
10488 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
10489 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
10490 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
10491 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_phone($object->array_options['options_' . $key]);
10492 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
10493 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
10494 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATED__'] = price($object->array_options['options_' . $key]); // For compatibility
10495 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATTED__'] = price($object->array_options['options_' . $key]);
10496 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
10497 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = !empty($object->array_options['options_' . $key]) ? $object->array_options['options_' . $key] : '';
10498 }
10499 }
10500 }
10501 }
10502 }
10503
10504 // Complete substitution array with the url to make online payment
10505 if (empty($substitutionarray['__REF__'])) {
10506 $paymenturl = '';
10507 } else {
10508 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
10509 require_once DOL_DOCUMENT_ROOT . '/core/lib/payments.lib.php';
10510 $outputlangs->loadLangs(array('paypal', 'other'));
10511
10512 $amounttouse = 0;
10513 $typeforonlinepayment = 'free';
10514 if (is_object($object) && $object->element == 'commande') {
10515 $typeforonlinepayment = 'order';
10516 }
10517 if (is_object($object) && $object->element == 'facture') {
10518 $typeforonlinepayment = 'invoice';
10519 }
10520 if (is_object($object) && $object->element == 'member') {
10521 $typeforonlinepayment = 'member';
10522 if (!empty($object->last_subscription_amount)) {
10523 $amounttouse = $object->last_subscription_amount;
10524 }
10525 }
10526 if (is_object($object) && $object->element == 'contrat') {
10527 $typeforonlinepayment = 'contract';
10528 }
10529 if (is_object($object) && $object->element == 'fichinter') {
10530 $typeforonlinepayment = 'ficheinter';
10531 }
10532
10533 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], (float) $amounttouse);
10534 $paymenturl = $url;
10535 }
10536
10537 if ($object->id > 0) {
10538 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
10539 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
10540
10541 // Show structured communication
10542 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
10543 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions_be.lib.php';
10544 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
10545 }
10546
10547 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
10548 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10549 } else {
10550 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
10551 }
10552 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
10553 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
10554 } else {
10555 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
10556 }
10557 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
10558 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
10559 } else {
10560 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
10561 }
10562 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
10563 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
10564 } else {
10565 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
10566 }
10567 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
10568 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
10569 } else {
10570 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
10571 }
10572 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
10573 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10574 } else {
10575 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
10576 }
10577
10578 if (is_object($object) && $object->element == 'propal') {
10579 '@phan-var-force Propal $object';
10581 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/comm/propal/card.php?id=" . $object->id;
10582 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10583 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
10584 }
10585 if (is_object($object) && $object->element == 'commande') {
10586 '@phan-var-force Commande $object';
10588 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT . "/commande/card.php?id=" . $object->id;
10589 }
10590 if (is_object($object) && $object->element == 'facture') {
10591 '@phan-var-force Facture $object';
10593 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT . "/compta/facture/card.php?id=" . $object->id;
10594 }
10595 if (is_object($object) && $object->element == 'contrat') {
10596 '@phan-var-force Contrat $object';
10598 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT . "/contrat/card.php?id=" . $object->id;
10599 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10600 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
10601 }
10602 if (is_object($object) && $object->element == 'fichinter') {
10603 '@phan-var-force Fichinter $object';
10605 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT . "/fichinter/card.php?id=" . $object->id;
10606 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10607 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
10608 }
10609 if (is_object($object) && $object->element == 'supplier_proposal') {
10610 '@phan-var-force SupplierProposal $object';
10612 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/supplier_proposal/card.php?id=" . $object->id;
10613 }
10614 if (is_object($object) && $object->element == 'invoice_supplier') {
10615 '@phan-var-force FactureFournisseur $object';
10617 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT . "/fourn/facture/card.php?id=" . $object->id;
10618 }
10619 if (is_object($object) && $object->element == 'payment_supplier') {
10620 '@phan-var-force PaiementFourn $object';
10622 //print_r($object);
10623 $liste_factures = [];
10624 $total = 0;
10625
10626 $sql = 'SELECT f.ref,f.multicurrency_code as f_mccode, pf.*
10627 FROM '.MAIN_DB_PREFIX.'paiementfourn_facturefourn as pf
10628 JOIN '.MAIN_DB_PREFIX.'facture_fourn as f ON pf.fk_facturefourn = f.rowid
10629 WHERE pf.fk_paiementfourn = '.((int) $object->id);
10630
10631 $resql = $db->query($sql);
10632 if ($resql) {
10633 while ($objp = $db->fetch_object($resql)) {
10634 $liste_factures[] = ' - '.$outputlangs->trans('Invoice').' '. $objp->ref.' '.$outputlangs->trans('AmountPayed').' '.price($objp->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $objp->multicurrency_code);
10635 }
10636 }
10637 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_LIST__'] = implode("\n", $liste_factures);
10638 ;
10639 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_TOTAL__'] = price($object->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $object->multicurrency_code ? $object->multicurrency_code : $conf->currency);
10640 }
10641 if (is_object($object) && $object->element == 'shipping') {
10642 '@phan-var-force Expedition $object';
10644 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT . "/expedition/card.php?id=" . $object->id;
10645 }
10646 }
10647
10648 if (is_object($object) && $object->element == 'action') {
10649 '@phan-var-force ActionComm $object';
10651 $substitutionarray['__EVENT_LABEL__'] = $object->label;
10652 $substitutionarray['__EVENT_DESCRIPTION__'] = $object->note;
10653 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action" . $object->type_code);
10654 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
10655 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
10656 $substitutionarray['__EVENT_DATE_TZUSER__'] = dol_print_date($object->datep, 'day', 'tzuserrel', $outputlangs);
10657 $substitutionarray['__EVENT_TIME_TZUSER__'] = dol_print_date($object->datep, 'hour', 'tzuserrel', $outputlangs);
10658 }
10659 }
10660 }
10661
10662 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
10663 '@phan-var-force Facture|FactureRec $object';
10665 include_once DOL_DOCUMENT_ROOT . '/core/lib/functionsnumtoword.lib.php';
10666
10667 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
10668 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
10669 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
10670 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
10671
10672 $already_payed_all = 0;
10673 if (is_object($object) && ($object instanceof Facture)) {
10674 $already_payed_all = $object->totalpaid + $object->totaldeposits + $object->totalcreditnotes;
10675 }
10676
10677 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
10678 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
10679 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
10680
10681 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
10682 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
10683 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
10684
10685 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
10686
10687 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10688 $substitutionarray['__AMOUNT_VAT_TEXT__'] = is_object($object) ? (isset($object->total_vat) ? dol_convertToWord($object->total_vat, $outputlangs, '', true) : dol_convertToWord($object->total_tva, $outputlangs, '', true)) : '';
10689 $substitutionarray['__AMOUNT_VAT_TEXTCURRENCY__'] = is_object($object) ? (isset($object->total_vat) ? dol_convertToWord($object->total_vat, $outputlangs, $conf->currency, true) : dol_convertToWord($object->total_tva, $outputlangs, $conf->currency, true)) : '';
10690
10691 $mysocuselocaltax1 = false;
10692 $mysocuselocaltax2 = false;
10693 if ($mysoc instanceof Societe && !empty($mysoc->country_code)) {
10694 $tmparray = $mysoc->useLocalTax(-1);
10695 $mysocuselocaltax1 = $tmparray[1];
10696 $mysocuselocaltax2 = $tmparray[2];
10697 }
10698
10699 // Local taxes
10700 if ($onlykey != 2 || $mysocuselocaltax1) {
10701 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
10702 }
10703 if ($onlykey != 2 || $mysocuselocaltax2) {
10704 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
10705 }
10706
10707 // Amount keys formatted in a currency
10708 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10709 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10710 $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc - $already_payed_all, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10711 $substitutionarray['__AMOUNT_VAT_FORMATTED__'] = is_object($object) ? (isset($object->total_vat) ? price($object->total_vat, 0, $outputlangs, 0, -1, -1, $conf->currency) : ($object->total_tva ? price($object->total_tva, 0, $outputlangs, 0, -1, -1, $conf->currency) : null)) : '';
10712 if ($onlykey != 2 || $mysocuselocaltax1) {
10713 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10714 }
10715 if ($onlykey != 2 || $mysocuselocaltax2) {
10716 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10717 }
10718 // Amount keys formatted in a currency (with the typo error for backward compatibility)
10719 if ($onlykey != 2) {
10720 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
10721 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
10722 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
10723 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
10724 if ($mysocuselocaltax1) {
10725 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
10726 }
10727 if ($mysoc->useLocalTax2) {
10728 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
10729 }
10730 }
10731
10732 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
10733 $substitutionarray['__AMOUNT_MULTICURRENCY_FORMATED__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? price($object->multicurrency_total_ttc, 0, $outputlangs, 0, -1, -1, $object->multicurrency_code) : '';
10734 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
10735 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
10736 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
10737 // TODO Add other keys for foreign multicurrency
10738
10739 // For backward compatibility
10740 if ($onlykey != 2) {
10741 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
10742 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
10743 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10744 }
10745 }
10746
10747
10748 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10749 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
10750
10751 $now = dol_now();
10752
10753 $tmp = dol_getdate($now, true);
10754 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10755 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10756 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10757 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10758
10759 $daytext = $outputlangs->trans('Day' . $tmp['wday']);
10760
10761 $substitutionarray = array_merge($substitutionarray, array(
10762 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10763 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10764 '__DAY__' => (string) $tmp['mday'],
10765 '__DAY_TEXT__' => $daytext, // Monday
10766 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10767 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10768 '__MONTH__' => (string) $tmp['mon'],
10769 '__MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp['mon'])),
10770 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp['mon'])),
10771 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp['mon'])),
10772 '__YEAR__' => (string) $tmp['year'],
10773 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10774 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10775 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10776 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10777 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp3['month'])),
10778 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp3['month'])),
10779 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp3['month'])),
10780 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10781 '__NEXT_DAY__' => (string) $tmp4['day'],
10782 '__NEXT_MONTH__' => (string) $tmp5['month'],
10783 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp5['month'])),
10784 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp5['month'])),
10785 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp5['month'])),
10786 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10787 ));
10788 }
10789
10790 if (isModEnabled('multicompany')) {
10791 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10792 }
10793 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10794 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10795 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10796 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey') . ' (load also language file before)';
10797 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10798 }
10799
10800 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10801
10802 return $substitutionarray;
10803}
10804
10821function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10822{
10823 global $db, $langs;
10824
10825 if (!is_array($substitutionarray)) {
10826 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10827 }
10828
10829 if (empty($outputlangs)) {
10830 $outputlangs = $langs;
10831 }
10832
10833 // Is initial text HTML or simple text ?
10834 $msgishtml = 0;
10835 if (dol_textishtml($text, 1)) {
10836 $msgishtml = 1;
10837 }
10838
10839 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10840 if (is_object($outputlangs)) {
10841 $reg = array();
10842 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10843 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10844 $tmp = explode('|', $reg[1]);
10845 if (!empty($tmp[1])) {
10846 $outputlangs->load($tmp[1]);
10847 }
10848
10849 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10850
10851 if (empty($converttextinhtmlifnecessary)) {
10852 // convert $newval into HTML is necessary
10853 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10854 } else {
10855 if (! $msgishtml) {
10856 $valueishtml = dol_textishtml($value, 1);
10857 //var_dump("valueishtml=".$valueishtml);
10858
10859 if ($valueishtml) {
10860 $text = dol_htmlentitiesbr($text);
10861 $msgishtml = 1;
10862 }
10863 } else {
10864 $value = dol_nl2br((string) $value);
10865 }
10866
10867 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $value, $text);
10868 }
10869 }
10870 }
10871
10872 // Make substitution for constant keys.
10873 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10874 $reg = array();
10875 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10876 $originalkeyfound = $reg[1];
10877 $keyfound = preg_replace('/\|urlencode$/', '', $originalkeyfound);
10878
10879 if (isASecretKey($keyfound)) {
10880 $value = '*****forbidden*****';
10881 } else {
10882 $value = getDolGlobalString($keyfound);
10883 // Execute some functions on value of substitution key
10884 if (preg_match('/\|urlencode$/', $originalkeyfound)) {
10885 $value = urlencode($value);
10886 }
10887 }
10888
10889 if (empty($converttextinhtmlifnecessary)) {
10890 // convert $newval into HTML is necessary
10891 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10892 } else {
10893 if (! $msgishtml) {
10894 $valueishtml = dol_textishtml($value, 1);
10895
10896 if ($valueishtml) {
10897 $text = dol_htmlentitiesbr($text);
10898 $msgishtml = 1;
10899 }
10900 } else {
10901 $value = dol_nl2br((string) $value);
10902 }
10903
10904 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $value, $text);
10905 }
10906 }
10907
10908 // Make substitution for array $substitutionarray
10909 foreach ($substitutionarray as $key => $value) {
10910 if (!isset($value)) {
10911 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10912 }
10913
10914 if (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN') && ($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__')) {
10915 $value = ''; // Protection
10916 }
10917
10918 if (empty($converttextinhtmlifnecessary)) {
10919 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10920 } else {
10921 if (! $msgishtml) {
10922 $valueishtml = dol_textishtml($value, 1);
10923
10924 if ($valueishtml) {
10925 $text = dol_htmlentitiesbr($text);
10926 $msgishtml = 1;
10927 }
10928 } else {
10929 $value = dol_nl2br((string) $value);
10930 }
10931 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10932 }
10933 }
10934
10935 // TODO Implement the lazyload substitution
10936 /*
10937 add a loop to scan $substitutionarray:
10938 For each key ending with '@lazyload', we extract the substitution key 'XXX' and we check inside the $text (the 1st parameter of make_substitutions), if the string XXX exists.
10939 If no, we don't need to make replacement, so we do nothing.
10940 If yes, we can make the substitution:
10941
10942 include_once $path;
10943 $tmpobj = new $class($db);
10944 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10945 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10946 */
10947 $memory_object_list = array();
10948 foreach ($substitutionarray as $key => $value) {
10949 $lazy_load_arr = array();
10950 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10951 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10952 $key_to_substitute = $lazy_load_arr[1];
10953 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10954 $param_arr = explode(':', (string) $value);
10955 // path:class:method:id
10956 if (count($param_arr) == 4) {
10957 $path = $param_arr[0];
10958 $class = $param_arr[1];
10959 $method = $param_arr[2];
10960 $id = (int) $param_arr[3];
10961
10962 // load class file and init object list in memory
10963 if (!isset($memory_object_list[$class])) {
10964 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10965 require_once DOL_DOCUMENT_ROOT . $path;
10966 if (class_exists($class)) {
10967 $memory_object_list[$class] = array(
10968 'list' => array(),
10969 );
10970 }
10971 }
10972 }
10973
10974 // fetch object and set substitution
10975 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
10976 if (method_exists($class, $method)) {
10977 if (!isset($memory_object_list[$class]['list'][$id])) {
10978 $tmpobj = new $class($db);
10979 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10980 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
10981 $memory_object_list[$class]['list'][$id] = $tmpobj;
10982 } else {
10983 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10984 $tmpobj = $memory_object_list[$class]['list'][$id];
10985 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10986 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
10987 }
10988
10989 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
10990 }
10991 }
10992 }
10993 }
10994 }
10995 }
10996 }
10997
10998 return $text;
10999}
11000
11013function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
11014{
11015 global $conf, $user;
11016
11017 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
11018
11019 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
11020
11021 // Check if there is external substitution to do, requested by plugins
11022 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
11023
11024 foreach ($dirsubstitutions as $reldir) {
11025 $dir = dol_buildpath($reldir, 0);
11026
11027 // Check if directory exists
11028 if (!dol_is_dir($dir)) {
11029 continue;
11030 }
11031
11032 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
11033 foreach ($substitfiles as $substitfile) {
11034 $reg = array();
11035 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
11036 $module = $reg[1];
11037
11038 dol_syslog("Library " . $substitfile['name'] . " found into " . $dir);
11039 // Include the user's functions file
11040 require_once $dir . $substitfile['name'];
11041 // Call the user's function, and only if it is defined
11042 $function_name = $module . "_" . $callfunc;
11043 if (function_exists($function_name)) {
11044 $function_name($substitutionarray, $outputlangs, $object, $parameters);
11045 }
11046 }
11047 }
11048 }
11049 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
11050 // to list all tags in odt template
11051 $tags = '';
11052 foreach ($substitutionarray as $key => $value) {
11053 $tags .= '{' . $key . '} => ' . $value . "\n";
11054 }
11055 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
11056 }
11057}
11058
11068function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
11069{
11070 print get_date_range($date_start, $date_end, $format, $outputlangs);
11071}
11072
11083function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
11084{
11085 global $langs;
11086
11087 $out = '';
11088
11089 if (!is_object($outputlangs)) {
11090 $outputlangs = $langs;
11091 }
11092
11093 if ($date_start && $date_end) {
11094 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11095 }
11096 if ($date_start && !$date_end) {
11097 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11098 }
11099 if (!$date_start && $date_end) {
11100 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11101 }
11102
11103 return $out;
11104}
11105
11114function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
11115{
11116 $ret = '';
11117 // If order not defined, we use the setup
11118 if ($nameorder < 0) {
11119 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
11120 }
11121 if ($nameorder == 1) {
11122 $ret .= $firstname;
11123 if ($firstname && $lastname) {
11124 $ret .= ' ';
11125 }
11126 $ret .= $lastname;
11127 } elseif ($nameorder == 2 || $nameorder == 3) {
11128 $ret .= $firstname;
11129 if (empty($ret) && $nameorder == 3) {
11130 $ret .= $lastname;
11131 }
11132 } else { // 0, 4 or 5
11133 $ret .= $lastname;
11134 if (empty($ret) && $nameorder == 5) {
11135 $ret .= $firstname;
11136 }
11137 if ($nameorder == 0) {
11138 if ($firstname && $lastname) {
11139 $ret .= ' ';
11140 }
11141 $ret .= $firstname;
11142 }
11143 }
11144 return $ret;
11145}
11146
11147
11160function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
11161{
11162 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
11163 if (!is_array($mesgs)) {
11164 $mesgs = trim((string) $mesgs);
11165 // If mesgs is a not an empty string
11166 if ($mesgs) {
11167 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
11168 return;
11169 }
11170 if ($attop) {
11171 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11172 } else {
11173 $_SESSION['dol_events'][$style][] = $mesgs;
11174 }
11175 }
11176 } else {
11177 // If mesgs is an array
11178 foreach ($mesgs as $mesg) {
11179 $mesg = trim((string) $mesg);
11180 if ($mesg) {
11181 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
11182 return;
11183 }
11184 if ($attop) {
11185 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11186 } else {
11187 $_SESSION['dol_events'][$style][] = $mesg;
11188 }
11189 }
11190 }
11191 }
11192}
11193
11207function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
11208{
11209 if (empty($mesg) && empty($mesgs)) {
11210 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
11211 } else {
11212 if ($messagekey) {
11213 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
11214 // TODO
11215 $mesg .= '';
11216 }
11217 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE" . $messagekey])) {
11218 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
11219 dol_print_error(null, 'Bad parameter style=' . $style . ' for setEventMessages');
11220 }
11221 if (empty($mesgs)) {
11222 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
11223 } else {
11224 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
11225 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
11226 }
11227 setEventMessage($mesgs, $style, $noduplicate, $attop);
11228 }
11229 }
11230 }
11231}
11232
11242function dol_htmloutput_events($disabledoutputofmessages = 0)
11243{
11244 // Show mesgs
11245 if (isset($_SESSION['dol_events']['mesgs'])) {
11246 if (empty($disabledoutputofmessages)) {
11247 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
11248 }
11249 unset($_SESSION['dol_events']['mesgs']);
11250 }
11251 // Show errors
11252 if (isset($_SESSION['dol_events']['errors'])) {
11253 if (empty($disabledoutputofmessages)) {
11254 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
11255 }
11256 unset($_SESSION['dol_events']['errors']);
11257 }
11258
11259 // Show warnings
11260 if (isset($_SESSION['dol_events']['warnings'])) {
11261 if (empty($disabledoutputofmessages)) {
11262 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
11263 }
11264 unset($_SESSION['dol_events']['warnings']);
11265 }
11266}
11267
11282function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
11283{
11284 global $conf, $langs;
11285
11286 $ret = 0;
11287 $return = '';
11288 $out = '';
11289 $divstart = $divend = '';
11290
11291 // If inline message with no format, we add it.
11292 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
11293 $divstart = '<div class="' . $style . ' clearboth">';
11294 $divend = '</div>';
11295 }
11296
11297 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
11298 $langs->load("errors");
11299 $out .= $divstart;
11300 if (is_array($mesgarray) && count($mesgarray)) {
11301 foreach ($mesgarray as $message) {
11302 $ret++;
11303 $out .= $langs->trans($message);
11304 if ($ret < count($mesgarray)) {
11305 $out .= "<br>\n";
11306 }
11307 }
11308 }
11309 if ($mesgstring) {
11310 $ret++;
11311 $out .= $langs->trans($mesgstring);
11312 }
11313 $out .= $divend;
11314 }
11315
11316 if ($out) {
11317 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
11318 $return = '<script nonce="' . getNonce() . '">
11319 $(document).ready(function() {
11320 /* jnotify(message, preset of message type, keepmessage) */
11321 $.jnotify("' . dol_escape_js($out) . '", "' . ($style == "ok" ? 3000 : $style) . '", ' . ($style == "ok" ? "false" : "true") . ',{ remove: function (){} } );
11322 });
11323 </script>';
11324 } else {
11325 $return = $out;
11326 }
11327 }
11328
11329 return $return;
11330}
11331
11343function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11344{
11345 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11346}
11347
11361function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
11362{
11363 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
11364 return;
11365 }
11366
11367 $iserror = 0;
11368 $iswarning = 0;
11369 if (is_array($mesgarray)) {
11370 foreach ($mesgarray as $val) {
11371 if ($val && preg_match('/class="error"/i', $val)) {
11372 $iserror++;
11373 break;
11374 }
11375 if ($val && preg_match('/class="warning"/i', $val)) {
11376 $iswarning++;
11377 break;
11378 }
11379 }
11380 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
11381 $iserror++;
11382 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
11383 $iswarning++;
11384 }
11385 if ($style == 'error' || $style == 'errors') {
11386 $iserror++;
11387 }
11388 if ($style == 'warning' || $style == 'warnings') {
11389 $iswarning++;
11390 }
11391
11392 if ($iserror || $iswarning) {
11393 // Remove div from texts
11394 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
11395 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
11396 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
11397 // Remove div from texts array
11398 if (is_array($mesgarray)) {
11399 $newmesgarray = array();
11400 foreach ($mesgarray as $val) {
11401 if (is_string($val)) {
11402 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
11403 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
11404 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
11405 $newmesgarray[] = $tmpmesgstring;
11406 } else {
11407 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
11408 }
11409 }
11410 $mesgarray = $newmesgarray;
11411 }
11412 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
11413 } else {
11414 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
11415 }
11416}
11417
11429function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11430{
11431 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11432}
11433
11454function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
11455{
11456 // Clean parameters
11457 $order = strtolower($order);
11458
11459 if (is_array($array)) {
11460 $sizearray = count($array);
11461 if ($sizearray > 0) {
11462 // Build a temp array with sorting key as value
11463 $temp = array();
11464 foreach (array_keys($array) as $key) {
11465 $tmpmultikey = explode(',', $index);
11466 $newindex = $tmpmultikey[0];
11467 if (is_object($array[$key])) {
11468 $temp[$key] = empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex;
11469 // Add other keys
11470 if (!empty($tmpmultikey[1])) {
11471 $newindex = $tmpmultikey[1];
11472 $temp[$key] .= '__' . (empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex);
11473 }
11474 } else {
11475 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11476 $temp[$key] = empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex];
11477 // Add other keys
11478 if (!empty($tmpmultikey[1])) {
11479 $newindex = $tmpmultikey[1];
11480 // @phan-suppress-next-line PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11481 $temp[$key] .= '__' . (empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex]);
11482 }
11483 }
11484 if ($natsort == -1) {
11485 $temp[$key] = '___' . $temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
11486 }
11487 }
11488 if (empty($natsort) || $natsort == -1) {
11489 if ($order == 'asc') {
11490 asort($temp);
11491 } else {
11492 arsort($temp);
11493 }
11494 } else {
11495 if ($case_sensitive) {
11496 natsort($temp);
11497 } else {
11498 natcasesort($temp); // natecasesort is not sensible to case
11499 }
11500 if ($order != 'asc') {
11501 $temp = array_reverse($temp, true);
11502 }
11503 }
11504
11505 $sorted = array();
11506
11507 foreach (array_keys($temp) as $key) {
11508 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
11509 }
11510
11511 return $sorted;
11512 }
11513 }
11514 return $array;
11515}
11516
11517
11525function utf8_check($str)
11526{
11527 $str = (string) $str; // Sometimes string is an int.
11528
11529 // We must use here a binary strlen function (so not dol_strlen)
11530 $strLength = strlen($str);
11531 for ($i = 0; $i < $strLength; $i++) {
11532 if (ord($str[$i]) < 0x80) {
11533 continue; // 0bbbbbbb
11534 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
11535 $n = 1; // 110bbbbb
11536 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
11537 $n = 2; // 1110bbbb
11538 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
11539 $n = 3; // 11110bbb
11540 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
11541 $n = 4; // 111110bb
11542 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
11543 $n = 5; // 1111110b
11544 } else {
11545 return false; // Does not match any model
11546 }
11547 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
11548 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
11549 return false;
11550 }
11551 }
11552 }
11553 return true;
11554}
11555
11563function utf8_valid($str)
11564{
11565 /* 2 other methods to test if string is utf8
11566 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
11567 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
11568 */
11569 return preg_match('//u', $str) ? true : false;
11570}
11571
11572
11579function ascii_check($str)
11580{
11581 if (function_exists('mb_check_encoding')) {
11582 //if (mb_detect_encoding($str, 'ASCII', true) return false;
11583 if (!mb_check_encoding($str, 'ASCII')) {
11584 return false;
11585 }
11586 } else {
11587 if (preg_match('/[^\x00-\x7f]/', $str)) {
11588 return false; // Contains a byte > 7f
11589 }
11590 }
11591
11592 return true;
11593}
11594
11595
11603function dol_osencode($str)
11604{
11605 $tmp = ini_get("unicode.filesystem_encoding");
11606 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
11607 $tmp = 'iso-8859-1'; // By default for windows
11608 }
11609 if (empty($tmp)) {
11610 $tmp = 'utf-8'; // By default for other
11611 }
11612 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
11613 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
11614 }
11615
11616 if ($tmp == 'iso-8859-1') {
11617 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
11618 }
11619 return $str;
11620}
11621
11622
11638function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
11639{
11640 global $conf;
11641
11642 // If key empty
11643 if ($key == '') {
11644 return 0;
11645 }
11646
11647 // Check in cache
11648 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
11649 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
11650 }
11651
11652 dol_syslog('dol_getIdFromCode (value for field ' . $fieldid . ' from key ' . $key . ' not found into cache)', LOG_DEBUG);
11653
11654 $sql = "SELECT " . $fieldid . " as valuetoget";
11655 $sql .= " FROM " . MAIN_DB_PREFIX . $tablename;
11656 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
11657 $sql .= " WHERE " . $fieldkey . " = " . ((int) $key);
11658 } else {
11659 $sql .= " WHERE " . $fieldkey . " = '" . $db->escape($key) . "'";
11660 }
11661 if (!empty($entityfilter)) {
11662 $sql .= " AND entity IN (" . getEntity($tablename) . ")";
11663 }
11664 if ($filters) {
11665 $sql .= $filters;
11666 }
11667
11668 $resql = $db->query($sql);
11669 if ($resql) {
11670 $obj = $db->fetch_object($resql);
11671 $valuetoget = '';
11672 if ($obj) {
11673 $valuetoget = $obj->valuetoget;
11674 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
11675 } else {
11676 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
11677 }
11678 $db->free($resql);
11679
11680 return $valuetoget;
11681 } else {
11682 return -1;
11683 }
11684}
11685
11695function isStringVarMatching($var, $regextext, $matchrule = 1)
11696{
11697 // Tolerate callers (custom modules, older code) that already pass a full regex with delimiters
11698 // like '/^(aaa|bbb)/' instead of the bare body. Without this, the function would build
11699 // '/^/^(aaa|bbb)//' which trips preg_match() with 'Unknown modifier ^'.
11700 $regextext = preg_replace('#^/\^?#', '', (string) $regextext);
11701 $regextext = preg_replace('#\$?/[imsxuADSUXJ]*$#', '', $regextext);
11702
11703 if ($matchrule == 1) {
11704 if ($var == 'mainmenu') {
11705 global $mainmenu;
11706 return (preg_match('/^' . $regextext . '/', $mainmenu));
11707 } elseif ($var == 'leftmenu') {
11708 global $leftmenu;
11709 return (preg_match('/^' . $regextext . '/', $leftmenu));
11710 } else {
11711 return 'This variable is not accessible with dol_eval';
11712 }
11713 } else {
11714 return 'This value '.$matchrule.' for param $matchrule is not yet implemented';
11715 }
11716}
11717
11718
11728function verifCond($strToEvaluate, $onlysimplestring = '1')
11729{
11730 //print $strToEvaluate."<br>\n";
11731 $rights = true;
11732 if (isset($strToEvaluate) && $strToEvaluate !== '') {
11733 //var_dump($strToEvaluate);
11734 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
11735 $rep = dol_eval($strToEvaluate, 1, 1, $onlysimplestring); // The dol_eval() must contains all the "global $xxx;" for all variables $xxx found into the string condition
11736 //var_dump($strToEvaluate, $rep);
11737 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
11738 //var_dump($rights);
11739 }
11740 return $rights;
11741}
11742
11757function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11758{
11759 if ($returnvalue != 1) {
11760 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
11761 }
11762
11763 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
11764 return dol_eval_new($s);
11765 } else {
11766 return dol_eval_standard($s, $hideerrors, $onlysimplestring);
11767 }
11768}
11769
11779function dol_eval_new($s)
11780{
11781 // Only this global variables can be read by eval function and returned to caller
11782 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11783 $db, $langs, $user, $website, $websitepage,
11784 $action, $mainmenu, $leftmenu,
11785 $mysoc,
11786 $objectoffield, // To allow the use of $objectoffield in computed fields
11787
11788 // Old variables used
11789 $object,
11790 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11791
11792 // PHP < 7.4.0
11793 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11794 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11795
11796 // PHP < 8.0.0
11797 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11798 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11799 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11800 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11801 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11802
11803 // PHP < 8.1.0
11804 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11805 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11806 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11807 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11808
11809 // PHP < 8.4.0
11810 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11811 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11812 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11813
11814 $prohibited_token_ids = [
11815 /*
11816 * Prohibited int tokens
11817 */
11818
11819 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11820 'T_ABSTRACT',
11821 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
11822 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
11823 'T_ATTRIBUTE',
11824 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11825 'T_BAD_CHARACTER',
11826 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11827 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11828 'T_CALLABLE',
11829 'T_CATCH',
11830 'T_CLASS',
11831 'T_CLOSE_TAG',
11832 'T_CONST',
11833 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11834 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11835 'T_DECLARE',
11836 'T_DIR',
11837 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11838 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11839 'T_ECHO',
11840 'T_ENDDECLARE',
11841 'T_ENUM',
11842 'T_EVAL',
11843 'T_EXIT',
11844 'T_EXTENDS',
11845 // 'T_FOR', 'T_FOREACH',
11846 'T_FILE',
11847 'T_FINAL',
11848 'T_FINALLY',
11849 'T_FN',
11850 'T_FUNCTION',
11851 'T_FUNC_C',
11852 'T_GLOBAL',
11853 'T_GOTO',
11854 'T_HALT_COMPILER',
11855 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11856 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11857 'T_IMPLEMENTS',
11858 'T_INCLUDE',
11859 'T_INCLUDE_ONCE',
11860 'T_INSTEADOF',
11861 'T_INTERFACE',
11862 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11863 'T_LINE',
11864 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11865 'T_METHOD_C',
11866 // 'T_NEW',
11867 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11868 'T_NAMESPACE',
11869 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11870 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11871 'T_OPEN_TAG',
11872 'T_OPEN_TAG_WITH_ECHO',
11873 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11874 'T_PRINT',
11875 'T_PRIVATE',
11876 'T_PROTECTED',
11877 'T_PUBLIC',
11878 // 'T_PROPERTY_C',
11879 'T_READONLY',
11880 'T_REQUIRE',
11881 'T_REQUIRE_ONCE',
11882 'T_RETURN',
11883 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11884 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11885 'T_STATIC',
11886 'T_THROW',
11887 'T_TRAIT',
11888 'T_TRAIT_C',
11889 'T_TRY',
11890 'T_UNSET',
11891 'T_UNSET_CAST',
11892 'T_USE',
11893 // 'T_VARIABLE',
11894 'T_VAR',
11895 // 'T_WHILE', 'T_WHITESPACE',
11896 // 'T_XOR_EQUAL',
11897 // 'T_YIELD', 'T_YIELD_FROM',
11898
11899 /*
11900 * Prohibited string tokens
11901 */
11902 ';',
11903 '`',
11904 ];
11905
11906 $prohibited_variables = [
11907 '$_COOKIE',
11908 '$_ENV',
11909 '$_FILES',
11910 '$GLOBALS',
11911 '$_GET',
11912 '$_POST',
11913 '$_REQUEST',
11914 '$_SERVER',
11915 '$_SESSION',
11916 ];
11917
11918 $prohibited_functions = [
11919 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11920 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11921 'override_function',
11922 'session_id',
11923 'session_create_id',
11924 'session_regenerate_id',
11925 'call_user_func',
11926 'call_user_func_array', // PREVENT calling forbidden functions
11927 'exec',
11928 'passthru',
11929 'shell_exec',
11930 'system',
11931 'proc_open',
11932 'popen',
11933 'dol_eval',
11934 'dol_eval_new',
11935 'dol_eval_standard',
11936 'dol_contctdesc',
11937 'executeCLI',
11938 'verifCond',
11939 'GETPOST', // Native Dolibarr functions
11940 'create_function',
11941 'assert',
11942 'mb_ereg_replace',
11943 'mb_eregi_replace', // function with eval capabilities
11944 'dol_compress_dir',
11945 'dol_decode',
11946 'dol_delete_file',
11947 'dol_delete_dir',
11948 'dol_delete_dir_recursive',
11949 'dol_copy',
11950 'archiveOrBackupFile', // more dolibarr functions
11951 'fopen',
11952 'file_put_contents',
11953 'fputs',
11954 'fputscsv',
11955 'fwrite',
11956 'fpassthru',
11957 'mkdir',
11958 'rmdir',
11959 'symlink',
11960 'touch',
11961 'unlink',
11962 'umask', // PHP functions related to file operations
11963 'invoke',
11964 'invokeArgs', // Method of ReflectionFunction to execute a function
11965 'filter_input',
11966 'filter_input_array',
11967 'GETPOST', // PREVENT CODE INJECTION
11968 ];
11969
11970 $prohibited_token_arrangements = [
11971 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11972 ' T_VARIABLE ( ',
11973 ' " ( ',
11974 ' \' ( ',
11975 ' T_CONSTANT_ENCAPSED_STRING ( ',
11976 ' ) ( ',
11977 ];
11978
11979 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
11980
11981 $tokens_arrangement = ' ';
11982
11983 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
11984 if (is_array($tokens[$i])) {
11985 $token_id = $tokens[$i][0];
11986 $token_value = $tokens[$i][1];
11987 $token_name = token_name($tokens[$i][0]);
11988 } else {
11989 $token_id = $tokens[$i];
11990 $token_value = $tokens[$i];
11991 $token_name = $tokens[$i];
11992 }
11993
11994 // Ignore whitespaces
11995 if (T_WHITESPACE === $token_id) {
11996 continue;
11997 }
11998
11999 // Keep history to check arrangements
12000 $tokens_arrangement .= "{$token_name} ";
12001
12002 // Prohibited Variables
12003 if (
12004 T_VARIABLE === $token_id
12005 && in_array($token_value, $prohibited_variables, true)
12006 ) {
12007 return "« {$token_value} » is prohibited in « {$s} »";
12008 }
12009
12010 // Prohibited Functions
12011 if (
12012 T_STRING === $token_id
12013 && in_array($token_value, $prohibited_functions, true)
12014 ) {
12015 return "« {$token_value} » is prohibited in « {$s} »";
12016 }
12017 }
12018
12019 // Prohibited Token IDs
12020 $maxi = count($prohibited_token_ids);
12021 for ($i = 0; $i < $maxi; ++$i) {
12022 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
12023 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
12024 }
12025 }
12026
12027 // Prohibited token arrangements
12028 $maxi = count($prohibited_token_arrangements);
12029 for ($i = 0; $i < $maxi; ++$i) {
12030 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
12031 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
12032 }
12033 }
12034
12035 // Return result
12036 try {
12037 return @eval("return {$s};") ?? '';
12038 } catch (Throwable $ex) {
12039 return "Exception during evaluation: " . $s . " - " . $ex->getMessage();
12040 }
12041}
12042
12056function dol_eval_standard($s, $hideerrors = 1, $onlysimplestring = '1')
12057{
12058 // Only this global variables can be read by eval function and returned to caller
12059 // The less we have, the better it is.
12060
12061 global $conf; // TODO Remove this to exclude $conf. We can read $conf->module->enabled with isModEnabled(), $conf->global->xxx properties with getDolGlobalString(), $conf->currency with getDolCurrency(), $conf->entity with getDolEntity()
12062 global $db, $langs, $user, $website, $websitepage;
12063 global $action, $mainmenu, $leftmenu;
12064 global $mysoc;
12065 global $objectoffield; // To allow the use of $objectoffield in computed fields
12066 global $object;
12067
12068 // Old variables (deprecated)
12069 if (getDolGlobalString('MAIN_ALLOW_OLD_VAR_OBJ_IN_DOL_EVAL')) {
12070 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $objectoffield
12071 }
12072
12073 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
12074 if ($onlysimplestring == '0') { // '0' is deprecated, we process it as the more secured '1'
12075 $onlysimplestring = '1';
12076 }
12077 if (!in_array($onlysimplestring, array('1', '2'))) {
12078 return "Bad call of dol_eval. Parameter onlysimplestring must be '1' or '2'.";
12079 }
12080 if (!is_scalar($s)) {
12081 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
12082 }
12083
12084 try {
12085 global $dolibarr_main_restrict_eval_methods;
12086
12087 // Set $dolibarr_main_restrict_eval_methods_array
12088 if (!isset($dolibarr_main_restrict_eval_methods)) {
12089 $dolibarr_main_restrict_eval_methods = 'getDolGlobalString, getDolGlobalInt, getDolCurrency, getDolEntity, getDolDBType, fetchNoCompute, hasRight, isAdmin, isModEnabled, isStringVarMatching, abs, min, max, round, dol_now, preg_match';
12090 }
12091 //print '$dolibarr_main_restrict_eval_methods = '.$dolibarr_main_restrict_eval_methods."\n";
12092 $dolibarr_main_restrict_eval_methods_array = explode(',', str_replace(" ", "", $dolibarr_main_restrict_eval_methods));
12093
12094 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
12095 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
12096 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$objectoffield->canvas == "patient@cabinetmed"'
12097 // We must accept with 2: (($var1 = new Task($db)) && ($var1->fetchNoCompute($object->id) <= 99) && ($var2 = new Project($db)) && ($var2->fetchNoCompute($var1->fk_project) > 0)) ? $var2->ref : "Parent project not found"
12098
12099 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
12100 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
12101 if ($onlysimplestring == '2') {
12102 $specialcharsallowed .= '<[]'; // Later we check that < has space before and after
12103 }
12104 global $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12105 if (!empty($dolibarr_main_allow_unsecured_special_chars_in_dol_eval)) {
12106 $specialcharsallowed .= (string) $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12107 }
12108 if (preg_match('/[^a-z0-9\s' . preg_quote($specialcharsallowed, '/') . ']/i', $s)) {
12109 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): ' . $s;
12110 }
12111
12112 // Check if we found a | without a space before and after
12113 /* Disabled to allow preg_match('/(AAA|BBB)/')
12114 $tmps = str_replace(' || ', '__XXX__', $s);
12115 if (strpos($tmps, '|') !== false) {
12116 return 'Bad string syntax to evaluate (The char | can be used only when duplicated || with a space before and after): ' . $s;
12117 }
12118 */
12119
12120 // Check if there is PHP comments (can be used to obfuscate code)
12121 if (strpos($s, '/*') !== false || strpos($s, '//') !== false) {
12122 return 'Bad string syntax to evaluate (The comment string /* and // are not allowed): ' . $s;
12123 }
12124
12125 // Check if we found a ? without a space before and after
12126 $tmps = str_replace(' ? ', '__XXX__', $s);
12127 if (strpos($tmps, '?') !== false) {
12128 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): ' . $s;
12129 }
12130
12131 // Check if there is a < or <= without spaces after
12132 if (preg_match('/<=?[^\s]/', $s)) {
12133 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found a < or <= without space after): ' . $s;
12134 }
12135
12136 // Check if there is dynamic call (first we use black list patterns)
12137 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
12138 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found a call using "$abc(" or "$abc (" instead of using the direct name of the function): ' . $s;
12139 }
12140
12141 if (empty($dolibarr_main_restrict_eval_methods)) {
12142 // If $dolibarr_main_restrict_eval_methods was set to '', we must check if we try dynamic call
12143
12144 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
12145 $savescheck = '';
12146 $scheck = $s;
12147 while ($scheck && $savescheck != $scheck) {
12148 $savescheck = $scheck;
12149 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
12150 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
12151 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
12152 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12153 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12154 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
12155 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
12156 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
12157 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
12158 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
12159 }
12160 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
12161
12162 // Now test if it remains 1 open parenthesis.
12163 if (strpos($scheck, '(') !== false) {
12164 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found call of a function or method without using the direct name of the function): ' . $s;
12165 }
12166 }
12167
12168 if (strpos($s, '`') !== false) {
12169 return 'Bad string syntax to evaluate (backtick char is forbidden): ' . $s;
12170 }
12171
12172 // Disallow also concat operator
12173 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) {
12174 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
12175 return 'Bad string syntax to evaluate (dot char is forbidden if not strictly between 2 numbers): ' . $s;
12176 }
12177 }
12178
12179 // We exclude string using a $ character that are not an expected global or temporary vars, so that are not:
12180 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $var....
12181 $savescheck = '';
12182 $scheck = $s;
12183 while ($scheck && $savescheck != $scheck) {
12184 $savescheck = $scheck;
12185 $scheck = preg_replace('/\$conf->[a-z\_]+->enabled/', '__VARCONFENABLED__', $scheck); // Remove this once $user->module->enabled has been replaced everywhere with isModEnabled.
12186 $scheck = preg_replace('/\$user->id/', '__VARUSERID__', $scheck);
12187 $scheck = preg_replace('/\$user->hasRight/', '__VARUSERHASRIGHT__', $scheck);
12188 $scheck = preg_replace('/\$user->rights/', '__VARUSERHASRIGHT__', $scheck); // Remove this once $user->rights->xxx is replaced everywhere with $user->hasRight()
12189 $scheck = preg_replace('/\$user->admin/', '__VARUSERISADMIN__', $scheck); // Remove this once $user->admin is replaced everywhere with $user->isAdmin()
12190 $scheck = preg_replace('/\‍(\$db\‍)/', '__VARDB__', $scheck);
12191 $scheck = preg_replace('/\$langs/', '__VARLANGSTRANS__', $scheck);
12192 $scheck = preg_replace('/\$mysoc/', '__VARMYSOC__', $scheck);
12193 $scheck = preg_replace('/\$action/', '__VARACTION__', $scheck);
12194 $scheck = preg_replace('/\$mainmenu/', '__VARMAINMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12195 $scheck = preg_replace('/\$leftmenu/', '__VARLEFTMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12196 $scheck = preg_replace('/\$websitepage/', '__VARWEBSITEPAGE__', $scheck);
12197 $scheck = preg_replace('/\$website/', '__VARWEBSITE__', $scheck);
12198 $scheck = preg_replace('/\$objectoffield/', '__VAROBJECTOFFIELD__', $scheck);
12199 $scheck = preg_replace('/\$object/', '__VAROBJECT__', $scheck);
12200 $scheck = preg_replace('/\$var/', '__VARVAR__', $scheck);
12201
12202 // deprecated (now we use $objecf->canvas or $objectoffield->canvas)
12203 $scheck = preg_replace('/\$soc->canvas/', '__VARSOCCANVAS__', $scheck);
12204 $scheck = preg_replace('/\$obj->canvas/', '__VAROBJCANVAS__', $scheck);
12205
12206 // Now test if it remains one '$'
12207 if (strpos($scheck, '$') !== false) {
12208 dol_syslog('Bad string syntax to evaluate (found use of $ not matching pattern: $user->hasRight, ($db), $langs, $mysoc, $action, $mainmenu, $leftmenu, $website, $websitepage, $objectoffield or $var123): ' . $s, LOG_WARNING);
12209 return 'Bad string syntax to evaluate (found use of $ not matching pattern: $user->hasRight, ($db), $langs, $mysoc, $action, $mainmenu, $leftmenu, $website, $websitepage, $objectoffield or $var123): ' . $s;
12210 }
12211 }
12212
12213 // We block use of php exec or php file functions
12214 $forbiddenphpstrings = array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction', 'SplFileObject', 'SplTempFileObject');
12215
12216 if (empty($dolibarr_main_restrict_eval_methods)) { // If forced to ''
12217 // We list all forbidden function as keywords we don't want to see (we don't mind it if is "keyword(" or just "keyword", we don't want "keyword" at all)
12218 // We must exclude all functions that allow to execute another function. This includes all function that has a parameter with type "callable" to avoid things
12219 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
12220 $forbiddenphpfunctions = array();
12221 $forbiddenphpmethods = array();
12222
12223 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
12224 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
12225 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
12226
12227 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("array_all", "array_any", "array_diff_ukey", "array_filter", "array_find", "array_find_key", "array_map", "array_reduce", "array_intersect_uassoc", "array_intersect_ukey", "array_walk", "array_walk_recursive"));
12228 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
12229 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("error_log", "set_error_handler", "set_exception_handler", "libxml_set_external_entity_loader", "register_shutdown_function", "register_tick_function", "unregister_tick_function"));
12230 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
12231 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
12232
12233 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
12234
12235 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
12236 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
12237 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("pcntl_alarm", "pcntl_exec", "pcntl_fork", "pcntl_waitpid", "pcntl_wait", "pcntl_wifexited", "pcntl_wifstopped", "pcntl_wifsignaled", "pcntl_wifcontinued", "pcntl_wexitstatus", "pcntl_wtermsig", "pcntl_wstopsig", "pcntl_signal"));
12238 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("pcntl_signal_get_handler", "pcntl_signal_dispatch", "pcntl_get_last_error", "pcntl_strerror", "pcntl_sigprocmask", "pcntl_sigwaitinfo", "pcntl_sigtimedwait", "pcntl_getpriority", "pcntl_async_signals", "pcntl_unshare", ));
12239 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("putenv", "dl", "apache_child_terminate", "apache_setenv"));
12240 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("posix_kill", "posix_setuid", "posix_setgid"));
12241 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
12242 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
12243 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
12244 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_dir_list", "dol_dir_list_in_database", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
12245 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("chdir", "dir", "fopen", "file", "file_exists", "file_get_contents", "file_put_contents", "fget", "fgetc", "fgetcsv", "fputs", "fputscsv", "fpassthru", "fscanf", "fseek", "fwrite", "is_file", "is_dir", "is_link", "mkdir", "opendir", "rmdir", "scandir", "symlink", "touch", "unlink", "umask"));
12246 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
12247 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) { // We disallow all function that allow to obfuscate the real name of a function
12248 // @phpcs:ignore
12249 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64" . "_" . "decode", "rawurl" . "decode", "url" . "decode", "str" . "_rot13", "hex" . "2bin", "printf", "sprintf")); // name of forbidden functions are split to avoid false positive
12250 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concat", "dol_concatdesc")); // native dolibarr functions
12251 }
12252 // Remove from blacklist the function that are into the whitelist
12253 /*foreach ($forbiddenphpfunctions as $key => $forbiddenphpfunction) {
12254 if (in_array($forbiddenphpfunction, $dolibarr_main_restrict_eval_methods_array)) {
12255 unset($forbiddenphpfunctions[$key]);
12256 }
12257 }*/
12258
12259 $forbiddenphpmethods = array_merge($forbiddenphpmethods, array('invoke', 'invokeArgs')); // Methods of ReflectionFunction to execute a function
12260 // Remove from blacklist the function that are into the whitelist
12261 /*foreach ($forbiddenphpmethods as $key => $forbiddenphpmethod) {
12262 if (in_array($forbiddenphpmethod, $dolibarr_main_restrict_eval_methods_array)) {
12263 unset($forbiddenphpmethods[$key]);
12264 }
12265 }*/
12266
12267 $forbiddenphpregex = 'global\s*\$';
12268 $forbiddenphpregex .= '|';
12269 $forbiddenphpregex .= '\b(' . implode('|', $forbiddenphpfunctions) . ')\b';
12270
12271 $forbiddenphpmethodsregex = '->(' . implode('|', $forbiddenphpmethods) . ')';
12272
12273 // Now scan all forbidden patterns
12274 do {
12275 $oldstringtoclean = $s;
12276 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12277 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12278 $s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12279 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12280 } while ($oldstringtoclean != $s);
12281
12282 if (strpos($s, '__forbiddenstring__') !== false) {
12283 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12284 return 'Bad string syntax to evaluate: ' . $s;
12285 }
12286 }
12287
12288 if (!empty($dolibarr_main_restrict_eval_methods)) {
12289 // Accept only white-listed allowed function and classes
12290 // TODO Get all pattern '/([\s\w]+)\‍(/', then check that $reg[1] is a defined class or a function into a given list
12291 $pattern = '/([\s\w\'\]\"]+)\‍(/';
12292
12293 $matches = array();
12294 preg_match_all($pattern, $s, $matches);
12295
12296 if (count($matches)) {
12297 foreach ($matches[1] as $m) {
12298 $m = trim($m);
12299 if (empty($m)) {
12300 continue;
12301 }
12302 $reg = array();
12303 if (!preg_match('/new ([A-Z][\w]+)/i', $m, $reg)) {
12304 if (!in_array($m, $dolibarr_main_restrict_eval_methods_array)) {
12305 if ($m != "'" && $m != '"') {
12306 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12307 return 'Bad string syntax to evaluate. A function or method "'.$m.'" was called and is not into the parameter $dolibarr_main_restrict_eval_methods of white-listed functions and methods: ' . $s;
12308 }
12309 }
12310 } else {
12311 if (!class_exists($reg[1])) {
12312 dol_syslog('Bad string syntax to evaluate: Class "'.$reg[1].'" does not exist. ' . $s, LOG_WARNING);
12313 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" does not exist. ' . $s;
12314 }
12315 $parents = class_parents($reg[1]); // Get list of parent classes of class we want to check
12316 if (!in_array('CommonObject', $parents)) { // Only classes that inherit CommonObject are ok. This forbid dangerous classes like ReflectionFunction, SplFileObject, ...
12317 dol_syslog('Bad string syntax to evaluate: Class "'.$reg[1].'" is not allowed because only classes extended CommonObject can be used in dynamic evaluation. ' . $s, LOG_WARNING);
12318 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" is not allowed because only classes extended CommonObject can be used in dynamic evaluation. ' . $s;
12319 }
12320 }
12321 }
12322 }
12323
12324 $forbiddenphpregex = 'global\s*\$';
12325 $forbiddenphpregex .= '|'; // or
12326 $forbiddenphpregex .= '}\s*\[';
12327 $forbiddenphpregex .= '|'; // or
12328 $forbiddenphpregex .= '\‍)\s*\‍(';
12329
12330 // Now scan all forbidden patterns
12331 do {
12332 $oldstringtoclean = $s;
12333 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12334 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12335 //$s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12336 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12337 } while ($oldstringtoclean != $s);
12338
12339 if (strpos($s, '__forbiddenstring__') !== false) {
12340 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12341 return 'Bad string syntax to evaluate: ' . $s;
12342 }
12343 }
12344
12345 //print $s."<br>\n";
12346 ob_start(); // An evaluation has no reason to output data
12347 $isObBufferActive = true;
12348 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
12349 $tmpo = ob_get_clean();
12350 $isObBufferActive = false;
12351 if ($tmpo) {
12352 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
12353 }
12354 return $tmps;
12355 } catch (Error $e) {
12356 if ($isObBufferActive) {
12357 // Clean up buffer which was left behind due to exception.
12358 $tmpo = ob_get_clean();
12359 $isObBufferActive = false;
12360 }
12361 $error = 'dol_eval try/catch error : ';
12362 $error .= $e->getMessage();
12363 dol_syslog($error, LOG_WARNING);
12364 return 'Exception during evaluation: ' . $s;
12365 }
12366}
12367
12375function dol_validElement($element)
12376{
12377 return (trim($element) != '');
12378}
12379
12388function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
12389{
12390 if (empty($codelang)) {
12391 return '';
12392 }
12393
12394 if ($codelang == 'auto') {
12395 return '<span class="fa fa-language"></span>';
12396 }
12397
12398 $langtocountryflag = array(
12399 'ar_AR' => '',
12400 'ca_ES' => 'catalonia',
12401 'da_DA' => 'dk',
12402 'fr_CA' => 'mq',
12403 'sv_SV' => 'se',
12404 'sw_SW' => 'unknown',
12405 'AQ' => 'unknown',
12406 'CW' => 'unknown',
12407 'IM' => 'unknown',
12408 'JE' => 'unknown',
12409 'MF' => 'unknown',
12410 'BL' => 'unknown',
12411 'SX' => 'unknown'
12412 );
12413
12414 if (isset($langtocountryflag[$codelang])) {
12415 $flagImage = $langtocountryflag[$codelang];
12416 } else {
12417 $tmparray = explode('_', $codelang);
12418 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
12419 }
12420
12421 $morecss = '';
12422 $reg = array();
12423 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
12424 $morecss = $reg[1];
12425 $moreatt = "";
12426 }
12427
12428 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
12429 return '<span class="flag-sprite ' . strtolower($flagImage) . ($morecss ? ' ' . $morecss : '') . '"' . ($moreatt ? ' ' . $moreatt : '') . (!$notitlealt ? ' title="' . $codelang . '"' : '') . '></span>';
12430}
12431
12439function getLanguageCodeFromCountryCode($countrycode)
12440{
12441 global $mysoc;
12442
12443 if (empty($countrycode)) {
12444 return null;
12445 }
12446
12447 if (strtoupper($countrycode) == 'MQ') {
12448 return 'fr_CA';
12449 }
12450 if (strtoupper($countrycode) == 'SE') {
12451 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
12452 }
12453 if (strtoupper($countrycode) == 'CH') {
12454 if ($mysoc->country_code == 'FR') {
12455 return 'fr_CH';
12456 }
12457 if ($mysoc->country_code == 'DE') {
12458 return 'de_CH';
12459 }
12460 if ($mysoc->country_code == 'IT') {
12461 return 'it_CH';
12462 }
12463 }
12464
12465 // Locale list taken from:
12466 // http://stackoverflow.com/questions/3191664/
12467 // list-of-all-locales-and-their-short-codes
12468 $locales = array(
12469 'af-ZA',
12470 'am-ET',
12471 'ar-AE',
12472 'ar-BH',
12473 'ar-DZ',
12474 'ar-EG',
12475 'ar-IQ',
12476 'ar-JO',
12477 'ar-KW',
12478 'ar-LB',
12479 'ar-LY',
12480 'ar-MA',
12481 'ar-OM',
12482 'ar-QA',
12483 'ar-SA',
12484 'ar-SY',
12485 'ar-TN',
12486 'ar-YE',
12487 //'as-IN', // Moved after en-IN
12488 'ba-RU',
12489 'be-BY',
12490 'bg-BG',
12491 'bn-BD',
12492 //'bn-IN', // Moved after en-IN
12493 'bo-CN',
12494 'br-FR',
12495 'ca-ES',
12496 'co-FR',
12497 'cs-CZ',
12498 'cy-GB',
12499 'da-DK',
12500 'de-AT',
12501 'de-CH',
12502 'de-DE',
12503 'de-LI',
12504 'de-LU',
12505 'dv-MV',
12506 'el-GR',
12507 'en-AU',
12508 'en-BZ',
12509 'en-CA',
12510 'en-GB',
12511 'en-IE',
12512 'en-IN',
12513 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
12514 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
12515 'en-JM',
12516 'en-MY',
12517 'en-NZ',
12518 'en-PH',
12519 'en-SG',
12520 'en-TT',
12521 'en-US',
12522 'en-ZA',
12523 'en-ZW',
12524 'es-AR',
12525 'es-BO',
12526 'es-CL',
12527 'es-CO',
12528 'es-CR',
12529 'es-DO',
12530 'es-EC',
12531 'es-ES',
12532 'es-GT',
12533 'es-HN',
12534 'es-MX',
12535 'es-NI',
12536 'es-PA',
12537 'es-PE',
12538 'es-PR',
12539 'es-PY',
12540 'es-SV',
12541 'es-US',
12542 'es-UY',
12543 'es-VE',
12544 'et-EE',
12545 'eu-ES',
12546 'fa-IR',
12547 'fi-FI',
12548 'fo-FO',
12549 'fr-BE',
12550 'fr-CA',
12551 'fr-CH',
12552 'fr-FR',
12553 'fr-LU',
12554 'fr-MC',
12555 'fy-NL',
12556 'ga-IE',
12557 'gd-GB',
12558 'gl-ES',
12559 'gu-IN',
12560 'he-IL',
12561 'hi-IN',
12562 'hr-BA',
12563 'hr-HR',
12564 'hu-HU',
12565 'hy-AM',
12566 'id-ID',
12567 'ig-NG',
12568 'ii-CN',
12569 'is-IS',
12570 'it-CH',
12571 'it-IT',
12572 'ja-JP',
12573 'ka-GE',
12574 'kk-KZ',
12575 'kl-GL',
12576 'km-KH',
12577 'kn-IN',
12578 'ko-KR',
12579 'ky-KG',
12580 'lb-LU',
12581 'lo-LA',
12582 'lt-LT',
12583 'lv-LV',
12584 'mi-NZ',
12585 'mk-MK',
12586 'ml-IN',
12587 'mn-MN',
12588 'mr-IN',
12589 'ms-BN',
12590 'ms-MY',
12591 'mt-MT',
12592 'nb-NO',
12593 'ne-NP',
12594 'nl-BE',
12595 'nl-NL',
12596 'nn-NO',
12597 'oc-FR',
12598 'or-IN',
12599 'pa-IN',
12600 'pl-PL',
12601 'ps-AF',
12602 'pt-BR',
12603 'pt-PT',
12604 'rm-CH',
12605 'ro-MD',
12606 'ro-RO',
12607 'ru-RU',
12608 'rw-RW',
12609 'sa-IN',
12610 'se-FI',
12611 'se-NO',
12612 'se-SE',
12613 'si-LK',
12614 'sk-SK',
12615 'sl-SI',
12616 'sq-AL',
12617 'sv-FI',
12618 'sv-SE',
12619 'sw-KE',
12620 'ta-IN',
12621 'te-IN',
12622 'th-TH',
12623 'tk-TM',
12624 'tn-ZA',
12625 'tr-TR',
12626 'tt-RU',
12627 'ug-CN',
12628 'uk-UA',
12629 'ur-PK',
12630 'vi-VN',
12631 'wo-SN',
12632 'xh-ZA',
12633 'yo-NG',
12634 'zh-CN',
12635 'zh-HK',
12636 'zh-MO',
12637 'zh-SG',
12638 'zh-TW',
12639 'zu-ZA',
12640 );
12641
12642 $buildprimarykeytotest = strtolower($countrycode) . '-' . strtoupper($countrycode);
12643 if (in_array($buildprimarykeytotest, $locales)) {
12644 return strtolower($countrycode) . '_' . strtoupper($countrycode);
12645 }
12646
12647 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
12648 foreach ($locales as $locale) {
12649 $locale_language = locale_get_primary_language($locale);
12650 $locale_region = locale_get_region($locale);
12651 if (strtoupper($countrycode) == $locale_region) {
12652 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
12653 return strtolower($locale_language) . '_' . strtoupper($locale_region);
12654 }
12655 }
12656 } else {
12657 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
12658 }
12659
12660 return null;
12661}
12662
12693function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
12694{
12695 global $hookmanager, $db;
12696
12697 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
12698 foreach ($conf->modules_parts['tabs'][$type] as $value) {
12699 $values = explode(':', $value);
12700
12701 $reg = array();
12702 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
12703 if (count($values) !== 6) {
12704 dol_syslog('The module_parts["tabs"] entries must be composed of 6 values separated by ":", but got "' . $value . '". Please check your module descriptor classes.', LOG_ERR);
12705 continue;
12706 }
12707
12708 // new declaration with permissions:
12709 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12710 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12711 if ($values[0] != $type) {
12712 continue;
12713 }
12714
12715 $newtab = array();
12716 $postab = $h;
12717 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
12718 $str = $values[1];
12719 $posstart = strpos($str, '(');
12720 if ($posstart > 0) {
12721 $posend = strpos($str, ')');
12722 if ($posstart > 0) {
12723 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
12724 if (is_numeric($res1)) {
12725 $postab = (int) $res1;
12726 $values[1] = '+' . substr($str, $posend + 1);
12727 }
12728 }
12729 }
12730
12731 global $objectoffield; // So we can use $objectoffield int verifCond
12732 $objectoffield = $object;
12733
12734 if (!verifCond($values[4], '2')) {
12735 continue;
12736 }
12737
12738 if ($values[3]) {
12739 if ($filterorigmodule) { // If a filter of module origin has been requested
12740 if (strpos($values[3], '@')) { // This is an external module
12741 if ($filterorigmodule != 'external') {
12742 continue;
12743 }
12744 } else { // This looks a core module
12745 if ($filterorigmodule != 'core') {
12746 continue;
12747 }
12748 }
12749 }
12750 $langs->load($values[3]);
12751 }
12752
12753 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
12754 // If label is "SUBSTITUION_..."
12755 $substitutionarray = array();
12756 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
12757 $label = make_substitutions($reg[1], $substitutionarray);
12758 } else {
12759 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
12760 $labeltemp = explode(',', $values[2]);
12761 $label = $langs->trans($labeltemp[0]);
12762
12763 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
12764 dol_include_once($labeltemp[2]);
12765 $classtoload = $labeltemp[1];
12766 if (class_exists($classtoload)) {
12767 $obj = new $classtoload($db);
12768 $function = $labeltemp[3];
12769 if ($obj && $function && method_exists($obj, $function)) {
12770 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12771 $nbrec = $obj->$function($object->id, $obj);
12772 if (!empty($nbrec)) {
12773 $label .= '<span class="badge marginleftonlyshort">' . $nbrec . '</span>';
12774 }
12775 }
12776 }
12777 }
12778 }
12779 $url = preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]);
12780 $link = parse_url($url);
12781 $query = [];
12782 if (isset($link['query'])) {
12783 parse_str($link['query'], $query);
12784 }
12785 $newtab[0] = dolBuildUrl(dol_buildpath($link['path'], 1), $query);
12786 $newtab[1] = $label;
12787 $newtab[2] = str_replace('+', '', $values[1]);
12788 $h++;
12789
12790 // set tab at its position
12791 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
12792 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
12793 if ($values[0] != $type) {
12794 continue;
12795 }
12796 $tabname = str_replace('-', '', $values[1]);
12797 foreach ($head as $key => $val) {
12798 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
12799 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
12800 if ($head[$key][2] == $tabname && $condition) {
12801 unset($head[$key]);
12802 break;
12803 }
12804 }
12805 }
12806 }
12807 }
12808
12809 // No need to make a return $head. Var is modified as a reference
12810 if (!empty($hookmanager)) {
12811 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
12812 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
12813 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
12814 if ($reshook > 0) { // Hook ask to replace completely the array
12815 $head = $hookmanager->resArray;
12816 } else { // Hook
12817 $head = array_merge($head, $hookmanager->resArray);
12818 }
12819 $h = count($head);
12820 }
12821}
12822
12834function printCommonFooter($zone = 'private')
12835{
12836 global $conf, $hookmanager, $user, $langs;
12837 global $action;
12838 global $micro_start_time;
12839
12840 if ($zone == 'private') {
12841 print "\n" . '<!-- Common footer for private page -->' . "\n";
12842 } else {
12843 print "\n" . '<!-- Common footer for public page -->' . "\n";
12844 }
12845
12846 // A div to store page_y POST parameter so we can read it using javascript
12847 print "\n<!-- A div to store page_y POST parameter -->\n";
12848 print '<div id="page_y" style="display: none;">' . (GETPOST('page_y') ? GETPOST('page_y') : '') . '</div>' . "\n";
12849
12850 $parameters = array('zone' => $zone);
12851 $tmpobject = null;
12852 // @phan-suppress-next-line PhanPluginConstantVariableNull
12853 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters, $tmpobject, $action); // Note that $action and $object may have been modified by some hooks
12854 if (empty($reshook)) {
12855 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
12856 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
12857 }
12858
12859 print "\n";
12860 if (!empty($conf->use_javascript_ajax)) {
12861 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
12862 print '<script>' . "\n";
12863 print 'jQuery(document).ready(function() {' . "\n";
12864
12865 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
12866 print "\n";
12867 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */' . "\n";
12868 print 'jQuery("li.menuhider").click(function(event) {';
12869 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }' . "\n";
12870 print ' console.log("We click on .menuhider");' . "\n";
12871 print ' $("body").toggleClass("sidebar-collapse")' . "\n";
12872 print '});' . "\n";
12873 }
12874
12875 // Management of focus and mandatory for fields
12876 if ($action == 'create' || $action == 'add' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))) || ((empty($action) || $action == 'addline') && (preg_match('/card\.php/', $_SERVER["PHP_SELF"])))) {
12877 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */' . "\n";
12878 $relativepathstring = $_SERVER["PHP_SELF"];
12879 // Clean $relativepathstring
12880 if (constant('DOL_URL_ROOT')) {
12881 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
12882 }
12883 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
12884 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
12885 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
12886
12887 if (!empty($user->default_values[$relativepathstring]['focus'])) {
12888 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
12889 $qualified = 0;
12890 if ($defkey != '_noquery_') {
12891 $tmpqueryarraytohave = explode('&', $defkey);
12892 $foundintru = 0;
12893 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12894 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12895 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12896 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12897 $foundintru = 1;
12898 }
12899 }
12900 if (!$foundintru) {
12901 $qualified = 1;
12902 }
12903 //var_dump($defkey.'-'.$qualified);
12904 } else {
12905 $qualified = 1;
12906 }
12907
12908 if ($qualified) {
12909 print 'console.log("set the focus by executing jQuery(...).focus();")' . "\n";
12910 foreach ($defval as $paramkey => $paramval) {
12911 // Set focus on field
12912 print 'jQuery("input[name=\'' . $paramkey . '\']").focus();' . "\n";
12913 print 'jQuery("textarea[name=\'' . $paramkey . '\']").focus();' . "\n"; // TODO KO with ckeditor
12914 print 'jQuery("select[name=\'' . $paramkey . '\']").focus();' . "\n"; // Not really useful, but we keep it in case of.
12915 }
12916 }
12917 }
12918 }
12919 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12920 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12921 $qualified = 0;
12922 if ($defkey != '_noquery_') {
12923 $tmpqueryarraytohave = explode('&', $defkey);
12924 $foundintru = 0;
12925 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12926 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12927 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12928 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12929 $foundintru = 1;
12930 }
12931 }
12932 if (!$foundintru) {
12933 $qualified = 1;
12934 }
12935 //var_dump($defkey.'-'.$qualified);
12936 } else {
12937 $qualified = 1;
12938 }
12939
12940 if ($qualified) {
12941 print 'console.log("set the js code to manage fields that are set as mandatory");' . "\n";
12942
12943 foreach ($defval as $paramkey => $paramval) {
12944 // Solution 1: Add handler on submit to check if mandatory fields are empty
12945 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12946 print "form.on('submit', function(event) {
12947 var submitter = \$(this).find(':submit:focus').get(0);
12948 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12949
12950 if (buttonName == 'cancel') {
12951 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12952 return true;
12953 }
12954
12955 console.log('We did not click on cancel button but on something else, we check that field [name=".dol_escape_js($paramkey)."] is not empty');
12956
12957 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12958 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12959
12960 if (tmptypefield == 'textarea') {
12961 // We must instead check the content of ckeditor
12962 var tmpeditor = CKEDITOR.instances['" . dol_escape_js($paramkey) . "'];
12963 if (tmpeditor) {
12964 tmpvalue = tmpeditor.getData();
12965 console.log('For textarea tmpvalue is '+tmpvalue);
12966 }
12967 }
12968
12969 let tmpvalueisempty = false;
12970 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
12971 tmpvalueisempty = true;
12972 }
12973 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
12974 tmpvalueisempty = true;
12975 }
12976 if (tmpvalueisempty && buttonName !== 'cancel') {
12977 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
12978 event.preventDefault(); // Stop submission of form to allow custom code to decide.
12979 event.stopPropagation(); // Stop other handlers.
12980
12981 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
12982
12983 return false;
12984 }
12985 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
12986 return true;
12987 });
12988 \n";
12989
12990 // Solution 2: Add property 'required' on input
12991 // so browser will check value and try to focus on it when submitting the form.
12992 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
12993 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12994 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12995 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
12996 //print '// required on a select works only if key is "", so we add the required attributes but also we reset the key -1 or 0 to an empty string'."\n";
12997 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
12998 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
12999 // Add 'field required' class on closest td for all input elements : input, textarea and select
13000 //print '}, 500);'; // 500 milliseconds delay
13001
13002 // Now set the class "fieldrequired"
13003 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
13004 }
13005
13006 // If we submit using the cancel button, we remove the required attributes
13007 print 'jQuery("input[name=\'cancel\']").click(function() {
13008 console.log("We click on cancel button so removed all required attribute");
13009 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
13010 });' . "\n";
13011 }
13012 }
13013 }
13014 }
13015
13016 print '});' . "\n";
13017
13018 // End of tuning
13019 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
13020 print "\n";
13021 print "/* JS CODE TO ENABLE to add memory info */\n";
13022 print 'window.console && console.log("';
13023 if (getDolGlobalString('MEMCACHED_SERVER')) {
13024 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER') . ' - ';
13025 }
13026 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
13027 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
13028 $micro_end_time = microtime(true);
13029 print ' - Build time: ' . ceil(1000 * ($micro_end_time - $micro_start_time)) . ' ms';
13030 }
13031
13032 if (function_exists("memory_get_usage")) {
13033 print ' - Mem: ' . memory_get_usage(); // Do not use true here, it seems it takes the peak amount
13034 }
13035 if (function_exists("memory_get_peak_usage")) {
13036 print ' - Real mem peak: ' . memory_get_peak_usage(true);
13037 }
13038 if (function_exists("zend_loader_file_encoded")) {
13039 print ' - Zend encoded file: ' . (zend_loader_file_encoded() ? 'yes' : 'no');
13040 }
13041 print '");' . "\n";
13042 }
13043
13044 print "\n" . '</script>' . "\n";
13045
13046 // Google Analytics
13047 // TODO Remove this, can be replaced with the hook printCommonFooter
13048 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
13049 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
13050 foreach ($tmptagarray as $tmptag) {
13051 print "\n";
13052 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
13053 print '
13054 <!-- Global site tag (gtag.js) - Google Analytics -->
13055 <script nonce="' . getNonce() . '" async src="https://www.googletagmanager.com/gtag/js?id=' . trim($tmptag) . '"></script>
13056 <script>
13057 window.dataLayer = window.dataLayer || [];
13058 function gtag(){dataLayer.push(arguments);}
13059 gtag(\'js\', new Date());
13060
13061 gtag(\'config\', \'' . trim($tmptag) . '\');
13062 </script>';
13063 print "\n";
13064 }
13065 }
13066 }
13067
13068 // Add Xdebug coverage of code
13069 if (defined('XDEBUGCOVERAGE')) {
13070 print_r(xdebug_get_code_coverage());
13071 }
13072
13073 // Output string from hooks
13074 if (!empty($hookmanager->resPrint)) {
13075 print $hookmanager->resPrint;
13076 }
13077
13078 // Add DebugBar data
13079 if ($user->hasRight('debugbar', 'read')) {
13080 global $debugbar;
13081 if ($debugbar instanceof DebugBar\DebugBar) {
13082 if (isset($debugbar['time'])) {
13083 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
13084 $debugbar['time']->stopMeasure('pageaftermaster');
13085 }
13086 print '<!-- Output debugbar data -->' . "\n";
13087 $renderer = $debugbar->getJavascriptRenderer();
13088 print $renderer->render();
13089 }
13090 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
13091 print "\n";
13092 print "<!-- Start of log output\n";
13093 //print '<div class="hidden">'."\n";
13094 foreach ($conf->logbuffer as $logline) {
13095 print $logline . "<br>\n";
13096 }
13097 //print '</div>'."\n";
13098 print "End of log output -->\n";
13099 }
13100 }
13101}
13102
13112function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
13113{
13114 if (is_null($string)) {
13115 return array();
13116 }
13117
13118 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
13119 // This is a regex string
13120 $newdelimiter = $delimiter;
13121 } else {
13122 // This is a simple string
13123 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
13124 $newdelimiter = preg_quote($delimiter, '/');
13125 }
13126
13127 if ($a = preg_split('/' . $newdelimiter . '/', $string)) {
13128 $ka = array();
13129 foreach ($a as $s) { // each part
13130 if ($s) {
13131 if ($pos = strpos($s, $kv)) { // key/value delimiter
13132 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
13133 } else { // key delimiter not found
13134 $ka[] = trim($s);
13135 }
13136 }
13137 }
13138 return $ka;
13139 }
13140
13141 return array();
13142}
13143
13151function dolExplodeKeepIfQuotes($input)
13152{
13153 // Use regexp to capture words and section in quotes
13154 $matches = array();
13155 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
13156
13157 // Merge result and delete empty values
13158
13159 $result = array_map(
13166 static function ($a, $b, $c) {
13167 if ($a !== '') {
13168 return $a;
13169 }
13170 if ($b !== '') {
13171 return $b;
13172 }
13173 if ($c !== '') {
13174 return $c;
13175 }
13176 return '';
13177 },
13178 $matches[1],
13179 $matches[2],
13180 $matches[3]
13181 );
13182 return array_values(array_filter(
13183 $result,
13190 static function ($val) {
13191 return $val !== '';
13192 }
13193 ));
13194}
13195
13196
13203function dol_set_focus($selector)
13204{
13205 print "\n" . '<!-- Set focus onto a specific field -->' . "\n";
13206 print '<script nonce="' . getNonce() . '">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("' . dol_escape_js($selector) . '").focus(); });</script>' . "\n";
13207}
13208
13209
13217function dol_getmypid()
13218{
13219 if (!function_exists('getmypid')) {
13220 return mt_rand(99900000, 99965535);
13221 } else {
13222 return getmypid(); // May be a number on 64 bits (depending on OS)
13223 }
13224}
13225
13248function natural_search($fields, $value, $mode = 0, $nofirstand = 0, $sqltoadd = '')
13249{
13250 global $db, $langs;
13251
13252 $value = trim($value);
13253
13254 if ($mode == 0) {
13255 $value = preg_replace('/\*/', '%', $value); // Replace * with %
13256 }
13257 if ($mode == 1) {
13258 $value = preg_replace('/([!<>=]+)\s+([0-9' . preg_quote($langs->trans("SeparatorDecimal"), '/') . '\-])/', '\1\2', $value); // Clean string '< 10' into '<10' so we can then explode on space to get all tests to do
13259 }
13260
13261 $value = preg_replace('/\s*\|\s*/', '|', $value);
13262
13263 // Split criteria on ' ' but not if we are inside quotes.
13264 // For mode 3, the split is done later on the , only and not on the ' '.
13265 if ($mode != -3 && $mode != 3) {
13266 $crits = dolExplodeKeepIfQuotes($value);
13267 } else {
13268 $crits = array($value);
13269 }
13270
13271 $res = '';
13272 if (!is_array($fields)) {
13273 $fields = array($fields);
13274 }
13275 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
13276 foreach ($crits as $crit) { // Loop on each AND criteria
13277 $crit = trim($crit);
13278 $i2 = 0; // count the nb of valid criteria added for this this first criteria
13279 $newres = '';
13280
13281 foreach ($fields as $field) {
13282 if ($mode == 1) {
13283 $tmpcrits = explode('|', $crit);
13284 $i3 = 0; // count the nb of valid criteria added for this current field
13285 foreach ($tmpcrits as $tmpcrit) {
13286 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13287 continue;
13288 }
13289 $tmpcrit = trim($tmpcrit);
13290
13291 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13292
13293 $operator = '=';
13294 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
13295
13296 $reg = array();
13297 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
13298 if (!empty($reg[1])) {
13299 $operator = $reg[1];
13300 }
13301 if ($newcrit != '') {
13302 $numnewcrit = price2num($newcrit);
13303 if (is_numeric($numnewcrit)) {
13304 $newres .= $db->sanitize($field) . ' ' . $operator . ' ' . ((float) $numnewcrit); // should be a numeric
13305 } else {
13306 $newres .= '1 = 2'; // force false, we received a corrupted data
13307 }
13308 $i3++; // a criteria was added to string
13309 }
13310 }
13311 $i2++; // a criteria for 1 more field was added to string
13312 } elseif ($mode == 2 || $mode == -2) {
13313 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
13314 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -2 ? 'NOT ' : '');
13315 $newres .= $crit ? "IN (" . $db->sanitize($db->escape($crit)) . ")" : "IN (0)";
13316 if ($mode == -2) {
13317 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13318 }
13319 $i2++; // a criteria for 1 more field was added to string
13320 } elseif ($mode == 3 || $mode == -3) {
13321 $tmparray = explode(',', $crit);
13322 if (count($tmparray)) {
13323 $listofcodes = '';
13324 foreach ($tmparray as $val) {
13325 $val = trim($val);
13326 if ($val) { // TODO Test with if ($val !== '') {
13327 $listofcodes .= ($listofcodes ? ',' : '');
13328 $listofcodes .= "'" . $db->escape($val) . "'";
13329 }
13330 }
13331 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -3 ? 'NOT ' : '') . "IN (" . $db->sanitize($listofcodes, 1, 0, 1) . ")";
13332 $i2++; // a criteria for 1 more field was added to string
13333 }
13334 if ($mode == -3) {
13335 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13336 }
13337 } elseif ($mode == 4) {
13338 $tmparray = explode(',', $crit);
13339 if (count($tmparray)) {
13340 $listofcodes = '';
13341 foreach ($tmparray as $val) {
13342 $val = trim($val);
13343 if ($val) {
13344 $newres .= ($i2 > 0 ? " OR (" : "(") . $db->sanitize($field) . " LIKE '" . $db->escape($val) . ",%'";
13345 $newres .= ' OR ' . $db->sanitize($field) . " = '" . $db->escape($val) . "'";
13346 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . "'";
13347 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . ",%'";
13348 $newres .= ')';
13349 $i2++; // a criteria for 1 more field was added to string (we can add several criteria for the same field as it is a multiselect search criteria)
13350 }
13351 }
13352 }
13353 } else { // $mode=0
13354 $tmpcrits = explode('|', $crit);
13355 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
13356 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
13357 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13358 continue;
13359 }
13360 $tmpcrit = trim($tmpcrit);
13361
13362 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
13363 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
13364 } else {
13365 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13366 }
13367
13368 $isSellist = false;
13369 $table = $label = $key = null;
13370
13371 if (strpos($field, 'ef.') === 0) {
13372 $extrafieldName = substr($field, 3);
13373 $extrafields = new ExtraFields($db);
13374 $extrafields->fetch_name_optionals_label('product');
13375
13376 if (isset($extrafields->attributes['product']['type'][$extrafieldName]) && $extrafields->attributes['product']['type'][$extrafieldName] === 'sellist') {
13377 $isSellist = true;
13378 $paramArray = $extrafields->attributes['product']['param'][$extrafieldName]['options'] ?? [];
13379 $param = array_key_first($paramArray);
13380 list($table, $label, $key) = explode(':', $param);
13381 }
13382 }
13383
13384 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
13385 $newres .= $db->sanitize($field) . " = " . (is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
13386 } else {
13387 $tmpcrit2 = $tmpcrit;
13388 $tmpbefore = '%';
13389 $tmpafter = '%';
13390 $tmps = '';
13391
13392 if ($isSellist) {
13393 $newres .= $field . " IN (SELECT t." . $key . " FROM " . $db->prefix() . $table . " AS t WHERE t." . $label . " LIKE '%" . $db->escape($tmpcrit2) . "%')";
13394 } else {
13395 if (preg_match('/^!/', $tmpcrit)) {
13396 $tmps .= $db->sanitize($field) . " NOT LIKE "; // ! as exclude character
13397 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
13398 } else {
13399 $tmps .= $db->sanitize($field) . " LIKE ";
13400 }
13401 $tmps .= "'";
13402
13403 if (preg_match('/^[\^\$]/', $tmpcrit)) {
13404 $tmpbefore = '';
13405 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
13406 }
13407 if (preg_match('/[\^\$]$/', $tmpcrit)) {
13408 $tmpafter = '';
13409 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
13410 }
13411
13412 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13413 $tmps = "(" . $tmps;
13414 }
13415 $newres .= $tmps;
13416 $newres .= $tmpbefore;
13417 $newres .= $db->escape($tmpcrit2);
13418 $newres .= $tmpafter;
13419 $newres .= "'";
13420 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13421 $newres .= " OR " . $field . " IS NULL)";
13422 }
13423 }
13424 }
13425
13426 $i3++;
13427 }
13428
13429 $i2++; // a criteria for 1 more field was added to string
13430 }
13431 }
13432
13433 if ($sqltoadd) {
13434 $newres .= ($newres ? '' : ' OR ').str_replace('__KEYTOSEARCH__', $crit, $sqltoadd);
13435 }
13436
13437 if ($newres) {
13438 $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') . $newres . ($i2 > 1 ? ')' : '');
13439 }
13440 $i1++;
13441 }
13442 $res = ($nofirstand ? "" : " AND ") . "(" . $res . ")";
13443
13444 return $res;
13445}
13446
13453function showDirectDownloadLink($object)
13454{
13455 global $langs;
13456
13457 $out = '';
13458 $url = $object->getLastMainDocLink($object->element);
13459
13460 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe') . ' <span class="opacitymedium">' . $langs->trans("DirectDownloadLink") . '</span><br>';
13461 if ($url) {
13462 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="' . $url . '"></div>';
13463 $out .= ajax_autoselect("directdownloadlink", '');
13464 } else {
13465 $out .= '<div class="urllink">' . $langs->trans("FileNotShared") . '</div>';
13466 }
13467
13468 return $out;
13469}
13470
13479function getImageFileNameForSize($file, $extName, $extImgTarget = '')
13480{
13481 $dirName = dirname($file);
13482 if ($dirName == '.') {
13483 $dirName = '';
13484 }
13485
13486 if (!in_array($extName, array('', '_small', '_mini'))) {
13487 return 'Bad parameter extName';
13488 }
13489
13490 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
13491 $fileName = basename($fileName);
13492
13493 if (empty($extImgTarget)) {
13494 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
13495 }
13496 if (empty($extImgTarget)) {
13497 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
13498 }
13499 if (empty($extImgTarget)) {
13500 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
13501 }
13502 if (empty($extImgTarget)) {
13503 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
13504 }
13505 if (empty($extImgTarget)) {
13506 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
13507 }
13508 if (empty($extImgTarget)) {
13509 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
13510 }
13511
13512 if (!$extImgTarget) {
13513 return $file;
13514 }
13515
13516 $subdir = '';
13517 if ($extName) {
13518 $subdir = 'thumbs/';
13519 }
13520
13521 return ($dirName ? $dirName . '/' : '') . $subdir . $fileName . $extName . $extImgTarget; // New filename for thumb
13522}
13523
13524
13534function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
13535{
13536 global $conf, $langs;
13537
13538 if (empty($conf->use_javascript_ajax)) {
13539 return '';
13540 }
13541
13542 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
13543
13544 if ($alldata == 1) {
13545 if ($isAllowedForPreview) {
13546 return array('target' => '_blank', 'css' => 'documentpreview', 'url' => DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : ''), 'mime' => dol_mimetype($relativepath));
13547 } else {
13548 return array();
13549 }
13550 }
13551
13552 // old behavior, return a string
13553 if ($isAllowedForPreview) {
13554 $tmpurl = DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : '');
13555 $title = $langs->transnoentities("Preview");
13556 //$title = '%27-alert(document.domain)-%27'; // An example of js injection into a corrupted title string, that should be blocked by the dol_escape_uri().
13557 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
13558
13559 // We need to do a dol_escape_uri() on the full string after the javascript: because such parts are the URI and when we click on such links, a RFC3986 decode is done,
13560 // by the browser, converting the %27 (like when having param file=abc%27def), or when having a corrupted title), into a ', BEFORE interpreting the content that can be a js code.
13561 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
13562 return 'javascript:' . dol_escape_uri('document_preview(\'' . dol_escape_js($tmpurl) . '\', \'' . dol_escape_js(dol_mimetype($relativepath)) . '\', \'' . dol_escape_js($title) . '\')');
13563 } else {
13564 return '';
13565 }
13566}
13567
13574function getLabelSpecialCode($idcode)
13575{
13576 global $langs;
13577
13578 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
13579 if ($idcode > 10) {
13580 return 'Module ID ' . $idcode;
13581 }
13582 if (!empty($arrayspecialines[$idcode])) {
13583 return $langs->trans($arrayspecialines[$idcode]);
13584 }
13585 return '';
13586}
13587
13596function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
13597{
13598 global $langs;
13599 $out = '<script nonce="' . getNonce() . '">
13600 jQuery(document).ready(function () {
13601 jQuery("' . ((strpos($htmlname, '.') === 0 ? '' : '#') . $htmlname) . '").click(function() { jQuery(this).select(); } );
13602 });
13603 </script>';
13604 if ($addlink) {
13605 if ($textonlink === 'image') {
13606 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . img_picto('', 'globe') . '</a>';
13607 } else {
13608 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . $langs->trans("Link") . '</a>';
13609 }
13610 }
13611 return $out;
13612}
13613
13621function dolIsAllowedForPreview($file)
13622{
13623 // Check .noexe extension in filename
13624 if (preg_match('/\.noexe$/i', $file)) {
13625 return 0;
13626 }
13627
13628 // Check mime types
13629 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp', 'webm', 'mp4');
13630 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
13631 $mime_preview[] = 'svg+xml';
13632 }
13633 //$mime_preview[]='vnd.oasis.opendocument.presentation';
13634 //$mime_preview[]='archive';
13635 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
13636 if ($num_mime !== false) {
13637 return 1;
13638 }
13639
13640 // By default, not allowed for preview
13641 return 0;
13642}
13643
13644
13654function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
13655{
13656 $mime = $default;
13657 $imgmime = 'other.png';
13658 $famime = 'file-o';
13659 $srclang = '';
13660
13661 $tmpfile = preg_replace('/\.noexe$/', '', $file);
13662
13663 // Plain text files
13664 if (preg_match('/\.txt$/i', $tmpfile)) {
13665 $mime = 'text/plain';
13666 $imgmime = 'text.png';
13667 $famime = 'file-alt';
13668 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
13669 $mime = 'text/richtext';
13670 $imgmime = 'text.png';
13671 $famime = 'file-alt';
13672 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
13673 $mime = 'text/csv';
13674 $imgmime = 'text.png';
13675 $famime = 'file-csv';
13676 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
13677 $mime = 'text/tab-separated-values';
13678 $imgmime = 'text.png';
13679 $famime = 'file-alt';
13680 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
13681 $mime = 'text/plain';
13682 $imgmime = 'text.png';
13683 $famime = 'file-alt';
13684 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
13685 $mime = 'text/plain';
13686 $imgmime = 'text.png';
13687 $srclang = 'ini';
13688 $famime = 'file-alt';
13689 } elseif (preg_match('/\.md$/i', $tmpfile)) {
13690 $mime = 'text/plain';
13691 $imgmime = 'text.png';
13692 $srclang = 'md';
13693 $famime = 'file-alt';
13694 } elseif (preg_match('/\.css$/i', $tmpfile)) {
13695 $mime = 'text/css';
13696 $imgmime = 'css.png';
13697 $srclang = 'css';
13698 $famime = 'file-alt';
13699 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
13700 $mime = 'text/plain';
13701 $imgmime = 'text.png';
13702 $srclang = 'lang';
13703 $famime = 'file-alt';
13704 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
13705 $mime = 'text/plain';
13706 $imgmime = 'text.png';
13707 $famime = 'file-alt';
13708 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
13709 $mime = 'text/html';
13710 $imgmime = 'html.png';
13711 $srclang = 'html';
13712 $famime = 'file-alt';
13713 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
13714 $mime = 'text/xml';
13715 $imgmime = 'other.png';
13716 $srclang = 'xml';
13717 $famime = 'file-alt';
13718 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
13719 $mime = 'text/xml';
13720 $imgmime = 'other.png';
13721 $srclang = 'xaml';
13722 $famime = 'file-alt';
13723 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
13724 $mime = 'text/plain';
13725 $imgmime = 'text.png';
13726 $srclang = 'bas';
13727 $famime = 'file-code';
13728 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
13729 $mime = 'text/plain';
13730 $imgmime = 'text.png';
13731 $srclang = 'c';
13732 $famime = 'file-code';
13733 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
13734 $mime = 'text/plain';
13735 $imgmime = 'text.png';
13736 $srclang = 'cpp';
13737 $famime = 'file-code';
13738 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
13739 $mime = 'text/plain';
13740 $imgmime = 'text.png';
13741 $srclang = 'cs';
13742 $famime = 'file-code';
13743 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
13744 $mime = 'text/plain';
13745 $imgmime = 'text.png';
13746 $srclang = 'h';
13747 $famime = 'file-code';
13748 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
13749 $mime = 'text/plain';
13750 $imgmime = 'text.png';
13751 $srclang = 'java';
13752 $famime = 'file-code';
13753 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
13754 $mime = 'text/plain';
13755 $imgmime = 'php.png';
13756 $srclang = 'php';
13757 $famime = 'file-code';
13758 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
13759 $mime = 'text/plain';
13760 $imgmime = 'php.png';
13761 $srclang = 'php';
13762 $famime = 'file-code';
13763 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
13764 $mime = 'text/plain';
13765 $imgmime = 'pl.png';
13766 $srclang = 'perl';
13767 $famime = 'file-code';
13768 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
13769 $mime = 'text/plain';
13770 $imgmime = 'text.png';
13771 $srclang = 'sql';
13772 $famime = 'file-code';
13773 } elseif (preg_match('/\.js$/i', $tmpfile)) {
13774 $mime = 'text/x-javascript';
13775 $imgmime = 'jscript.png';
13776 $srclang = 'js';
13777 $famime = 'file-code';
13778 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
13779 $mime = 'application/vnd.oasis.opendocument.presentation';
13780 $imgmime = 'ooffice.png';
13781 $famime = 'file-powerpoint';
13782 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
13783 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
13784 $imgmime = 'ooffice.png';
13785 $famime = 'file-excel';
13786 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
13787 $mime = 'application/vnd.oasis.opendocument.text';
13788 $imgmime = 'ooffice.png';
13789 $famime = 'file-word';
13790 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
13791 $mime = 'application/msaccess';
13792 $imgmime = 'mdb.png';
13793 $famime = 'file';
13794 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
13795 $mime = 'application/msword';
13796 $imgmime = 'doc.png';
13797 $famime = 'file-word';
13798 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
13799 $mime = 'application/msword';
13800 $imgmime = 'doc.png';
13801 $famime = 'file-word';
13802 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
13803 $mime = 'application/vnd.ms-excel';
13804 $imgmime = 'xls.png';
13805 $famime = 'file-excel';
13806 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
13807 $mime = 'application/vnd.ms-excel';
13808 $imgmime = 'xls.png';
13809 $famime = 'file-excel';
13810 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
13811 $mime = 'application/vnd.ms-excel';
13812 $imgmime = 'xls.png';
13813 $famime = 'file-excel';
13814 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
13815 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
13816 $imgmime = 'xls.png';
13817 $famime = 'file-excel';
13818 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
13819 $mime = 'application/vnd.ms-powerpoint';
13820 $imgmime = 'ppt.png';
13821 $famime = 'file-powerpoint';
13822 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
13823 $mime = 'application/x-mspowerpoint';
13824 $imgmime = 'ppt.png';
13825 $famime = 'file-powerpoint';
13826 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
13827 $mime = 'application/pdf';
13828 $imgmime = 'pdf.png';
13829 $famime = 'file-pdf';
13830 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
13831 $mime = 'text/x-bat';
13832 $imgmime = 'script.png';
13833 $srclang = 'dos';
13834 $famime = 'file-code';
13835 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
13836 $mime = 'text/x-sh';
13837 $imgmime = 'script.png';
13838 $srclang = 'bash';
13839 $famime = 'file-code';
13840 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
13841 $mime = 'text/x-ksh';
13842 $imgmime = 'script.png';
13843 $srclang = 'bash';
13844 $famime = 'file-code';
13845 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
13846 $mime = 'text/x-bash';
13847 $imgmime = 'script.png';
13848 $srclang = 'bash';
13849 $famime = 'file-code';
13850 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
13851 $mime = 'image/x-icon';
13852 $imgmime = 'image.png';
13853 $famime = 'file-image';
13854 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
13855 $mime = 'image/jpeg';
13856 $imgmime = 'image.png';
13857 $famime = 'file-image';
13858 } elseif (preg_match('/\.png$/i', $tmpfile)) {
13859 $mime = 'image/png';
13860 $imgmime = 'image.png';
13861 $famime = 'file-image';
13862 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
13863 $mime = 'image/gif';
13864 $imgmime = 'image.png';
13865 $famime = 'file-image';
13866 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
13867 $mime = 'image/bmp';
13868 $imgmime = 'image.png';
13869 $famime = 'file-image';
13870 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
13871 $mime = 'image/tiff';
13872 $imgmime = 'image.png';
13873 $famime = 'file-image';
13874 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
13875 $mime = 'image/svg+xml';
13876 $imgmime = 'image.png';
13877 $famime = 'file-image';
13878 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
13879 $mime = 'image/webp';
13880 $imgmime = 'image.png';
13881 $famime = 'file-image';
13882 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
13883 $mime = 'text/calendar';
13884 $imgmime = 'other.png';
13885 $famime = 'file-alt';
13886 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
13887 $mime = 'text/calendar';
13888 $imgmime = 'other.png';
13889 $famime = 'file-alt';
13890 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
13891 $mime = 'application/x-bittorrent';
13892 $imgmime = 'other.png';
13893 $famime = 'file-o';
13894 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
13895 $mime = 'audio';
13896 $imgmime = 'audio.png';
13897 $famime = 'file-audio';
13898 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
13899 $mime = 'video/mp4';
13900 $imgmime = 'video.png';
13901 $famime = 'file-video';
13902 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
13903 $mime = 'video/ogg';
13904 $imgmime = 'video.png';
13905 $famime = 'file-video';
13906 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
13907 $mime = 'video/webm';
13908 $imgmime = 'video.png';
13909 $famime = 'file-video';
13910 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
13911 $mime = 'video/x-msvideo';
13912 $imgmime = 'video.png';
13913 $famime = 'file-video';
13914 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
13915 $mime = 'video/divx';
13916 $imgmime = 'video.png';
13917 $famime = 'file-video';
13918 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
13919 $mime = 'video/xvid';
13920 $imgmime = 'video.png';
13921 $famime = 'file-video';
13922 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
13923 $mime = 'video';
13924 $imgmime = 'video.png';
13925 $famime = 'file-video';
13926 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
13927 // application/xxx where zzz is zip, ...
13928 $mime = 'archive';
13929 $imgmime = 'archive.png';
13930 $famime = 'file-archive';
13931 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
13932 $mime = 'application/octet-stream';
13933 $imgmime = 'other.png';
13934 $famime = 'file-o';
13935 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
13936 $mime = 'library';
13937 $imgmime = 'library.png';
13938 $famime = 'file-o';
13939 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13940 $mime = 'error';
13941 $imgmime = 'error.png';
13942 $famime = 'file-alt';
13943 }
13944
13945 if ($famime == 'file-o') {
13946 // file-o seems to not work in fontawesome 5
13947 $famime = 'file';
13948 }
13949
13950 // Return mimetype string
13951 switch ((int) $mode) {
13952 case 1:
13953 $tmp = explode('/', $mime);
13954 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13955 case 2:
13956 return $imgmime;
13957 case 3:
13958 return $srclang;
13959 case 4:
13960 return $famime;
13961 }
13962 return $mime;
13963}
13964
13976function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
13977{
13978 global $conf, $db;
13979
13980 $tablename = preg_replace('/^' . preg_quote(MAIN_DB_PREFIX, '/') . '/', '', $tablename); // Clean name of table for backward compatibility.
13981
13982 $dictvalues = (isset($conf->cache['dictvalues_' . $tablename]) ? $conf->cache['dictvalues_' . $tablename] : null);
13983
13984 if (is_null($dictvalues)) {
13985 $dictvalues = array();
13986
13987 $sql = "SELECT * FROM " . MAIN_DB_PREFIX . $tablename . " WHERE 1 = 1"; // Here select * is allowed as it is generic code and we don't have list of fields
13988 if ($checkentity) {
13989 $sql .= ' AND entity IN (0,' . getEntity($tablename) . ')';
13990 }
13991
13992 $resql = $db->query($sql);
13993 if ($resql) {
13994 while ($obj = $db->fetch_object($resql)) {
13995 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
13996 }
13997 } else {
13998 dol_print_error($db);
13999 }
14000
14001 $conf->cache['dictvalues_' . $tablename] = $dictvalues;
14002 }
14003
14004 if (!empty($dictvalues[$id])) {
14005 // Found
14006 $tmp = $dictvalues[$id];
14007 return (property_exists($tmp, $field) ? $tmp->$field : '');
14008 } else {
14009 // Not found
14010 return '';
14011 }
14012}
14013
14020function colorIsLight($stringcolor)
14021{
14022 $stringcolor = str_replace('#', '', $stringcolor);
14023 $res = -1;
14024 if (!empty($stringcolor)) {
14025 $res = 0;
14026 $tmp = explode(',', $stringcolor);
14027 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
14028 $r = $tmp[0];
14029 $g = $tmp[1];
14030 $b = $tmp[2];
14031 } else {
14032 $hexr = $stringcolor[0] . $stringcolor[1];
14033 $hexg = $stringcolor[2] . $stringcolor[3];
14034 $hexb = $stringcolor[4] . $stringcolor[5];
14035 $r = hexdec($hexr);
14036 $g = hexdec($hexg);
14037 $b = hexdec($hexb);
14038 }
14039 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
14040 if ($bright > 0.6) {
14041 $res = 1;
14042 }
14043 }
14044 return $res;
14045}
14046
14055function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
14056{
14057 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
14058 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
14059 if (empty($menuentry['enabled'])) {
14060 return 0; // Entry disabled by condition
14061 }
14062 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
14063 $tmploops = explode('|', $menuentry['module']);
14064 $found = 0;
14065 foreach ($tmploops as $tmploop) {
14066 if (in_array($tmploop, $listofmodulesforexternal)) {
14067 $found++;
14068 break;
14069 }
14070 }
14071 if (!$found) {
14072 return 0; // Entry is for menus all excluded to external users
14073 }
14074 }
14075 if (!$menuentry['perms'] && $type_user) {
14076 return 0; // No permissions and user is external
14077 }
14078 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
14079 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
14080 }
14081 if (!$menuentry['perms']) {
14082 return 2; // No permissions and user is external
14083 }
14084 return 1;
14085}
14086
14094function roundUpToNextMultiple($n, $x = 5)
14095{
14096 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
14097 return (int) $result;
14098}
14099
14111function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
14112{
14113 $csstouse = 'badge';
14114 $csstouse .= (!empty($mode) ? ' badge-' . $mode : '');
14115 $csstouse .= (!empty($type) ? ' badge-' . $type : '');
14116 $csstouse .= (empty($params['css']) ? '' : ' ' . $params['css']);
14117
14118 $attr = array(
14119 'class' => $csstouse
14120 );
14121
14122 if (empty($html)) {
14123 $html = $label;
14124 }
14125
14126 if (!empty($url)) {
14127 $attr['href'] = $url;
14128 }
14129
14130 if ($mode === 'dot') {
14131 $attr['class'] .= ' classfortooltip';
14132 $attr['title'] = $html;
14133 $attr['aria-label'] = $label;
14134 $html = '';
14135 }
14136
14137 // Override attr
14138 if (!empty($params['attr']) && is_array($params['attr'])) {
14139 foreach ($params['attr'] as $key => $value) {
14140 if ($key == 'class') {
14141 $attr['class'] .= ' ' . $value;
14142 } elseif ($key == 'classOverride') {
14143 $attr['class'] = $value;
14144 } else {
14145 $attr[$key] = $value;
14146 }
14147 }
14148 }
14149
14150 // TODO: add hook
14151
14152 // escape all attribute
14153 $attr = array_map('dolPrintHTMLForAttribute', $attr);
14154
14155 $TCompiledAttr = array();
14156 foreach ($attr as $key => $value) {
14157 $TCompiledAttr[] = $key . '="' . $value . '"';
14158 }
14159
14160 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
14161
14162 $tag = !empty($url) ? 'a' : 'span';
14163
14164 return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
14165}
14166
14167
14180function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
14181{
14182 global $conf;
14183
14184 $return = '';
14185 $dolGetBadgeParams = array();
14186
14187 if (!empty($params['badgeParams'])) {
14188 $dolGetBadgeParams = $params['badgeParams'];
14189 }
14190
14191 // TODO : add a hook
14192 if ($displayMode == 0) {
14193 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
14194 } elseif ($displayMode == 1) {
14195 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14196 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
14197 // Use status with images (for backward compatibility)
14198 $return = '';
14199 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : $statusLabel) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
14200 $htmlLabelShort = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : (!empty($statusLabelShort) ? $statusLabelShort : $statusLabel)) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
14201
14202 // For small screen, we always use the short label instead of long label.
14203 if (!empty($conf->dol_optimize_smallscreen)) {
14204 if ($displayMode == 0) {
14205 $displayMode = 1;
14206 } elseif ($displayMode == 4) {
14207 $displayMode = 2;
14208 } elseif ($displayMode == 6) {
14209 $displayMode = 5;
14210 }
14211 }
14212
14213 // For backward compatibility. Image's filename are still in French, so we use this array to convert
14214 $statusImg = array(
14215 'status0' => 'statut0',
14216 'status1' => 'statut1',
14217 'status2' => 'statut2',
14218 'status3' => 'statut3',
14219 'status4' => 'statut4',
14220 'status5' => 'statut5',
14221 'status6' => 'statut6',
14222 'status7' => 'statut7',
14223 'status8' => 'statut8',
14224 'status9' => 'statut9'
14225 );
14226
14227 if (!empty($statusImg[$statusType])) {
14228 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
14229 } else {
14230 $htmlImg = img_picto($statusLabel, $statusType);
14231 }
14232
14233 if ($displayMode === 2) {
14234 $return = $htmlImg . ' ' . $htmlLabelShort;
14235 } elseif ($displayMode === 3) {
14236 $return = $htmlImg;
14237 } elseif ($displayMode === 4) {
14238 $return = $htmlImg . ' ' . $htmlLabel;
14239 } elseif ($displayMode === 5) {
14240 $return = $htmlLabelShort . ' ' . $htmlImg;
14241 } else { // $displayMode >= 6
14242 $return = $htmlLabel . ' ' . $htmlImg;
14243 }
14244 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
14245 // Use new badge
14246 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14247
14248 $dolGetBadgeParams['attr']['class'] = 'badge-status';
14249 if (empty($dolGetBadgeParams['attr']['title'])) {
14250 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
14251 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
14252 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
14253 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
14254 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
14255 }
14256
14257 if ($displayMode == 3) {
14258 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
14259 } elseif ($displayMode === 5) {
14260 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
14261 } else {
14262 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
14263 }
14264 }
14265
14266 return $return;
14267}
14268
14269
14305function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
14306{
14307 global $hookmanager, $action, $object, $langs;
14308
14309 // If $url is an array, we must build a dropdown button or recursively iterate over each value
14310 if (is_array($url)) {
14311 // Loop on $url array to remove entries of disabled modules
14312 foreach ($url as $key => $subbutton) {
14313 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
14314 unset($url[$key]);
14315 }
14316 }
14317
14318 $out = '';
14319
14320 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
14321 foreach ($url as $button) {
14322 if (!empty($button['lang'])) {
14323 $langs->load($button['lang']);
14324 }
14325 $label = $langs->trans($button['label']);
14326 $text = $button['text'] ?? '';
14327 $actionType = $button['actionType'] ?? '';
14328 $tmpUrl = DOL_URL_ROOT . $button['url'] . (empty($params['backtopage']) ? '' : '&amp;backtopage=' . urlencode($params['backtopage']));
14329 $id = $button['id'] ?? '';
14330 $userRight = $button['perm'] ?? 1;
14331 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
14332
14333 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
14334 }
14335 return $out;
14336 }
14337
14338 if (count($url) > 1) {
14339 $out .= '<div class="dropdown inline-block dropdown-holder">';
14340 $out .= '<a style="margin-right: auto;" class="dropdown-toggle classfortooltip butAction' . ($userRight ? '' : 'Refused') . '" title="' . dol_escape_htmltag($label) . '" data-toggle="dropdown">' . ($text ? $text : $label) . '</a>';
14341 $out .= '<div class="dropdown-content">';
14342 foreach ($url as $subbutton) {
14343 if (!empty($subbutton['lang'])) {
14344 $langs->load($subbutton['lang']);
14345 }
14346
14347 if (!empty($subbutton['urlraw'])) {
14348 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14349 } else {
14350 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14351 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14352 }
14353
14354 $subbuttonparam = array();
14355 if (!empty($subbutton['attr'])) {
14356 $subbuttonparam['attr'] = $subbutton['attr'];
14357 }
14358 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
14359
14360 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
14361 }
14362 $out .= "</div>";
14363 $out .= "</div>";
14364 } else {
14365 foreach ($url as $subbutton) { // Should loop on 1 record only
14366 if (!empty($subbutton['lang'])) {
14367 $langs->load($subbutton['lang']);
14368 }
14369
14370 if (!empty($subbutton['urlraw'])) {
14371 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14372 } else {
14373 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14374 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14375 }
14376
14377 $label = $langs->trans($subbutton['label']);
14378 $text = $subbutton['text'] ?? '';
14379 if (empty($text)) {
14380 $text = $label;
14381 $label = '';
14382 }
14383
14384 $out .= dolGetButtonAction($label, $text, 'default', $tmpurl, '', $subbutton['perm'], $params);
14385 }
14386 }
14387
14388 return $out;
14389 }
14390
14391 // Here, $url is a simple link
14392 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
14393 $class = "dropdown-item";
14394 } else {
14395 $class = 'butAction';
14396 if ($actionType == 'edit') {
14397 $class = 'butAction butActionEdit';
14398 } elseif ($actionType == 'email') {
14399 $class = 'butAction butActionEmail';
14400 } elseif ($actionType == 'clone') {
14401 $class = 'butAction butActionClone';
14402 } elseif ($actionType == 'danger' || $actionType == 'delete') {
14403 $class = 'butAction butActionDelete';
14404 if (!empty($url) && strpos($url, 'token=') === false) {
14405 $url .= '&token=' . newToken();
14406 }
14407 }
14408 }
14409 $attr = array(
14410 'class' => $class,
14411 'href' => empty($url) ? '' : $url,
14412 'title' => $label
14413 );
14414
14415 if (empty($text)) {
14416 $text = $label;
14417 $attr['title'] = ''; // if html not set, using label on title is redundant
14418 } else {
14419 $attr['title'] = $label;
14420 $attr['aria-label'] = $label;
14421 }
14422
14423 if (empty($userRight) || $userRight < 0) {
14424 $attr['class'] = 'butActionRefused';
14425 $attr['href'] = '';
14426 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
14427 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '');
14428 $attr['title'] .= ((empty($userRight) && empty($label)) ? $langs->trans('NotEnoughPermissions') : '');
14429 }
14430
14431 if (!empty($id)) {
14432 $attr['id'] = $id;
14433 }
14434
14435 // Override attr
14436 if (!empty($params['attr']) && is_array($params['attr'])) {
14437 foreach ($params['attr'] as $key => $value) {
14438 if ($key == 'class') {
14439 $attr['class'] .= ' ' . $value;
14440 } elseif ($key == 'classOverride') {
14441 $attr['class'] = $value;
14442 } else {
14443 $attr[$key] = $value;
14444 }
14445 }
14446 }
14447
14448 // automatic add tooltip when title is detected
14449 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
14450 $attr['class'] .= ' classfortooltip';
14451 }
14452
14453 // Js Confirm button
14454 if ($userRight && !empty($params['confirm'])) {
14455 if (!is_array($params['confirm'])) {
14456 $params['confirm'] = array();
14457 }
14458
14459 if (empty($params['confirm']['url'])) {
14460 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
14461 }
14462
14463 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
14464 $attr['data-confirm-url'] = $params['confirm']['url'];
14465 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
14466 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
14467 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
14468 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
14469 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
14470 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
14471
14472 $attr['class'] .= ' butActionConfirm';
14473 }
14474
14475 if (isset($attr['href']) && empty($attr['href'])) {
14476 unset($attr['href']);
14477 }
14478
14479 // TODO replace $TCompiledAttr generation by commonHtmlAttributeBuilder given below
14480 $TCompiledAttr = array();
14481 foreach ($attr as $key => $value) {
14482 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
14483 // Not recommended
14484 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
14485 } elseif ($key == 'href') {
14486 $value = dolPrintHTMLForAttributeUrl($value);
14487 } else {
14488 $value = dolPrintHTMLForAttribute($value);
14489 }
14490
14491 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14492 }
14493 // TODO replace $TCompiledAttr generation by uncomment line below and remove old code
14494 // $TCompiledAttr = commonHtmlAttributeBuilder($attr,$params['use_unsecured_unescapedattr'] ?? []);
14495 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
14496
14497 $tag = !empty($attr['href']) ? 'a' : 'span';
14498
14499 $parameters = array(
14500 'TCompiledAttr' => $TCompiledAttr, // array
14501 'compiledAttributes' => $compiledAttributes, // string
14502 'attr' => $attr,
14503 'tag' => $tag,
14504 'label' => $label,
14505 'html' => $text,
14506 'actionType' => $actionType,
14507 'url' => $url,
14508 'id' => $id,
14509 'userRight' => $userRight,
14510 'params' => $params
14511 );
14512
14513 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14514 if ($reshook < 0) {
14515 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
14516 }
14517
14518 if (empty($reshook)) {
14519 if (dol_textishtml($text)) { // If content already HTML encoded
14520 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
14521 } else {
14522 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
14523 }
14524 } else {
14525 return $hookmanager->resPrint;
14526 }
14527}
14528
14564function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
14565{
14566 $TCompiledAttr = array();
14567 if (empty($attr)) {
14568 return [];
14569 }
14570
14571 foreach ($attr as $key => $value) {
14572 // special boolean attributes case
14573 if (in_array($key, getListOfHtmlBooleanAttributes())) {
14574 if ($value) {
14575 $TCompiledAttr[$key] = $key;
14576 }
14577 continue;
14578 }
14579
14580 if (!empty($unescapedAttr) && in_array($key, $unescapedAttr)) {
14581 // Not recommended
14582 $value = dol_htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE);
14583 } elseif ($key == 'href') {
14584 $value = dolPrintHTMLForAttributeUrl((string) $value);
14585 } else {
14586 $value = dolPrintHTMLForAttribute((string) $value);
14587 }
14588
14589 $TCompiledAttr[$key] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14590 }
14591
14592 return $TCompiledAttr;
14593}
14594
14609function getListOfHtmlBooleanAttributes(): array
14610{
14611 return [
14612 // Input / Form
14613 'checked',
14614 'disabled',
14615 'readonly',
14616 'required',
14617 'autofocus',
14618 'multiple',
14619
14620 // Option
14621 'selected',
14622
14623 // Form / General
14624 'novalidate',
14625 'formnovalidate',
14626
14627 // Media
14628 'autoplay',
14629 'controls',
14630 'loop',
14631 'muted',
14632 'playsinline',
14633
14634 // Other elements
14635 'hidden',
14636 'open',
14637 'ismap',
14638 'reversed',
14639 'allowfullscreen',
14640 'itemscope',
14641 'nomodule',
14642 'defer',
14643 'async',
14644 'default',
14645 'inert',
14646 ];
14647}
14648
14649
14658function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
14659{
14660 if (empty($url)) {
14661 return '';
14662 }
14663
14664 $parsedUrl = parse_url($url);
14665 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
14666 return $url;
14667 }
14668
14669 if (!empty($parsedUrl['query'])) {
14670 // Use parse_str() function to parse the string passed via URL
14671 parse_str($parsedUrl['query'], $urlQuery);
14672 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
14673 $url .= '&amp;backtopage=' . urlencode($params['backtopage']);
14674 }
14675 }
14676
14677 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
14678 $url = DOL_URL_ROOT . $url;
14679 }
14680
14681 return $url;
14682}
14683
14684
14691function dolGetButtonTitleSeparator($moreClass = "")
14692{
14693 return '<span class="button-title-separator ' . $moreClass . '" ></span>';
14694}
14695
14702function getFieldErrorIcon($fieldValidationErrorMsg)
14703{
14704 $out = '';
14705 if (!empty($fieldValidationErrorMsg)) {
14706 $out .= '<span class="field-error-icon classfortooltip" title="' . dol_escape_htmltag($fieldValidationErrorMsg, 1) . '" role="alert" >'; // role alert is used for accessibility
14707 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
14708 $out .= '</span>';
14709 }
14710
14711 return $out;
14712}
14713
14726function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
14727{
14728 global $langs, $user;
14729
14730 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
14731 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
14732 return '';
14733 }
14734 // Fix old picto fa-th-list to use fa-grid-vertical instead
14735 if ($iconClass == 'fa fa-th-list imgforviewmode') {
14736 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
14737 }
14738
14739 $class = 'btnTitle';
14740 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
14741 $class .= ' btnTitlePlus';
14742 }
14743 $useclassfortooltip = 1;
14744
14745 if (!empty($params['morecss'])) {
14746 $class .= ' ' . $params['morecss'];
14747 }
14748
14749 $attr = array(
14750 'class' => $class,
14751 'href' => empty($url) ? '' : $url
14752 );
14753
14754 if (!empty($helpText)) {
14755 $attr['title'] = $helpText;
14756 } elseif ($label) { // empty($attr['title']) &&
14757 $attr['title'] = $label;
14758 $useclassfortooltip = 0;
14759 }
14760
14761 if ($status == 2) {
14762 $attr['class'] .= ' btnTitleSelected';
14763 } elseif ($status <= 0) {
14764 $attr['class'] .= ' refused';
14765
14766 $attr['href'] = '';
14767
14768 if ($status == -1) { // disable
14769 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
14770 } elseif ($status == 0) { // Not enough permissions
14771 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
14772 }
14773 }
14774
14775 if (!empty($attr['title']) && $useclassfortooltip) {
14776 $attr['class'] .= ' classfortooltip';
14777 }
14778
14779 if (!empty($id)) {
14780 $attr['id'] = $id;
14781 }
14782
14783 // Override attr
14784 if (!empty($params['attr']) && is_array($params['attr'])) {
14785 foreach ($params['attr'] as $key => $value) {
14786 if ($key == 'class') {
14787 $attr['class'] .= ' ' . $value;
14788 } elseif ($key == 'classOverride') {
14789 $attr['class'] = $value;
14790 } else {
14791 $attr[$key] = $value;
14792 }
14793 }
14794 }
14795
14796 if (isset($attr['href']) && empty($attr['href'])) {
14797 unset($attr['href']);
14798 }
14799
14800 // TODO : add a hook
14801
14802 // Generate attributes with escapement
14803 $TCompiledAttr = array();
14804 foreach ($attr as $key => $value) {
14805 $TCompiledAttr[] = $key . '="' . dol_escape_htmltag($value) . '"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
14806 }
14807
14808 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
14809
14810 $tag = (empty($attr['href']) ? 'span' : 'a');
14811
14812 $button = '<' . $tag . ' ' . $compiledAttributes . '>';
14813 $button .= '<span class="' . $iconClass . ' valignmiddle btnTitle-icon"></span>';
14814 if (!empty($params['forcenohideoftext'])) {
14815 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label' . (empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '') . '">' . $label . '</span>';
14816 }
14817 $button .= '</' . $tag . '>';
14818
14819 return $button;
14820}
14821
14831function getElementProperties($elementType)
14832{
14833 global $conf, $db, $hookmanager;
14834
14835 $regs = array();
14836
14837 //$element_type='facture';
14838
14839 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
14840
14841 // Parse element/subelement
14842 $module = $elementType;
14843 $element = $elementType;
14844 $subelement = $elementType;
14845 $table_element = $elementType;
14846
14847 // If we ask a resource form external module (instead of default path)
14848 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
14849 $element = $subelement = $regs[1];
14850 $module = $regs[2];
14851 }
14852
14853 // If we ask a resource for a string with an element and a subelement
14854 // Example 'project_task'
14855 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
14856 $module = $element = $regs[1];
14857 $subelement = $regs[2];
14858 }
14859
14860 // Object lines will use parent classpath and module ref
14861 if (substr($elementType, -3) == 'det') {
14862 $module = preg_replace('/det$/', '', $element);
14863 $subelement = preg_replace('/det$/', '', $subelement);
14864 $classpath = $module . '/class';
14865 $classfile = $module;
14866 $classname = preg_replace('/det$/', 'Line', $element);
14867 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'supplier_order', 'commandefournisseur'))) {
14868 $classname = preg_replace('/det$/', 'Ligne', $element);
14869 }
14870 }
14871 // For compatibility and to work with non standard path
14872 if ($elementType == "action" || $elementType == "actioncomm") {
14873 $classpath = 'comm/action/class';
14874 $subelement = 'Actioncomm';
14875 $module = 'agenda';
14876 $table_element = 'actioncomm';
14877 } elseif ($elementType == 'cronjob') {
14878 $classpath = 'cron/class';
14879 $module = 'cron';
14880 $table_element = 'cron';
14881 } elseif ($elementType == 'adherent_type') {
14882 $classpath = 'adherents/class';
14883 $classfile = 'adherent_type';
14884 $module = 'adherent';
14885 $subelement = 'adherent_type';
14886 $classname = 'AdherentType';
14887 $table_element = 'adherent_type';
14888 } elseif ($elementType == 'bank_account') {
14889 $classpath = 'compta/bank/class';
14890 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14891 $classfile = 'account';
14892 $classname = 'Account';
14893 } elseif ($elementType == 'bank_line') {
14894 $classpath = 'compta/bank/class';
14895 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14896 $classfile = 'account';
14897 $classname = 'AccountLine';
14898 } elseif ($elementType == 'category') {
14899 $classpath = 'categories/class';
14900 $module = 'categorie';
14901 $subelement = 'categorie';
14902 $table_element = 'categorie';
14903 } elseif ($elementType == 'contact') {
14904 $classpath = 'contact/class';
14905 $classfile = 'contact';
14906 $module = 'societe';
14907 $subelement = 'contact';
14908 $table_element = 'socpeople';
14909 } elseif ($elementType == 'inventory') {
14910 $module = 'product';
14911 $classpath = 'product/inventory/class';
14912 } elseif ($elementType == 'inventoryline') {
14913 $module = 'product';
14914 $classpath = 'product/inventory/class';
14915 $table_element = 'inventorydet';
14916 $parent_element = 'inventory';
14917 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
14918 $module = 'stock';
14919 $classpath = 'product/stock/class';
14920 $classfile = 'entrepot';
14921 $classname = 'Entrepot';
14922 $table_element = 'entrepot';
14923 } elseif ($elementType == 'project') {
14924 $classpath = 'projet/class';
14925 $module = 'projet';
14926 $table_element = 'projet';
14927 } elseif ($elementType == 'project_task') {
14928 $classpath = 'projet/class';
14929 $module = 'projet';
14930 $subelement = 'task';
14931 $table_element = 'projet_task';
14932 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
14933 $classpath = 'compta/facture/class';
14934 $module = 'facture';
14935 $subelement = 'facture';
14936 $table_element = 'facture';
14937 } elseif ($elementType == 'facturedet') {
14938 $classpath = 'compta/facture/class';
14939 $classfile = 'facture';
14940 $classname = 'FactureLigne';
14941 $module = 'facture';
14942 $table_element = 'facturedet';
14943 $parent_element = 'facture';
14944 } elseif ($elementType == 'facturerec'|| $elementType == 'facture_rec') {
14945 $classpath = 'compta/facture/class';
14946 $classfile = 'facture-rec';
14947 $module = 'facture';
14948 $classname = 'FactureRec';
14949 } elseif ($elementType == 'commande' || $elementType == 'order') {
14950 $classpath = 'commande/class';
14951 $module = 'commande';
14952 $subelement = 'commande';
14953 $table_element = 'commande';
14954 } elseif ($elementType == 'commandedet') {
14955 $classpath = 'commande/class';
14956 $classfile = 'commande';
14957 $classname = 'OrderLine';
14958 $module = 'commande';
14959 $table_element = 'commandedet';
14960 $parent_element = 'commande';
14961 } elseif ($elementType == 'propal') {
14962 $classpath = 'comm/propal/class';
14963 $table_element = 'propal';
14964 } elseif ($elementType == 'propaldet') {
14965 $classpath = 'comm/propal/class';
14966 $classfile = 'propal';
14967 $subelement = 'propaleligne';
14968 $module = 'propal';
14969 $table_element = 'propaldet';
14970 $parent_element = 'propal';
14971 } elseif ($elementType == 'shipping') {
14972 $classpath = 'expedition/class';
14973 $classfile = 'expedition';
14974 $classname = 'Expedition';
14975 $module = 'expedition';
14976 $table_element = 'expedition';
14977 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
14978 $classpath = 'expedition/class';
14979 $classfile = 'expedition';
14980 $classname = 'ExpeditionLigne';
14981 $module = 'expedition';
14982 $table_element = 'expeditiondet';
14983 $parent_element = 'expedition';
14984 } elseif ($elementType == 'delivery_note') {
14985 $classpath = 'delivery/class';
14986 $subelement = 'delivery';
14987 $module = 'expedition';
14988 } elseif ($elementType == 'delivery') {
14989 $classpath = 'delivery/class';
14990 $subelement = 'delivery';
14991 $module = 'expedition';
14992 } elseif ($elementType == 'deliverydet') {
14993 // @todo
14994 } elseif ($elementType == 'supplier_proposal') {
14995 $classpath = 'supplier_proposal/class';
14996 $module = 'supplier_proposal';
14997 $element = 'supplierproposal';
14998 $classfile = 'supplier_proposal';
14999 $subelement = 'supplierproposal';
15000 } elseif ($elementType == 'supplier_proposaldet') {
15001 $classpath = 'supplier_proposal/class';
15002 $module = 'supplier_proposal';
15003 $classfile = 'supplier_proposal';
15004 $classname = 'SupplierProposalLine';
15005 $table_element = 'supplier_proposaldet';
15006 $parent_element = 'supplier_proposal';
15007 } elseif ($elementType == 'contract') {
15008 $classpath = 'contrat/class';
15009 $module = 'contrat';
15010 $subelement = 'contrat';
15011 $table_element = 'contract';
15012 } elseif ($elementType == 'contratdet') {
15013 $classpath = 'contrat/class';
15014 $module = 'contrat';
15015 $table_element = 'contratdet';
15016 $parent_element = 'contrat';
15017 } elseif ($elementType == 'mailing') {
15018 $classpath = 'comm/mailing/class';
15019 $module = 'mailing';
15020 $classfile = 'mailing';
15021 $classname = 'Mailing';
15022 $subelement = '';
15023 } elseif ($elementType == 'member' || $elementType == 'adherent') {
15024 $classpath = 'adherents/class';
15025 $module = 'adherent';
15026 $subelement = 'adherent';
15027 $table_element = 'adherent';
15028 } elseif ($elementType == 'subscription') {
15029 $classpath = 'adherents/class';
15030 $classfile = 'subscription';
15031 $module = 'adherent';
15032 $subelement = 'subscription';
15033 $classname = 'Subscription';
15034 $table_element = 'subscription';
15035 } elseif ($elementType == 'usergroup') {
15036 $classpath = 'user/class';
15037 $module = 'user';
15038 } elseif ($elementType == 'mo' || $elementType == 'mrp') {
15039 $classpath = 'mrp/class';
15040 $classfile = 'mo';
15041 $classname = 'Mo';
15042 $module = 'mrp';
15043 $subelement = '';
15044 $table_element = 'mrp_mo';
15045 } elseif ($elementType == 'mrp_production') {
15046 $classpath = 'mrp/class';
15047 $classfile = 'mo';
15048 $classname = 'MoLine';
15049 $module = 'mrp';
15050 $subelement = '';
15051 $table_element = 'mrp_production';
15052 $parent_element = 'mo';
15053 } elseif ($elementType == 'cabinetmed_cons') {
15054 $classpath = 'cabinetmed/class';
15055 $module = 'cabinetmed';
15056 $subelement = 'cabinetmedcons';
15057 $table_element = 'cabinetmedcons';
15058 } elseif ($elementType == 'fichinter') {
15059 $classpath = 'fichinter/class';
15060 $module = 'ficheinter';
15061 $subelement = 'fichinter';
15062 $table_element = 'fichinter';
15063 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
15064 $classpath = 'resource/class';
15065 $module = 'resource';
15066 $subelement = 'dolresource';
15067 $table_element = 'resource';
15068 } elseif ($elementType == 'opensurvey_sondage') {
15069 $classpath = 'opensurvey/class';
15070 $module = 'opensurvey';
15071 $subelement = 'opensurveysondage';
15072 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' || $elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
15073 $classpath = 'fourn/class';
15074 $module = 'fournisseur';
15075 $classfile = 'fournisseur.commande';
15076 $element = 'order_supplier';
15077 $subelement = '';
15078 $classname = 'CommandeFournisseur';
15079 $table_element = 'commande_fournisseur';
15080 } elseif ($elementType == 'commande_fournisseurdet') {
15081 $classpath = 'fourn/class';
15082 $module = 'fournisseur';
15083 $classfile = 'fournisseur.commande';
15084 $element = 'commande_fournisseurdet';
15085 $subelement = '';
15086 $classname = 'CommandeFournisseurLigne';
15087 $table_element = 'commande_fournisseurdet';
15088 $parent_element = 'commande_fournisseur';
15089 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
15090 $classpath = 'fourn/class';
15091 $module = 'fournisseur';
15092 $classfile = 'fournisseur.facture';
15093 $element = 'invoice_supplier';
15094 $subelement = '';
15095 $classname = 'FactureFournisseur';
15096 $table_element = 'facture_fourn';
15097 } elseif ($elementType == 'facture_fourn_det') {
15098 $classpath = 'fourn/class';
15099 $module = 'fournisseur';
15100 $classfile = 'fournisseur.facture';
15101 $element = 'facture_fourn_det';
15102 $subelement = '';
15103 $classname = 'SupplierInvoiceLine';
15104 $table_element = 'facture_fourn_det';
15105 $parent_element = 'invoice_supplier';
15106 } elseif ($elementType == "service") {
15107 $classpath = 'product/class';
15108 $subelement = 'product';
15109 $table_element = 'product';
15110 } elseif ($elementType == 'salary') {
15111 $classpath = 'salaries/class';
15112 $module = 'salaries';
15113 } elseif ($elementType == 'payment_salary') {
15114 $classpath = 'salaries/class';
15115 $classfile = 'paymentsalary';
15116 $classname = 'PaymentSalary';
15117 $module = 'salaries';
15118 } elseif ($elementType == 'productlot') {
15119 $module = 'productbatch';
15120 $classpath = 'product/stock/class';
15121 $classfile = 'productlot';
15122 $classname = 'Productlot';
15123 $element = 'productlot';
15124 $subelement = '';
15125 $table_element = 'product_lot';
15126 } elseif ($elementType == 'societeaccount') {
15127 $classpath = 'societe/class';
15128 $classfile = 'societeaccount';
15129 $classname = 'SocieteAccount';
15130 $module = 'societe';
15131 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
15132 $classpath = 'website/class';
15133 $classfile = 'websitepage';
15134 $classname = 'Websitepage';
15135 $module = 'website';
15136 $subelement = 'websitepage';
15137 $table_element = 'website_page';
15138 } elseif ($elementType == 'fiscalyear') {
15139 $classpath = 'core/class';
15140 $module = 'accounting';
15141 $subelement = 'fiscalyear';
15142 } elseif ($elementType == 'chargesociales') {
15143 $classpath = 'compta/sociales/class';
15144 $module = 'tax';
15145 $table_element = 'chargesociales';
15146 } elseif ($elementType == 'tva') {
15147 $classpath = 'compta/tva/class';
15148 $module = 'tax';
15149 $subdir = '/vat';
15150 $table_element = 'tva';
15151 } elseif ($elementType == 'emailsenderprofile') {
15152 $module = '';
15153 $classpath = 'core/class';
15154 $classfile = 'emailsenderprofile';
15155 $classname = 'EmailSenderProfile';
15156 $table_element = 'c_email_senderprofile';
15157 $subelement = '';
15158 } elseif ($elementType == 'conferenceorboothattendee') {
15159 $classpath = 'eventorganization/class';
15160 $classfile = 'conferenceorboothattendee';
15161 $classname = 'ConferenceOrBoothAttendee';
15162 $module = 'eventorganization';
15163 } elseif ($elementType == 'conferenceorbooth') {
15164 $classpath = 'eventorganization/class';
15165 $classfile = 'conferenceorbooth';
15166 $classname = 'ConferenceOrBooth';
15167 $module = 'eventorganization';
15168 } elseif ($elementType == 'ccountry') {
15169 $module = '';
15170 $classpath = 'core/class';
15171 $classfile = 'ccountry';
15172 $classname = 'Ccountry';
15173 $table_element = 'c_country';
15174 $subelement = '';
15175 } elseif ($elementType == 'ecmfiles') {
15176 $module = 'ecm';
15177 $classpath = 'ecm/class';
15178 $classfile = 'ecmfiles';
15179 $classname = 'Ecmfiles';
15180 $table_element = 'ecmfiles';
15181 $subelement = '';
15182 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
15183 $module = 'knowledgemanagement';
15184 $classpath = 'knowledgemanagement/class';
15185 $classfile = 'knowledgerecord';
15186 $classname = 'KnowledgeRecord';
15187 $table_element = 'knowledgemanagement_knowledgerecord';
15188 $subelement = '';
15189 } elseif ($elementType == 'customer') {
15190 $module = 'societe';
15191 $classpath = 'societe/class';
15192 $classfile = 'client';
15193 $classname = 'Client';
15194 $table_element = 'societe';
15195 $subelement = '';
15196 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
15197 $module = 'societe';
15198 $classpath = 'fourn/class';
15199 $classfile = 'fournisseur';
15200 $classname = 'Fournisseur';
15201 $table_element = 'societe';
15202 $subelement = '';
15203 } elseif ($elementType == 'recruitmentcandidature') {
15204 $module = 'recruitment';
15205 $classfile = 'recruitmentcandidature';
15206 $classpath = 'recruitment/class';
15207 $classname = 'RecruitmentCandidature';
15208 $subelement = 'recruitmentcandidature';
15209 $subdir = '/recruitmentcandidature';
15210 } elseif ($elementType == 'recruitmentjobposition') {
15211 $module = 'recruitment';
15212 $classfile = 'recruitmentjobposition';
15213 $classpath = 'recruitment/class';
15214 $classname = 'RecruitmentJobPosition';
15215 $subelement = 'recruitmentjobposition';
15216 $subdir = '/recruitmentjobposition';
15217 }
15218
15219
15220 if (empty($classfile)) {
15221 $classfile = strtolower($subelement);
15222 }
15223 if (empty($classname)) {
15224 $classname = ucfirst($subelement);
15225 }
15226 if (empty($classpath)) {
15227 $classpath = $module . '/class';
15228 }
15229
15230 //print 'getElementProperties subdir='.$subdir;
15231
15232 // Set dir_output
15233 if ($module && isset($conf->$module)) { // The generic case
15234 if (!empty($conf->$module->multidir_output[$conf->entity])) {
15235 $dir_output = $conf->$module->multidir_output[$conf->entity];
15236 } elseif (!empty($conf->$module->output[$conf->entity])) {
15237 $dir_output = $conf->$module->output[$conf->entity];
15238 } elseif (!empty($conf->$module->dir_output)) {
15239 $dir_output = $conf->$module->dir_output;
15240 }
15241 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
15242 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
15243 } elseif (!empty($conf->$module->temp[$conf->entity])) {
15244 $dir_temp = $conf->$module->temp[$conf->entity];
15245 } elseif (!empty($conf->$module->dir_temp)) {
15246 $dir_temp = $conf->$module->dir_temp;
15247 }
15248 }
15249
15250 // Overwrite value for special cases
15251 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
15252 $dir_output = $conf->fournisseur->commande->dir_output;
15253 $dir_temp = $conf->fournisseur->commande->dir_temp;
15254 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
15255 $dir_output = $conf->fournisseur->facture->dir_output;
15256 $dir_temp = $conf->fournisseur->facture->dir_temp;
15257 }
15258 $dir_output .= $subdir;
15259 $dir_temp .= $subdir;
15260
15261 $elementProperties = array(
15262 'module' => $module,
15263 'element' => $element,
15264 'table_element' => $table_element,
15265 'subelement' => $subelement,
15266 'classpath' => $classpath,
15267 'classfile' => $classfile,
15268 'classname' => $classname,
15269 'dir_output' => $dir_output,
15270 'dir_temp' => $dir_temp,
15271 'parent_element' => $parent_element,
15272 );
15273
15274
15275 // Add hook
15276 if (!is_object($hookmanager)) {
15277 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
15278 $hookmanager = new HookManager($db);
15279 }
15280 $hookmanager->initHooks(array('elementproperties'));
15281
15282
15283 // Hook params
15284 $parameters = array(
15285 'elementType' => $elementType,
15286 'elementProperties' => $elementProperties
15287 );
15288
15289 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
15290
15291 if ($reshook) {
15292 $elementProperties = $hookmanager->resArray;
15293 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
15294 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
15295 }
15296
15297 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
15298 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
15299 unset($hookmanager->contextarray[$key]);
15300 }
15301
15302 return $elementProperties;
15303}
15304
15317function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
15318{
15319 global $db, $conf;
15320
15321 $ret = 0;
15322
15323 $element_prop = getElementProperties($element_type);
15324
15325 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
15326 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
15327 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
15328 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
15329 // of service and we will return properties of a product.
15330 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
15331 } elseif ($element_prop['module'] == 'societeaccount') {
15332 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
15333 } else {
15334 $ismodenabled = isModEnabled($element_prop['module']);
15335 }
15336 //var_dump('element_type='.$element_type);
15337 //var_dump($element_prop);
15338 //var_dump($element_prop['module'].' '.$ismodenabled);
15339 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
15340 if ($useCache === 1 && $element_id > 0
15341 && !empty($conf->cache['fetchObjectByElement'][$element_type])
15342 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15343 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15344 ) {
15345 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
15346 }
15347
15348 dol_include_once('/' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15349
15350 if (class_exists($element_prop['classname'])) {
15351 $className = $element_prop['classname'];
15352 $objecttmp = new $className($db);
15353 '@phan-var-force CommonObject $objecttmp';
15356 if ($element_id > 0 || !empty($element_ref)) {
15357 $ret = $objecttmp->fetch($element_id, $element_ref);
15358 if ($ret >= 0) {
15359 if (empty($objecttmp->module)) {
15360 $objecttmp->module = $element_prop['module'];
15361 }
15362
15363 if ($useCache > 0) {
15364 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
15365 $conf->cache['fetchObjectByElement'][$element_type] = [];
15366 }
15367
15368 // Manage cache limit
15369 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
15370 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
15371 }
15372
15373 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
15374 }
15375
15376 return $objecttmp;
15377 }
15378 } else {
15379 return $objecttmp; // returned an object without fetch
15380 }
15381 } else {
15382 dol_syslog($element_prop['classname'] . ' doesn\'t exists in /' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15383 return -1;
15384 }
15385 }
15386
15387 return $ret;
15388}
15389
15395function getExecutableContent()
15396{
15397 $arrayofregexextension = array(
15398 'htm',
15399 'html',
15400 'shtml',
15401 'js',
15402 'phar',
15403 'php',
15404 'php3',
15405 'php4',
15406 'php5',
15407 'phtml',
15408 'pht',
15409 'pl',
15410 'py',
15411 'cgi',
15412 'ksh',
15413 'sh',
15414 'shtml',
15415 'bash',
15416 'bat',
15417 'cmd',
15418 'wpk',
15419 'exe',
15420 'dmg',
15421 'appimage'
15422 );
15423
15424 return $arrayofregexextension;
15425}
15426
15433function isAFileWithExecutableContent($filename)
15434{
15435 $arrayofregexextension = getExecutableContent();
15436
15437 foreach ($arrayofregexextension as $fileextension) {
15438 if (preg_match('/\.' . preg_quote($fileextension, '/') . '$/i', $filename)) {
15439 return true;
15440 }
15441 }
15442
15443 return false;
15444}
15445
15453function newToken()
15454{
15455 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
15456}
15457
15465function currentToken()
15466{
15467 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
15468}
15469
15475function getNonce()
15476{
15477 global $conf;
15478
15479 if (empty($conf->cache['nonce'])) {
15480 include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
15481 $conf->cache['nonce'] = dolGetRandomBytes(8);
15482 }
15483
15484 return $conf->cache['nonce'];
15485}
15486
15487
15501function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
15502{
15503 global $langs;
15504
15505 print '<div class="div-table-responsive-no-min">';
15506 print '<table class="noborder centpercent">';
15507 print '<tr class="liste_titre">';
15508
15509 print ($emptyColumns < 1) ? '<th>' : '<th colspan="' . ($emptyColumns + 1) . '">';
15510
15511 print '<span class="valignmiddle">' . $langs->trans($header) . '</span>';
15512
15513 if (!empty($link)) {
15514 if (!empty($arguments)) {
15515 print '<a href="' . DOL_URL_ROOT . '/' . $link . '?' . $arguments . '">';
15516 } else {
15517 print '<a href="' . DOL_URL_ROOT . '/' . $link . '">';
15518 }
15519 }
15520
15521 if ($number > -1) {
15522 print '<span class="badge marginleftonlyshort">' . $number . '</span>';
15523 } elseif (!empty($link)) {
15524 print '<span class="badge marginleftonlyshort">...</span>';
15525 }
15526
15527 if (!empty($link)) {
15528 print '</a>';
15529 }
15530
15531 print '</th>';
15532
15533 if ($number < 0 && !empty($link)) {
15534 print '<th class="right">';
15535 print '</th>';
15536 }
15537
15538 print '</tr>';
15539}
15540
15549function finishSimpleTable($addLineBreak = false)
15550{
15551 print '</table>';
15552 print '</div>';
15553
15554 if ($addLineBreak) {
15555 print '<br>';
15556 }
15557}
15558
15570function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
15571{
15572 global $langs;
15573
15574 if ($num === 0) {
15575 print '<tr class="oddeven">';
15576 print '<td colspan="' . $tableColumnCount . '"><span class="opacitymedium">' . $langs->trans($noneWord) . '</span></td>';
15577 print '</tr>';
15578 return;
15579 }
15580
15581 if ($nbofloop === 0) {
15582 // don't show a summary line
15583 return;
15584 }
15585
15586 /* Case already handled above, commented to satisfy phpstan.
15587 if ($num === 0) {
15588 $colspan = $tableColumnCount;
15589 } else
15590 */
15591 if ($num > $nbofloop) {
15592 $colspan = $tableColumnCount;
15593 } else {
15594 $colspan = $tableColumnCount - 1;
15595 }
15596
15597 if ($extraRightColumn) {
15598 $colspan--;
15599 }
15600
15601 print '<tr class="liste_total">';
15602
15603 if ($nbofloop > 0 && $num > $nbofloop) {
15604 print '<td colspan="' . $colspan . '" class="right">' . $langs->trans("XMoreLines", ($num - $nbofloop)) . '</td>';
15605 } else {
15606 print '<td colspan="' . $colspan . '" class="right"> ' . $langs->trans("Total") . '</td>';
15607 print '<td class="right centpercent">' . price($total) . '</td>';
15608 }
15609
15610 if ($extraRightColumn) {
15611 print '<td></td>';
15612 }
15613
15614 print '</tr>';
15615}
15616
15625function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
15626{
15627 if ($method == -1) {
15628 $method = 0;
15629 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
15630 $method = 1;
15631 }
15632 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
15633 $method = 2;
15634 }
15635 }
15636
15637 // Be sure we don't have output buffering enabled to have readfile working correctly
15638 while (ob_get_level()) {
15639 ob_end_flush();
15640 }
15641
15642 // Solution 0
15643 if ($method == 0) {
15644 readfile($fullpath_original_file_osencoded);
15645 } elseif ($method == 1) {
15646 // Solution 1
15647 $handle = fopen($fullpath_original_file_osencoded, "rb");
15648 while (!feof($handle)) {
15649 print fread($handle, 8192);
15650 }
15651 fclose($handle);
15652 } elseif ($method == 2) {
15653 // Solution 2
15654 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
15655 $handle2 = fopen("php://output", "wb");
15656 stream_copy_to_stream($handle1, $handle2);
15657 fclose($handle1);
15658 fclose($handle2);
15659 }
15660}
15661
15671function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
15672{
15673 global $langs;
15674
15675 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
15676
15677 $result = '<span class="clipboardCP' . ($showonlyonhover ? ' clipboardCPShowOnHover valignmiddle' : '') . '">';
15678 if ($texttoshow === 'none') {
15679 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15680 $result .= '<span class="clipboardCPValueToPrint"></span>';
15681 } elseif ($texttoshow) {
15682 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15683 $result .= '<span class="clipboardCPValueToPrint">' . dol_escape_htmltag($texttoshow, 1, 1) . '</span>';
15684 } else {
15685 $result .= '<' . $tag . ' class="clipboardCPValue">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15686 }
15687 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="' . dolPrintHTML($langs->trans("ClickToCopyToClipboard")) . '"></span>';
15688 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
15689 $result .= '<span class="clipboardCPText"></span>';
15690 $result .= '</span>';
15691
15692 return $result;
15693}
15694
15695
15703function jsonOrUnserialize($stringtodecode, $assoc = true)
15704{
15705 $result = json_decode($stringtodecode, $assoc);
15706 if ($result === null) {
15707 $result = unserialize($stringtodecode); // For backward compatibility. Is no more used in recent versions.
15708 }
15709
15710 return $result;
15711}
15712
15713
15730function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
15731{
15732 global $db, $user;
15733
15734 if (is_null($filter) || !is_string($filter) || $filter === '') {
15735 return '';
15736 }
15737 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
15738 $filter = '(' . $filter . ')';
15739 }
15740
15741 $regexstring = '\‍(([a-zA-Z0-9_\.]+:[<>!=insotlke]+:[^\‍(\‍)]+)\‍)'; // Must be (aaa:bbb:...) with aaa is a field name (with alias or not) and bbb is one of this operator '=', '<', '>', '<=', '>=', '!=', 'in', 'notin', 'like', 'notlike', 'is', 'isnot'
15742 $firstandlastparenthesis = 0;
15743
15744 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
15745 if ($noerror) {
15746 return '1 = 2';
15747 } else {
15748 return 'Filter syntax error - ' . $errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
15749 }
15750 }
15751
15752 // Test the filter syntax
15753 $t = preg_replace_callback('/' . $regexstring . '/i', 'dolForgeDummyCriteriaCallback', $filter);
15754 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
15755 // If the string result contains something else than '()', the syntax was wrong
15756
15757 if (preg_match('/[^\‍(\‍)]/', $t)) {
15758 $tmperrorstr = 'Bad syntax of the search string';
15759 $errorstr = 'Bad syntax of the search string: ' . $filter;
15760 if ($noerror) {
15761 return '1 = 2';
15762 } else {
15763 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - " . $errorstr, LOG_WARNING);
15764 return 'Filter error - ' . $tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
15765 }
15766 }
15767
15768 $ret = ($noand ? "" : " AND ") . ($nopar ? "" : '(') . preg_replace_callback('/' . $regexstring . '/i', 'dolForgeSQLCriteriaCallback', $filter) . ($nopar ? "" : ')');
15769
15770 if (is_object($db)) {
15771 $ret = str_replace('__NOW__', "'" . $db->idate(dol_now()) . "'", $ret);
15772 }
15773 if (is_object($user)) {
15774 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
15775 }
15776
15777 return $ret;
15778}
15779
15787function dolForgeExplodeAnd($sqlfilters)
15788{
15789 $arrayofandtags = array();
15790 $nbofchars = dol_strlen($sqlfilters);
15791
15792 $error = '';
15793 $parenthesislevel = 0;
15794 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
15795 if (!$result) {
15796 return array();
15797 }
15798 if ($parenthesislevel >= 1) {
15799 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
15800 }
15801
15802 $i = 0;
15803 $s = '';
15804 $countparenthesis = 0;
15805 while ($i < $nbofchars) {
15806 $char = dol_substr($sqlfilters, $i, 1);
15807
15808 if ($char == '(') {
15809 $countparenthesis++;
15810 } elseif ($char == ')') {
15811 $countparenthesis--;
15812 }
15813
15814 if ($countparenthesis == 0) {
15815 $char2 = dol_substr($sqlfilters, $i + 1, 1);
15816 $char3 = dol_substr($sqlfilters, $i + 2, 1);
15817 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
15818 // We found a AND
15819 $s = trim($s);
15820 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15821 $s = '(' . $s . ')';
15822 }
15823 $arrayofandtags[] = $s;
15824 $s = '';
15825 $i += 2;
15826 } else {
15827 $s .= $char;
15828 }
15829 } else {
15830 $s .= $char;
15831 }
15832 $i++;
15833 }
15834 if ($s) {
15835 $s = trim($s);
15836 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15837 $s = '(' . $s . ')';
15838 }
15839 $arrayofandtags[] = $s;
15840 }
15841
15842 return $arrayofandtags;
15843}
15844
15854function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
15855{
15856 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
15857 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
15858 $tmp = $sqlfilters;
15859
15860 $nb = dol_strlen($tmp);
15861 $counter = 0;
15862 $parenthesislevel = 0;
15863
15864 $error = '';
15865
15866 $i = 0;
15867 while ($i < $nb) {
15868 $char = dol_substr($tmp, $i, 1);
15869
15870 if ($char == '(') {
15871 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
15872 // We open a parenthesis and it is the first char
15873 $parenthesislevel++;
15874 }
15875 $counter++;
15876 } elseif ($char == ')') {
15877 $nbcharremaining = ($nb - $i - 1);
15878 if ($nbcharremaining >= $counter) {
15879 $parenthesislevel = min($parenthesislevel, $counter - 1);
15880 }
15881 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
15882 $parenthesislevel = $counter;
15883 }
15884 $counter--;
15885 }
15886
15887 if ($counter < 0) {
15888 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15889 $parenthesislevel = 0;
15890 dol_syslog($error, LOG_WARNING);
15891 return false;
15892 }
15893
15894 $i++;
15895 }
15896
15897 if ($counter > 0) {
15898 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15899 $parenthesislevel = 0;
15900 dol_syslog($error, LOG_WARNING);
15901 return false;
15902 }
15903
15904 return true;
15905}
15906
15914function dolForgeDummyCriteriaCallback($matches)
15915{
15916 //dol_syslog("Convert matches ".$matches[1]);
15917 if (empty($matches[1])) {
15918 return '';
15919 }
15920 $tmp = explode(':', $matches[1]);
15921 if (count($tmp) < 3) {
15922 return '';
15923 }
15924
15925 return '()'; // An empty criteria
15926}
15927
15936function dolForgeSQLCriteriaCallback($matches)
15937{
15938 global $db;
15939
15940 //dol_syslog("Convert matches ".$matches[1]);
15941 if (empty($matches[1])) {
15942 return '';
15943 }
15944 $tmp = explode(':', $matches[1], 3);
15945 if (count($tmp) < 3) {
15946 return '';
15947 }
15948
15949 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
15950
15951 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
15952
15953 $realOperator = [
15954 'NOTLIKE' => 'NOT LIKE',
15955 'ISNOT' => 'IS NOT',
15956 'NOTIN' => 'NOT IN',
15957 '!=' => '<>',
15958 ];
15959
15960 if (array_key_exists($operator, $realOperator)) {
15961 $operator = $realOperator[$operator];
15962 }
15963
15964 $tmpescaped = $tmp[2];
15965
15966 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
15967
15968 $regbis = array();
15969
15970 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID/code/field only (or subrequest if MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTERnot enabled)
15971 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
15972 $tmpescaped2 = '(';
15973 // Explode and sanitize each element in list
15974 $tmpelemarray = explode(',', $tmpescaped);
15975 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
15976 $reg = array();
15977 $tmpelem = trim($tmpelem);
15978 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
15979 $tmpelemarray[$tmpkey] = "'" . $db->escape($db->sanitize($reg[1], 2, 1, 1, 1)) . "'";
15980 } elseif (ctype_digit((string) $tmpelem)) { // if only 0-9 chars, no .
15981 $tmpelemarray[$tmpkey] = (int) $tmpelem;
15982 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
15983 $tmpelemarray[$tmpkey] = (float) $tmpelem;
15984 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
15985 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
15986 } else {
15987 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
15988 }
15989 }
15990 $tmpescaped2 .= implode(',', $tmpelemarray);
15991 $tmpescaped2 .= ')';
15992
15993 $tmpescaped = $tmpescaped2;
15994 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
15995 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
15996 $tmpescaped = $regbis[1];
15997 }
15998 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
15999 $tmpescaped = "'" . $db->escape($tmpescaped) . "'"; // We do not escape the _ and % so the LIKE will work as expected
16000 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
16001 // TODO Retrieve type of field for $operand field name.
16002 // So we can complete format. For example we could complete a year with month and day.
16003 $tmpescaped = "'" . $db->escape($regbis[1]) . "'";
16004 } else {
16005 if (strtoupper($tmpescaped) == 'NULL') {
16006 $tmpescaped = 'NULL';
16007 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
16008 $tmpescaped = (int) $tmpescaped;
16009 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
16010 $tmpescaped = (float) $tmpescaped;
16011 } else {
16012 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
16013 }
16014 }
16015
16016 return '(' . $db->escape($operand) . ' ' . strtoupper($operator) . ' ' . $tmpescaped . ')';
16017}
16018
16019
16029function getTimelineIcon($actionstatic, &$histo, $key)
16030{
16031 global $langs;
16032
16033 $out = '<!-- timeline icon -->' . "\n";
16034 $iconClass = 'fa fa-comments';
16035 $img_picto = '';
16036 $colorClass = '';
16037 $pictoTitle = '';
16038
16039 if ($histo[$key]['percent'] == -1) {
16040 $colorClass = 'timeline-icon-not-applicble';
16041 $pictoTitle = $langs->trans('StatusNotApplicable');
16042 } elseif ($histo[$key]['percent'] == 0) {
16043 $colorClass = 'timeline-icon-todo';
16044 $pictoTitle = $langs->trans('StatusActionToDo') . ' (0%)';
16045 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
16046 $colorClass = 'timeline-icon-in-progress';
16047 $pictoTitle = $langs->trans('StatusActionInProcess') . ' (' . $histo[$key]['percent'] . '%)';
16048 } elseif ($histo[$key]['percent'] >= 100) {
16049 $colorClass = 'timeline-icon-done';
16050 $pictoTitle = $langs->trans('StatusActionDone') . ' (100%)';
16051 }
16052
16053 if ($actionstatic->code == 'AC_TICKET_CREATE') {
16054 $iconClass = 'fa fa-ticket';
16055 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
16056 $iconClass = 'fa fa-pencilxxx';
16057 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16058 $iconClass = 'fa fa-comments';
16059 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16060 $iconClass = 'fa fa-mask';
16061 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16062 if ($actionstatic->type_picto) {
16063 $img_picto = img_picto('', $actionstatic->type_picto);
16064 } else {
16065 if ($actionstatic->type_code == 'AC_RDV') {
16066 $iconClass = 'fa fa-handshake';
16067 } elseif ($actionstatic->type_code == 'AC_TEL') {
16068 $iconClass = 'fa fa-phone';
16069 } elseif ($actionstatic->type_code == 'AC_FAX') {
16070 $iconClass = 'fa fa-fax';
16071 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
16072 $iconClass = 'fa fa-envelope';
16073 } elseif ($actionstatic->type_code == 'AC_INT') {
16074 $iconClass = 'fa fa-shipping-fast';
16075 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
16076 $iconClass = 'fa fa-robot';
16077 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
16078 $iconClass = 'fa fa-robot';
16079 }
16080 }
16081 }
16082
16083 $out .= '<i class="' . $iconClass . ' ' . $colorClass . '" title="' . $pictoTitle . '">' . $img_picto . '</i>' . "\n";
16084 return $out;
16085}
16086
16094{
16095 global $db;
16096
16097 $documents = array();
16098
16099 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
16100 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'ecm_files ecm';
16101 $sql .= " WHERE ecm.filepath = 'agenda/" . ((int) $object->id) . "'";
16102 //$sql.= " ecm.src_object_type = '".$db->escape($object->element)."' AND ecm.src_object_id = ".((int) $object->id); // Old version didn't add object_type during upload
16103 $sql .= ' OR ecm.agenda_id = ' . (int) $object->id;
16104 $sql .= ' ORDER BY ecm.position ASC';
16105
16106 $resql = $db->query($sql);
16107 if ($resql) {
16108 if ($db->num_rows($resql)) {
16109 while ($obj = $db->fetch_object($resql)) {
16110 $documents[$obj->id] = $obj;
16111 }
16112 }
16113 }
16114
16115 return $documents;
16116}
16117
16118
16136function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
16137{
16138 global $user, $conf;
16139 global $form;
16140
16141 global $param, $massactionbutton;
16142
16143 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16144
16145 // Check parameters
16146 if (!is_object($filterobj) && !is_object($objcon)) {
16147 dol_print_error(null, 'BadParameter');
16148 }
16149
16150 $histo = array();
16151 '@phan-var-force array<int,array{type:string,tododone:string,id:string,datestart:int|string,dateend:int|string,note:string,message:string,percent:string,userid:string,login:string,userfirstname:string,userlastname:string,userphoto:string,msg_from?:string,contact_id?:string,socpeopleassigned?:int[],lastname?:string,firstname?:string,fk_element?:int,elementtype?:string,acode:string,alabel?:string,libelle?:string,apicto?:string}> $histo';
16152
16153 $numaction = 0;
16154 $now = dol_now();
16155
16156 $sortfield_list = explode(',', $sortfield);
16157 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
16158 $sortfield_new_list = array();
16159 foreach ($sortfield_list as $sortfield_value) {
16160 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
16161 }
16162 $sortfield_new = implode(',', $sortfield_new_list);
16163
16164 $sql = null;
16165 $sql2 = null;
16166
16167 if (isModEnabled('agenda')) {
16168 // Search histo on actioncomm
16169 if (is_object($objcon) && $objcon->id > 0) {
16170 $sql = "SELECT DISTINCT a.id, a.label as label,";
16171 } else {
16172 $sql = "SELECT a.id, a.label as label,";
16173 }
16174 $sql .= " a.datep as dp,";
16175 $sql .= " a.note as message,";
16176 $sql .= " a.datep2 as dp2,";
16177 $sql .= " a.percent as percent, 'action' as type,";
16178 $sql .= " a.fk_element, a.elementtype,";
16179 $sql .= " a.fk_contact,";
16180 $sql .= " a.email_from as msg_from,";
16181 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
16182 $sql .= " u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname";
16183 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16184 $sql .= ", sp.lastname, sp.firstname";
16185 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16186 $sql .= ", m.lastname, m.firstname";
16187 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur', 'Propal', 'Expedition'))) {
16188 $sql .= ", o.ref";
16189 } else {
16190 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16191 $sql .= ", o.ref";
16192 }
16193 }
16194 $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as a";
16195 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as u on u.rowid = a.fk_user_action";
16196 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_actioncomm as c ON a.fk_action = c.id";
16197
16198 $force_filter_contact = $filterobj instanceof User;
16199
16200 if (is_object($objcon) && $objcon->id > 0) {
16201 $force_filter_contact = true;
16202 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "actioncomm_resources as r ON a.id = r.fk_actioncomm";
16203 $sql .= " AND r.element_type = '" . $db->escape($objcon->table_element) . "' AND r.fk_element = " . ((int) $objcon->id);
16204 }
16205
16206 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
16207 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople as sp ON a.fk_contact = sp.rowid";
16208 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
16209 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "element_resources as er";
16210 $sql .= " ON er.resource_type = 'dolresource'";
16211 $sql .= " AND er.element_id = a.id";
16212 $sql .= " AND er.resource_id = " . ((int) $filterobj->id);
16213 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16214 $sql .= ", " . MAIN_DB_PREFIX . "adherent as m";
16215 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16216 $sql .= ", " . MAIN_DB_PREFIX . "commande_fournisseur as o";
16217 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16218 $sql .= ", " . MAIN_DB_PREFIX . "product as o";
16219 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16220 $sql .= ", " . MAIN_DB_PREFIX . "ticket as o";
16221 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16222 $sql .= ", " . MAIN_DB_PREFIX . "bom_bom as o";
16223 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16224 $sql .= ", " . MAIN_DB_PREFIX . "contrat as o";
16225 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16226 $sql .= ", " . MAIN_DB_PREFIX . "facture as o";
16227 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16228 $sql .= ", " . MAIN_DB_PREFIX . "facture_fourn as o";
16229 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16230 $sql .= ", " . MAIN_DB_PREFIX . "commande as o";
16231 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16232 $sql .= ", " . MAIN_DB_PREFIX . "expedition as o";
16233 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16234 $sql .= ", " . MAIN_DB_PREFIX . "propal as o";
16235 } else {
16236 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16237 $sql .= ", " . MAIN_DB_PREFIX . $filterobj->table_element . " as o";
16238 }
16239 }
16240 $sql .= " WHERE a.entity IN (" . getEntity('agenda') . ")";
16241 if (!$force_filter_contact) {
16242 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
16243 $sql .= " AND a.fk_soc = " . ((int) $filterobj->id);
16244 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
16245 $sql .= " AND a.fk_project = o.rowid AND a.fk_project = " . ((int) $filterobj->id);
16246 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16247 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
16248 if ($filterobj->id) {
16249 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16250 }
16251 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16252 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
16253 if ($filterobj->id) {
16254 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16255 }
16256 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16257 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'shipping'";
16258 if ($filterobj->id) {
16259 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16260 }
16261 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16262 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'propal'";
16263 if ($filterobj->id) {
16264 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16265 }
16266 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16267 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
16268 if ($filterobj->id) {
16269 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16270 }
16271 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16272 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
16273 if ($filterobj->id) {
16274 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16275 }
16276 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16277 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
16278 if ($filterobj->id) {
16279 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16280 }
16281 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16282 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
16283 if ($filterobj->id) {
16284 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16285 }
16286 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16287 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
16288 if ($filterobj->id) {
16289 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16290 }
16291 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
16292 $sql .= " AND a.fk_contact = sp.rowid";
16293 if ($filterobj->id) {
16294 $sql .= " AND a.fk_contact = " . ((int) $filterobj->id);
16295 }
16296 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16297 $sql .= " AND a.fk_element = o.rowid";
16298 if ($filterobj->id) {
16299 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice'";
16300 }
16301 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16302 $sql .= " AND a.fk_element = o.rowid";
16303 if ($filterobj->id) {
16304 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice_supplier'";
16305 }
16306 } else {
16307 if (is_object($filterobj) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16308 $sql .= " AND a.fk_element = o.rowid";
16309 $sql .= " AND a.elementtype = '" . $db->escape($filterobj->element) . "'";
16310 if ($filterobj->id) {
16311 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16312 }
16313 }
16314 }
16315 } else {
16316 $sql .= " AND u.rowid = " . ((int) $filterobj->id);
16317 }
16318
16319 // Condition on actioncode
16320 if (!empty($actioncode) && $actioncode != '-1') {
16321 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16322 if ($actioncode == 'AC_NON_AUTO') {
16323 $sql .= " AND c.type != 'systemauto'";
16324 } elseif ($actioncode == 'AC_ALL_AUTO') {
16325 $sql .= " AND c.type = 'systemauto'";
16326 } else {
16327 if ($actioncode == 'AC_OTH') {
16328 $sql .= " AND c.type != 'systemauto'";
16329 } elseif ($actioncode == 'AC_OTH_AUTO') {
16330 $sql .= " AND c.type = 'systemauto'";
16331 }
16332 }
16333 } else {
16334 if ($actioncode == 'AC_NON_AUTO') {
16335 $sql .= " AND c.type != 'systemauto'";
16336 } elseif ($actioncode == 'AC_ALL_AUTO') {
16337 $sql .= " AND c.type = 'systemauto'";
16338 } else {
16339 $sql .= " AND c.code = '" . $db->escape($actioncode) . "'";
16340 }
16341 }
16342 }
16343 if ($donetodo == 'todo') {
16344 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '" . $db->idate($now) . "'))";
16345 } elseif ($donetodo == 'done') {
16346 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '" . $db->idate($now) . "'))";
16347 }
16348 if (is_array($filters) && $filters['search_agenda_label']) {
16349 $sql .= natural_search('a.label', $filters['search_agenda_label']);
16350 }
16351 }
16352
16353 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
16354 if (
16355 isModEnabled('mailing') && !empty($objcon->email)
16356 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')
16357 ) {
16358 $langs->load("mails");
16359
16360 $sql2 = "SELECT m.rowid as id, m.titre as label, mc.date_envoi as dp, mc.date_envoi as dp2, '100' as percent, 'mailing' as type";
16361 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
16362 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
16363 $sql2 .= ", u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname"; // User that valid action
16364 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16365 $sql2 .= ", '' as lastname, '' as firstname";
16366 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16367 $sql2 .= ", '' as lastname, '' as firstname";
16368 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16369 $sql2 .= ", '' as ref";
16370 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16371 $sql2 .= ", '' as ref";
16372 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16373 $sql2 .= ", '' as ref";
16374 }
16375 $sql2 .= " FROM " . MAIN_DB_PREFIX . "mailing as m, " . MAIN_DB_PREFIX . "mailing_cibles as mc, " . MAIN_DB_PREFIX . "user as u";
16376 $sql2 .= " WHERE mc.email = '" . $db->escape($objcon->email) . "'"; // Search is done on email.
16377 $sql2 .= " AND mc.statut = 1";
16378 $sql2 .= " AND u.rowid = m.fk_user_valid";
16379 $sql2 .= " AND mc.fk_mailing=m.rowid";
16380 }
16381
16382 $num = 0;
16383 $MAXWITHOUTPAGINATION = getDolGlobalInt('AGENDA_MAX_EVENTS_ON_PAGE_WITHOUT_PAGINATION', 100);
16384
16385 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
16386 if (!empty($sql) && !empty($sql2)) {
16387 $sql = $sql . " UNION " . $sql2;
16388 } elseif (empty($sql) && !empty($sql2)) {
16389 $sql = $sql2;
16390 }
16391
16392 //TODO Add navigation with this limits...
16393 $offset = 0;
16394 $limit = $MAXWITHOUTPAGINATION;
16395
16396 // Complete request and execute it with limit
16397 $sql .= $db->order($sortfield_new, $sortorder);
16398 if ($limit) {
16399 $sql .= $db->plimit($limit + 1, $offset);
16400 }
16401
16402 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
16403
16404 $resql = $db->query($sql);
16405 if ($resql) {
16406 $i = 0;
16407 $num = $db->num_rows($resql);
16408
16409 $imaxinloop = ($limit ? min($num, $limit) : $num);
16410 while ($i < $imaxinloop) {
16411 $obj = $db->fetch_object($resql);
16412
16413 if ($obj->type == 'action') {
16414 $contactaction = new ActionComm($db);
16415 $contactaction->id = $obj->id;
16416 $result = $contactaction->fetchResources();
16417 if ($result < 0) {
16418 dol_print_error($db);
16419 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
16420 }
16421
16422 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
16423 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
16424 $tododone = '';
16425 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
16426 $tododone = 'todo';
16427 }
16428
16429 $histo[$numaction] = array(
16430 'type' => $obj->type,
16431 'tododone' => $tododone,
16432 'id' => $obj->id,
16433 'datestart' => $db->jdate($obj->dp),
16434 'dateend' => $db->jdate($obj->dp2),
16435 'note' => $obj->label,
16436 'message' => $obj->message,
16437 'percent' => $obj->percent,
16438
16439 'userid' => $obj->user_id,
16440 'login' => $obj->user_login,
16441 'userfirstname' => $obj->user_firstname,
16442 'userlastname' => $obj->user_lastname,
16443 'userphoto' => $obj->user_photo,
16444 'msg_from' => $obj->msg_from,
16445
16446 'contact_id' => $obj->fk_contact,
16447 'socpeopleassigned' => $contactaction->socpeopleassigned,
16448 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
16449 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
16450 'fk_element' => $obj->fk_element,
16451 'elementtype' => $obj->elementtype,
16452 // Type of event
16453 'acode' => $obj->acode,
16454 'alabel' => $obj->alabel,
16455 'libelle' => $obj->alabel, // deprecated
16456 'apicto' => $obj->apicto
16457 );
16458 } else {
16459 $histo[$numaction] = array(
16460 'type' => $obj->type,
16461 'tododone' => 'done',
16462 'id' => $obj->id,
16463 'datestart' => $db->jdate($obj->dp),
16464 'dateend' => $db->jdate($obj->dp2),
16465 'note' => $obj->label,
16466 'message' => $obj->message,
16467 'percent' => $obj->percent,
16468 'acode' => $obj->acode,
16469
16470 'userid' => $obj->user_id,
16471 'login' => $obj->user_login,
16472 'userfirstname' => $obj->user_firstname,
16473 'userlastname' => $obj->user_lastname,
16474 'userphoto' => $obj->user_photo
16475 );
16476 }
16477
16478 $numaction++;
16479 $i++;
16480 }
16481 } else {
16482 dol_print_error($db);
16483 }
16484 }
16485
16486 // Set $out to show events
16487 $out = '';
16488
16489 if (!isModEnabled('agenda')) {
16490 $langs->loadLangs(array("admin", "errors"));
16491 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
16492 }
16493
16494 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
16495 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
16496
16497 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16498 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
16499 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
16500 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
16501
16502 $formactions = new FormActions($db);
16503
16504 $actionstatic = new ActionComm($db);
16505 $userstatic = new User($db);
16506 $contactstatic = new Contact($db);
16507 $userGetNomUrlCache = array();
16508 $contactGetNomUrlCache = array();
16509
16510 $out .= '<div class="filters-container" >';
16511 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
16512 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
16513
16514 if (
16515 $objcon && get_class($objcon) == 'Contact' &&
16516 (is_null($filterobj) || get_class($filterobj) == 'Societe')
16517 ) {
16518 $out .= '<input type="hidden" name="id" value="' . $objcon->id . '" />';
16519 } else {
16520 $out .= '<input type="hidden" name="id" value="' . $filterobj->id . '" />';
16521 }
16522 if (($filterobj && get_class($filterobj) == 'Societe')) {
16523 $out .= '<input type="hidden" name="socid" value="' . $filterobj->id . '" />';
16524 } else {
16525 $out .= '<input type="hidden" name="userid" value="' . $filterobj->id . '" />';
16526 }
16527
16528 $out .= "\n";
16529
16530 $out .= '<div class="div-table-responsive-no-min">';
16531 $out .= '<table class="noborder borderbottom centpercent">';
16532
16533 $out .= '<tr class="liste_titre">';
16534
16535 // Action column
16536 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16537 $out .= '<th class="liste_titre width50 middle">';
16538 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16539 $out .= $searchpicto;
16540 $out .= '</th>';
16541 }
16542
16543 // Date
16544 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ') . "\n";
16545
16546 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">' . $langs->trans("Search") . ' : </strong></th>';
16547 if ($donetodo) {
16548 $out .= '<th class="liste_titre"></th>';
16549 }
16550 // Type of event
16551 $out .= '<th class="liste_titre">';
16552 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="' . $langs->trans("ActionType") . '"></span>';
16553 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
16554 $out .= '</th>';
16555 // Label
16556 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
16557 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="' . $filters['search_agenda_label'] . '" placeholder="' . $langs->trans("Label") . '">';
16558 $out .= '</th>';
16559
16560 // Action column
16561 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16562 $out .= '<th class="liste_titre width50 middle">';
16563 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16564 $out .= $searchpicto;
16565 $out .= '</th>';
16566 }
16567
16568 $out .= '</tr>';
16569
16570 $out .= '</table>';
16571
16572 $out .= '</form>';
16573 $out .= '</div>';
16574
16575 $out .= "\n";
16576
16577 $out .= '<ul class="timeline">';
16578
16579 if ($donetodo) {
16580 $tmp = '';
16581 if ($filterobj instanceof Societe) {
16582 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16583 }
16584 if ($filterobj instanceof User) {
16585 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16586 }
16587 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
16588 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
16589 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
16590 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
16591 if ($filterobj instanceof Societe) {
16592 $tmp .= '</a>';
16593 }
16594 if ($filterobj instanceof User) {
16595 $tmp .= '</a>';
16596 }
16597 $out .= getTitleFieldOfList($tmp);
16598 }
16599
16600 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/cactioncomm.class.php';
16601 $caction = new CActionComm($db);
16602 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
16603
16604 $actualCycleDate = false;
16605
16606 // Loop on each event to show it
16607 foreach ($histo as $key => $value) {
16608 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
16609
16610 $actionstatic->type_picto = $histo[$key]['apicto'];
16611 $actionstatic->type_code = $histo[$key]['acode'];
16612
16613 $labeltype = $actionstatic->type_code;
16614 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
16615 $labeltype = 'AC_OTH';
16616 }
16617 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16618 $labeltype = $langs->trans("Message");
16619 } else {
16620 if (!empty($arraylist[$labeltype])) {
16621 $labeltype = $arraylist[$labeltype];
16622 }
16623 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
16624 $labeltype .= ' - ' . $arraylist[$actionstatic->code]; // Use code in priority on type_code
16625 }
16626 }
16627
16628 $url = DOL_URL_ROOT . '/comm/action/card.php?id=' . $histo[$key]['id'];
16629
16630 $tmpa = dol_getdate($histo[$key]['datestart'], false);
16631
16632 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'] . '-' . $tmpa['yday']) {
16633 $actualCycleDate = $tmpa['year'] . '-' . $tmpa['yday'];
16634 $out .= '<!-- timeline time label -->';
16635 $out .= '<li class="time-label">';
16636 $out .= '<span class="timeline-badge-date">';
16637 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
16638 $out .= '</span>';
16639 $out .= '</li>';
16640 $out .= '<!-- /.timeline-label -->';
16641 }
16642
16643
16644 $out .= '<!-- timeline item -->' . "\n";
16645 $out .= '<li class="timeline-code-' . (!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none") . '">';
16646
16647 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
16648 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
16649 //$out .= $timelineicon;
16650 //var_dump($timelineicon);
16651 $out .= $typeicon;
16652
16653 $out .= '<div class="timeline-item">' . "\n";
16654
16655 $out .= '<span class="time timeline-header-action2">';
16656
16657 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
16658 $out .= '<a class="paddingleft paddingright timeline-btn2 editfielda" href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
16659 $out .= $histo[$key]['id'];
16660 $out .= '</a> ';
16661 } else {
16662 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle') . ' ';
16663 }
16664
16665 if (
16666 $user->hasRight('agenda', 'allactions', 'create') ||
16667 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))
16668 ) {
16669 $out .= '<a class="paddingleft paddingright timeline-btn2 editfielda" href="' . DOL_MAIN_URL_ROOT . '/comm/action/card.php?action=edit&token=' . newToken() . '&id=' . $actionstatic->id . '&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?' . $param) . '">';
16670 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
16671 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
16672 $out .= '</a>';
16673 }
16674
16675 $out .= '</span>';
16676
16677 // Date
16678 $out .= '<span class="time"><i class="fa fa-clock valignmiddle"></i> ';
16679 $out .= '<span class="valignmiddle marginrightonly">';
16680 $out .= dol_print_date($histo[$key]['datestart'], 'day', 'tzuserrel');
16681 //$out .= '</span>';
16682 //$out .= '<span class="valignmiddle">'.
16683 $out .= ' '.dol_print_date($histo[$key]['datestart'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16684 //$out .= '</span>';
16685 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
16686 $tmpa = dol_getdate($histo[$key]['datestart'], true);
16687 $tmpb = dol_getdate($histo[$key]['dateend'], true);
16688 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
16689 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 1);
16690 } else {
16691 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'day', 'tzuserrel');
16692 //$out .= '<span class="valignmiddle marginrightonly">';
16693 $out .= ' '.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16694 //$out .= '</span>';
16695 }
16696 }
16697 $late = 0;
16698 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16699 $late = 1;
16700 }
16701 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16702 $late = 1;
16703 }
16704 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
16705 $late = 1;
16706 }
16707 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16708 $late = 1;
16709 }
16710 if ($late) {
16711 $out .= img_warning($langs->trans("Late")) . ' ';
16712 }
16713 $out .= "</span></span>\n";
16714
16715 $out .= '<span class="time">';
16716 $out .= $actionstatic->getLibStatut(2);
16717 $out .= '</span>';
16718
16719 // Ref
16720 $out .= '<h3 class="timeline-header">';
16721
16722 // Author of event
16723 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
16724 if ($histo[$key]['userid'] > 0) {
16725 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
16726 $userstatic->fetch($histo[$key]['userid']);
16727 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
16728 }
16729 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
16730 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
16731 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
16732 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
16733 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
16734 } else {
16735 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
16736 }
16737 }
16738 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
16739 } else {
16740 $out .= '<img class="photomemberphoto userphoto" alt="" src="/public/theme/common/user_anonymous.png">'.$langs->trans("Anonymous");
16741 }
16742 $out .= '</div>';
16743
16744 // Title
16745 $out .= ' <div class="messaging-title inline-block">';
16746 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
16747 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
16748 $out .= $labeltype . ' - ';
16749 }
16750
16751 $libelle = '';
16752
16753 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16754 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
16755 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16756 $out .= $langs->trans('TicketNewMessage');
16757 } elseif (isset($histo[$key]['type'])) {
16758 if ($histo[$key]['type'] == 'action') {
16759 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16760 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
16761 $libelle = $histo[$key]['note'];
16762 $actionstatic->id = $histo[$key]['id'];
16763 if ($libelle != $labeltype) {
16764 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16765 }
16766 } elseif ($histo[$key]['type'] == 'mailing') {
16767 $out .= '<a href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
16768 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16769 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : 'Send mass mailing');
16770 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16771 } else {
16772 $libelle .= $histo[$key]['note'];
16773 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16774 }
16775 }
16776 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
16777
16778 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
16779 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
16780 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
16781 } else {
16782 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
16783 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
16784 }
16785 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
16786 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
16787 }
16788 if ($link) {
16789 $out .= ' - ' . $link;
16790 }
16791 }
16792
16793 $out .= '</div>';
16794
16795 $out .= '</h3>';
16796
16797 // Message
16798 if (
16799 !empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
16800 && $actionstatic->code != 'AC_TICKET_CREATE'
16801 && $actionstatic->code != 'AC_TICKET_MODIFY'
16802 ) {
16803 $out .= '<div class="timeline-body wordbreak small">';
16804 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
16805 $newmess = dol_htmlentitiesbr($histo[$key]['message']);
16806 $truncatedText = dolGetFirstLineOfText($newmess, $truncateLines);
16807 if ($truncateLines > 0 && strlen($newmess) > strlen($truncatedText)) {
16808 $out .= '<div class="readmore-block --closed" >';
16809 $out .= ' <div class="readmore-block__excerpt">';
16810 $out .= dolPrintHTML($truncatedText);
16811 $out .= ' <br><a class="read-more-link" data-read-more-action="open" href="' . DOL_MAIN_URL_ROOT . '/comm/action/card.php?id=' . $actionstatic->id . '&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?' . $param) . '" >' . $langs->trans("ReadMore") . ' <span class="fa fa-chevron-right" aria-hidden="true"></span></a>';
16812 $out .= ' </div>';
16813 $out .= ' <div class="readmore-block__full-text" >';
16814 $out .= dolPrintHTML($newmess);
16815 $out .= ' <a class="read-less-link" data-read-more-action="close" href="#" ><span class="fa fa-chevron-up" aria-hidden="true"></span> ' . $langs->trans("ReadLess") . '</a>';
16816 $out .= ' </div>';
16817 $out .= '</div>';
16818 } else {
16819 $out .= dolPrintHTML($newmess);
16820 }
16821
16822 $out .= '</div>';
16823 }
16824
16825 // Timeline footer
16826 $footer = '';
16827
16828 // Contact for this action
16829 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
16830 $contactList = '';
16831 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
16832 if (empty($conf->cache['contact'][$cid])) {
16833 $contact = new Contact($db);
16834 $contact->fetch($cid);
16835 $conf->cache['contact'][$cid] = $contact;
16836 } else {
16837 $contact = $conf->cache['contact'][$cid];
16838 }
16839
16840 if ($contact) {
16841 $contactList .= !empty($contactList) ? ', ' : '';
16842 $contactList .= $contact->getNomUrl(1);
16843 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16844 if (!empty($contact->phone_pro)) {
16845 $contactList .= '(' . dol_print_phone($contact->phone_pro) . ')';
16846 }
16847 }
16848 }
16849 }
16850
16851 $footer .= $langs->trans('ActionOnContact') . ' : ' . $contactList;
16852 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
16853 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
16854 $contact = new Contact($db);
16855 $result = $contact->fetch($histo[$key]['contact_id']);
16856 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
16857 } else {
16858 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
16859 $result = ($contact instanceof Contact) ? $contact->id : 0;
16860 }
16861
16862 if ($result > 0) {
16863 $footer .= $contact->getNomUrl(1);
16864 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16865 if (!empty($contact->phone_pro)) {
16866 $footer .= '(' . dol_print_phone($contact->phone_pro) . ')';
16867 }
16868 }
16869 }
16870 }
16871
16872 $documents = getActionCommEcmList($actionstatic);
16873 if (!empty($documents)) {
16874 $footer .= '<div class="timeline-documents-container">';
16875 foreach ($documents as $doc) {
16876 $footer .= '<span id="document_' . $doc->id . '" class="timeline-documents" ';
16877 $footer .= ' data-id="' . $doc->id . '" ';
16878 $footer .= ' data-path="' . $doc->filepath . '"';
16879 $footer .= ' data-filename="' . dol_escape_htmltag($doc->filename) . '" ';
16880 $footer .= '>';
16881
16882 $filePath = DOL_DATA_ROOT . '/' . $doc->filepath . '/' . $doc->filename;
16883 $mime = dol_mimetype($filePath);
16884 if (empty($doc->agenda_id)) {
16885 $dir_ref = $actionstatic->id;
16886 $modulepart = 'actions';
16887 } else {
16888 $split_dir = explode('/', $doc->filepath);
16889 $modulepart = array_shift($split_dir);
16890 $dir_ref = implode('/', $split_dir);
16891 }
16892
16893 $file = $dir_ref . '/' . $doc->filename;
16894 $thumb = $dir_ref . '/thumbs/' . substr($doc->filename, 0, strrpos($doc->filename, '.')) . '_mini' . substr($doc->filename, strrpos($doc->filename, '.'));
16895 $doclink = dol_buildpath('document.php', 1) . '?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($file) . '&entity=' . $conf->entity;
16896 $viewlink = dol_buildpath('viewimage.php', 1) . '?modulepart=' . $modulepart . '&file=' . urlencode($thumb) . '&entity=' . $conf->entity;
16897
16898
16899
16900 $mimeAttr = ' mime="' . $mime . '" ';
16901 $class = '';
16902 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
16903 $class .= ' documentpreview';
16904 }
16905
16906 $footer .= '<a href="' . $doclink . '" class="btn-link ' . $class . '" target="_blank" rel="noopener noreferrer" ' . $mimeAttr . ' >';
16907 $footer .= img_mime($filePath) . ' ' . $doc->filename;
16908 $footer .= '</a>';
16909
16910 $footer .= '</span>';
16911 }
16912 $footer .= '</div>';
16913 }
16914
16915 if (!empty($footer)) {
16916 $out .= '<div class="timeline-footer">' . $footer . '</div>';
16917 }
16918
16919 $out .= '</div>' . "\n"; // end timeline-item
16920
16921 $out .= '</li>';
16922 $out .= '<!-- END timeline item -->';
16923 }
16924
16925 $out .= "</ul>\n";
16926
16927 // Code to manage the click on button data-read-more-action to show full description of an event
16928 $out .= '<script>
16929 jQuery(document).ready(function () {
16930 $(document).on("click", "[data-read-more-action]", function(e){
16931 console.log("We click on data-read-more-action");
16932 let readMoreBloc = $(this).closest(".readmore-block");
16933 if(readMoreBloc.length > 0){
16934 e.preventDefault();
16935 if($(this).attr("data-read-more-action") == "close"){
16936 readMoreBloc.addClass("--closed").removeClass("--open");
16937 $("html, body").animate({
16938 scrollTop: readMoreBloc.offset().top - 200
16939 }, 100);
16940 }else{
16941 readMoreBloc.addClass("--open").removeClass("--closed");
16942 }
16943 }
16944 });
16945 });
16946 </script>';
16947
16948
16949 if (empty($histo)) {
16950 $out .= '<span class="opacitymedium">' . $langs->trans("NoRecordFound") . '</span>';
16951 }
16952
16953 if ($num > $MAXWITHOUTPAGINATION) {
16954 $langs->load("errors");
16955 $colspan = 9;
16956 $out .= '<center><span class="opacitymedium">...' . $langs->trans("WarningTooManyDataPleaseUseMoreFilters", $MAXWITHOUTPAGINATION) . '...</span></center>';
16957 }
16958 }
16959
16960 if ($noprint) {
16961 return $out;
16962 } else {
16963 print $out;
16964 return null;
16965 }
16966}
16967
16979function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
16980{
16981 if ($timestamp === null) {
16982 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
16983 }
16984 $TParam = array(
16985 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
16986 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
16987 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
16988 );
16989 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
16990 $TParam = array_merge($TParam, array(
16991 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
16992 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
16993 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
16994 ));
16995 }
16996
16997 return '&' . http_build_query($TParam);
16998}
16999
17018function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
17019{
17020 global $conf, $db, $langs, $hookmanager;
17021 global $action, $object;
17022
17023 if (!is_object($langs)) {
17024 include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
17025 $langs = new Translate('', $conf);
17026 $langs->setDefaultLang();
17027 }
17028
17029 $langs->load("errors");
17030
17031 if ($printheader) {
17032 if (function_exists("llxHeader")) {
17033 llxHeader('');
17034 } elseif (function_exists("llxHeaderVierge")) {
17035 llxHeaderVierge('');
17036 }
17037 }
17038
17039 print '<div class="error">';
17040 if (empty($message)) {
17041 print $langs->trans("ErrorRecordNotFound");
17042 } else {
17043 print $langs->trans($message);
17044 }
17045 print '</div>';
17046 print '<br>';
17047
17048 if (empty($showonlymessage)) {
17049 if (empty($hookmanager)) {
17050 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
17051 $hookmanager = new HookManager($db);
17052 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
17053 $hookmanager->initHooks(array('main'));
17054 }
17055
17056 $parameters = array('message' => $message, 'params' => $params);
17057 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
17058 print $hookmanager->resPrint;
17059 }
17060
17061 if ($printfooter && function_exists("llxFooter")) {
17062 llxFooter();
17063 if (is_object($db)) {
17064 $db->close();
17065 }
17066 }
17067 exit(0);
17068}
17069
17098function array_merge_recursive_distinct(array $array1, array $array2): array
17099{
17100 $merged = $array1;
17101
17102 foreach ($array2 as $key => $value) {
17103 if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
17104 $merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
17105 } else {
17106 $merged[$key] = $value;
17107 }
17108 }
17109
17110 return $merged;
17111}
17112
17119function getObjectSocId($obj)
17120{
17121 if (!empty($obj->socid)) {
17122 return (int) $obj->socid;
17123 } elseif (!empty($obj->soc_id)) {
17124 return (int) $obj->soc_id;
17125 } elseif (!empty($obj->societe_id)) {
17126 return (int) $obj->societe_id;
17127 }
17128 return null;
17129}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
global $dolibarr_main_url_root
if(!defined( 'NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined( 'NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) if(!defined( 'NOLOGIN')) if(!defined('NOCSRFCHECK')) if(!defined( 'NOIPCHECK')) llxHeaderVierge($title, $head="", $disablejs=0, $disablehead=0, $arrayofjs=[], $arrayofcss=[])
Header function.
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:475
ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array(), $morecss='', $htmlname='', $forcenojs=0, $moreparam='', $readonly=0)
On/off button to change a property status of an object This uses the ajax service objectonoff....
Definition ajax.lib.php:793
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
$c
Definition line.php:331
$object ref
Definition info.php:90
Class to manage agenda events (actions)
Class to manage different types of events.
static getValidAddress($address, $format, $encode=0, $maxnumberofemail=0)
Return a formatted address string for SMTP protocol.
Class to manage contact/addresses.
Class to manage GeoIP conversion Usage: $geoip=new GeoIP('country',$datfile); $geoip->getCountryCodeF...
Class to manage standard extra fields.
Class to manage invoices.
Class to manage building of HTML components.
Class to manage generation of HTML components Only common components must be here.
Class to manage hooks.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
isACompany()
Check if third party is a company (Business) or an end user (Consumer)
Class to manage translations.
Class to manage Dolibarr users.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
global $mysoc
dol_get_prev_month($month, $year)
Return previous month.
Definition date.lib.php:522
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:507
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:86
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition date.lib.php:491
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:541
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert an image file or a PDF into another image format.
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
dol_is_dir($folder)
Test if filename is a directory.
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
isValidMailDomain($mail)
Return true if email has a domain name that can be resolved to MX type.
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access)
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_now($mode='gmt')
Return date for now.
dol_fiche_end($notab=0)
Show tab footer of a card.
verifCond($strToEvaluate, $onlysimplestring='1')
Verify if condition in string is ok or not.
recordNotFound($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Displays an error page when a record is not found.
getDolGlobalFloat($key, $default=0)
Return a Dolibarr global constant float value.
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formatted size.
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
Function that return localtax of a product line (according to seller, buyer and product vat rate) If ...
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
show_actions_messaging($conf, $langs, $db, $filterobj, $objcon=null, $noprint=0, $actioncode='', $donetodo='done', $filters=array(), $sortfield='a.datep, a.id', $sortorder='DESC')
Show html area with actions in messaging format.
dol_getmypid()
Return getmypid() or random PID when function is disabled Some web hosts disable this php function fo...
getLanguageCodeFromCountryCode($countrycode)
Return default language from country code.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
setEntity($currentobject)
Set entity id to use when to create an object.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
img_credit_card($brand, $morecss='fa-2x inline-block valignmiddle')
Return image of a credit card according to its brand name.
GETPOSTDATE($prefix, $hourTime='', $gm='auto', $saverestore='')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_ip($ip, $mode=0, $showname=0)
Return an IP formatted to be shown on screen.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
img_right($titlealt='default', $selected=0, $moreatt='')
Show right arrow logo.
dol_print_phone($phone, $countrycode='', $contactid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto='UTF-8')
This function is called to decode a HTML string (it decodes entities and br tags)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs=null, $mode=0, $extralangcode='')
Return a formatted address (part address/zip/town/state) according to country rules.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
getObjectSocId($obj)
Get the socid of an object, supporting legacy attribute names.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dolPrintHTML($s, $allowiframe=0)
Return a string (that can be on several lines) ready to be output on a HTML page.
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
dolPrintHTMLForTextArea($s, $allowiframe=0)
Return a string ready to be output on input textarea.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_eval_new($s)
Replace eval function to add more security.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Get formatted error messages to output (Used to show messages on html output).
dol_user_country()
Return country code for current user.
dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes=null)
Clean a string from some undesirable HTML tags.
getMultidirTemp($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its temporary ...
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
dol_get_fiche_end($notab=0)
Return tab footer of a card.
getDolEntity()
Return the current entity.
picto_required()
Return picto saying a field is required.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_action($titlealt, $numaction, $picto='', $moreatt='')
Show logo action.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
dol_sanitizePathName($str, $newstr='_', $unaccent=0, $allowdash=0)
Clean a string to use it as a path name.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dolPrintText($s)
Return a string label (possible on several lines and that should not contains any HTML) ready to be o...
utf8_valid($str)
Check if a string is in UTF8.
getPictoForType($key, $morecss='')
Return the picto for a data type.
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
getDolOptimizeSmallScreen()
Return if render must be optimized for small screen.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
Create a dialog with two buttons for export and overwrite of a website.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
jsonOrUnserialize($stringtodecode, $assoc=true)
Decode an encoded string.
dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
get_localtax_by_third($local)
Get values of localtaxes (1 or 2) for company country for the common vat with the highest value.
dol_escape_php($stringtoescape, $stringforquotes=2)
Returns text escaped for inclusion into a php string, build with double quotes " or '.
dolSetCookie(string $cookiename, string $cookievalue, int $expire=-1)
Set a cookie.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
dol_get_object_properties($obj, $properties=[])
Get properties for an object - including magic properties when requested.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_set_focus($selector)
Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
img_search($titlealt='default', $other='')
Show search logo.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
isValidPhone($phone)
Return true if phone number syntax is ok TODO Decide what to do with this.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
img_previous($titlealt='default', $moreatt='')
Show previous logo.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
getBrowserInfo($user_agent)
Return information about user browser.
dolGetFirstLetters($s, $nbofchar=1)
Return first letters of a strings.
dolPrintLabel($s, $escapeonlyhtmltags=0)
Return a string label (so on 1 line only and that should not contains any HTML) ready to be output on...
dol_clone_in_array($srcobject, $startlevel=0)
Create a clone of instance of object into a full array, using recursive call.
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
getMultidirVersion($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its versioned ...
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
dol_sanitizeUrl($stringtoclean, $type=1)
Clean a string to use it as an URL (into a href or src attribute)
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
img_printer($titlealt="default", $other='')
Show printer logo.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
ascii_check($str)
Check if a string is in ASCII.
get_date_range($date_start, $date_end, $format='', $outputlangs=null, $withparenthesis=1)
Format output for start and end date.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
getImgPictoConv($mode='fa')
Get array to convert the Dolibarr picto keys into Font awesome keys.
print_date_range($date_start, $date_end, $format='', $outputlangs=null)
Format output for start and end date.
getArrayOfSocialNetworks()
Get array of social network dictionary.
getDolDefaultContextPage($s)
Return the default context page string.
num2Alpha($n)
Return a numeric value into an Excel like column number.
dol_size($size, $type='')
Optimize a size for some browsers (phone, smarphone...)
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolPrintPassword($s)
Return a string ready to be output on an HTML attribute (alt, title, ...)
dol_escape_all($stringtoescape)
Returns text escaped for all protocols (so only alpha chars and numbers)
dolForgeSQLCriteriaCallback($matches)
Function to forge a SQL criteria from a USF (Universal Filter Syntax) string.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
dol_shutdown()
Function called at end of web php process.
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl='')
Format address string.
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
dol_escape_uri($stringtoescape)
Returns text escaped by RFC 3986 for inclusion into a clickable link.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
getDolDBType()
Return the current entity.
print_titre($title)
Show a title.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
img_error($titlealt='default')
Show error logo.
getTimelineIcon($actionstatic, &$histo, $key)
Get timeline icon.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formatted messages to output (Used to show messages on html output).
get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
Return localtax vat rate of a product in a particular country or default country vat if product is un...
getUserRemoteIP($trusted=0)
Return the real IP of remote user.
buildParamDate($prefix, $timestamp=null, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_next($titlealt='default', $moreatt='')
Show next logo.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_string_is_good_iso($s, $clean=0)
Check if a string is a correct iso string If not, it will not be considered as HTML encoded even if i...
getNonce()
Return a random string to be used as a nonce value for js.
isStringVarMatching($var, $regextext, $matchrule=1)
Check if a variable with name $var start with $regextext.
dolSlugify($stringtoslugify)
Returns text slugified (lowercase and no special char, separator is "-").
dol_concat($text1, $text2)
Concat 2 strings.
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode='add', $filterorigmodule='')
Complete or removed entries into a head array (used to build tabs).
get_htmloutput_mesg($mesgstring='', $mesgarray=[], $style='ok', $keepembedded=0)
Get formatted messages to output (Used to show messages on html output).
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $selectlimitsuffix='', $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
dol_nboflines($s, $maxchar=0)
Return nb of lines of a clear text.
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js, dangerous content and external links.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
dol_escape_xml($stringtoescape)
Returns text escaped for inclusion into a XML string.
getActionCommEcmList($object)
getActionCommEcmList
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
img_edit_add($titlealt='default', $other='')
Show logo "+".
print_fiche_titre($title, $mesg='', $picto='generic', $pictoisfullpath=0, $id='')
Show a title with picto.
dolForgeDummyCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax string.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0, $allowphp=0)
Clean a string to keep only desirable HTML tags.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_validElement($element)
Return if var element is ok.
dol_sanitizeKeyCode($str)
Clean a string to use it as a key or code.
isModEnabled($module)
Is Dolibarr module enabled.
array_merge_recursive_distinct(array $array1, array $array2)
Recursively merges two arrays while preserving keys and replacing existing values.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
img_searchclear($titlealt='default', $other='')
Show search logo.
getWarningDelay($module, $parmlevel1, $parmlevel2='')
Return a warning delay You can use it like this: if (getWarningDelay('module', 'paramlevel1')) It rep...
utf8_check($str)
Check if a string is in UTF8.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Print formatted error messages to output (Used to show messages on html output).
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
dol_eval_standard($s, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
get_product_vat_for_country($idprod, $thirdpartytouseforcountry, $idprodfournprice=0)
Return vat rate of a product in a particular country, or default country vat if product is unknown.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dolPrintHTMLForAttributeUrl($s)
Return a string ready to be output on a href attribute (this one need a special because we need conte...
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
img_edit_remove($titlealt='default', $other='')
Show logo "-".
img_info($titlealt='default')
Show info logo.
getDoliDBInstance($type, $host, $user, $pass, $name, $port)
Return a DoliDB instance (database handler).
dol_sanitizeEmail($stringtoclean)
Clean a string to use it as an Email.
dol_nboflines_bis($text, $maxlinesize=0, $charset='UTF-8')
Return nb of lines of a formatted text with and (WARNING: string must not have mixed and br sep...
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
const MODULE_MAPPING
This mapping defines the conversion to the current internal names from the alternative allowed names ...
dolBECalculateStructuredCommunication($invoice_number, $invoice_type)
Calculate Structured Communication / BE Bank payment reference number.
dol_convertToWord($num, $langs, $currency='', $centimes=false)
Function to return a number into a text.
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
treeview li table
No Email.
div refaddress div address
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
dol_setcache($memoryid, $data, $expire=0, $filecache=0, $replace=0)
Save data into a memory area shared by all users, all sessions on server.
dol_getcache($memoryid, $filecache=0)
Read a memory area shared by all users, all sessions on server.
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:125
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:128
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition waf.inc.php:66