dolibarr 23.0.4
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-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 if ($paramname == 'hashp' && $out == 'shared') {
1355 $out = ''; // We refuse to have hashp=shared as a parameter
1356 }
1357
1358 return $out;
1359}
1360
1370function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1371{
1372 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1373 // Check is done after replacement
1374 if ($out === null) {
1375 $out = '';
1376 }
1377 switch ($check) {
1378 case 'none':
1379 case 'password':
1380 break;
1381 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1382 if (!is_numeric($out)) {
1383 $out = '';
1384 }
1385 break;
1386 case 'intcomma':
1387 if (is_array($out)) {
1388 $out = implode(',', $out);
1389 }
1390 if (preg_match('/[^0-9,-]+/i', $out)) {
1391 $out = '';
1392 }
1393 break;
1394 case 'san_alpha':
1395 dol_syslog("Use of parameter value 'san_alpha' in GETPOST is deprecated. Use 'alphanohtml', 'aZ09comma', ...", LOG_WARNING);
1396 $out = filter_var($out, FILTER_SANITIZE_STRING);
1397 break;
1398 case 'email':
1399 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1400 break;
1401 case 'url':
1402 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1403 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1404 // TODO Allow ( ) but only into password of https://login:password@domain...
1405 break;
1406 case 'aZ':
1407 if (!is_array($out)) {
1408 $out = trim($out);
1409 if (preg_match('/[^a-z]+/i', $out)) {
1410 $out = '';
1411 }
1412 }
1413 break;
1414 case 'aZ09':
1415 if (!is_array($out)) {
1416 $out = trim($out);
1417 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1418 $out = '';
1419 }
1420 }
1421 break;
1422 case 'aZ09arobase': // great to sanitize $objecttype parameter
1423 if (!is_array($out)) {
1424 $out = trim($out);
1425 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1426 $out = '';
1427 }
1428 }
1429 break;
1430 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1431 if (!is_array($out)) {
1432 $out = trim($out);
1433 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1434 $out = '';
1435 }
1436 }
1437 break;
1438 case 'alpha': // No html and no ../ and "
1439 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1440 if (!is_array($out)) {
1441 $out = trim($out);
1442 do {
1443 $oldstringtoclean = $out;
1444 // Remove html tags
1445 $out = dol_string_nohtmltag($out, 0);
1446 // 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).
1447 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1448 // Remove also other dangerous string sequences
1449 // '../' or '..\' is dangerous because it allows dir transversals
1450 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1451 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1452 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1453 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1454 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1455 } while ($oldstringtoclean != $out);
1456 // keep lines feed
1457 }
1458 break;
1459 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'
1460 if (!is_array($out)) {
1461 $out = trim($out);
1462 do {
1463 $oldstringtoclean = $out;
1464 // Decode html entities
1465 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1466 // 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).
1467 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1468 // Remove also other dangerous string sequences
1469 // '../' or '..\' is dangerous because it allows dir transversals
1470 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1471 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1472 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1473 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1474 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1475 } while ($oldstringtoclean != $out);
1476 }
1477 break;
1478 case 'nohtml': // No html. Valid for JSON strings.
1479 $out = dol_string_nohtmltag($out, 0);
1480 break;
1481 case 'restricthtmlnolink':
1482 case 'restricthtml': // Recommended for most html textarea
1483 case 'restricthtmlallowclass':
1484 case 'restricthtmlallowiframe':
1485 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1486 case 'restricthtmlallowunvalid':
1487 $out = dol_htmlwithnojs($out, 1, $check);
1488 break;
1489 case 'custom':
1490 if (!empty($out)) {
1491 if (empty($filter)) {
1492 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1493 }
1494 if (is_null($options)) {
1495 $options = 0;
1496 }
1497 $out = filter_var($out, $filter, $options);
1498 }
1499 break;
1500 default:
1501 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1502 $out = GETPOST($out, 'alphanohtml');
1503 break;
1504 }
1505
1506 return $out;
1507}
1508
1517function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1518{
1519 global $dolibarr_main_force_https;
1520
1521 if ($expire == -1) {
1522 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1523 }
1524
1525 if (PHP_VERSION_ID < 70300) {
1526 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1527 } else {
1528 // Only available for php >= 7.3
1529 $cookieparams = array(
1530 'expires' => empty($cookievalue) ? 0 : $expire,
1531 'path' => '/',
1532 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1533 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1534 'httponly' => true,
1535 'samesite' => 'Lax' // None || Lax || Strict
1536 );
1537 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1538 }
1539 if (empty($cookievalue)) {
1540 unset($_COOKIE[$cookiename]);
1541 }
1542}
1543
1544if (!function_exists('dol_getprefix')) {
1555 function dol_getprefix($mode = '')
1556 {
1557 // If prefix is for email (we need to have $conf already loaded for this case)
1558 if ($mode == 'email') {
1559 global $conf;
1560
1561 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1562 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1563 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1564 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1565 return $_SERVER["SERVER_NAME"];
1566 }
1567 }
1568
1569 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1570 if (!empty($conf->file->instance_unique_id)) {
1571 return sha1('dolibarr' . $conf->file->instance_unique_id);
1572 }
1573
1574 // For backward compatibility when instance_unique_id is not set
1575 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1576 }
1577
1578 // If prefix is for session (no need to have $conf loaded)
1579 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1580 $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
1581
1582 // The recommended value (may be not defined for old versions)
1583 if (!empty($tmp_instance_unique_id)) {
1584 return sha1('dolibarr' . $tmp_instance_unique_id);
1585 }
1586
1587 // For backward compatibility when instance_unique_id is not set
1588 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1589 return sha1($_SERVER["SERVER_NAME"] . $_SERVER["DOCUMENT_ROOT"] . DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1590 } else {
1591 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1592 }
1593 }
1594}
1595
1606function dol_include_once($relpath, $classname = '')
1607{
1608 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']
1609
1610 if (strpos($relpath, '..') !== false) {
1611 // Found a not valid path
1612 dol_syslog('functions::dol_include_once Tried to load a file with a path including a forbidden sequence ".." : ' . $relpath, LOG_WARNING);
1613 return false;
1614 }
1615 if (!preg_match('/\.php$/', $relpath)) {
1616 // Found a not valid path
1617 dol_syslog('functions::dol_include_once Tried to load a file that is not a PHP file : ' . $relpath, LOG_WARNING);
1618 return false;
1619 }
1620
1621 $fullpath = dol_buildpath($relpath);
1622
1623 if (!file_exists($fullpath)) {
1624 dol_syslog('functions::dol_include_once Tried to load unexisting file: ' . $relpath, LOG_WARNING);
1625 return false;
1626 }
1627 if (!empty($classname) && !class_exists($classname)) {
1628 return include $fullpath;
1629 } else {
1630 return include_once $fullpath;
1631 }
1632}
1633
1634
1648function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1649{
1650 global $conf;
1651
1652 $path = preg_replace('/^\//', '', $path);
1653
1654 if (empty($type)) { // For a filesystem path
1655 $res = DOL_DOCUMENT_ROOT . '/' . $path; // Standard default path
1656 if (is_array($conf->file->dol_document_root)) {
1657 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1658 if ($key == 'main') {
1659 continue;
1660 }
1661 // if (@file_exists($dirroot.'/'.$path)) {
1662 if (@file_exists($dirroot . '/' . $path)) { // avoid [php:warn]
1663 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/...'
1664 continue;
1665 }
1666 $res = $dirroot . '/' . $path;
1667 return $res;
1668 }
1669 }
1670 }
1671 if ($returnemptyifnotfound) {
1672 // Not found into alternate dir
1673 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1674 return '';
1675 }
1676 }
1677 } else {
1678 // For an url path
1679 // We try to get local path of file on filesystem from url
1680 // Note that trying to know if a file on disk exist by forging path on disk from url
1681 // works only for some web server and some setup. This is bugged when
1682 // using proxy, rewriting, virtual path, etc...
1683 $res = '';
1684 if ($type == 1) {
1685 $res = DOL_URL_ROOT . '/' . $path; // Standard value
1686 }
1687 if ($type == 2) {
1688 $res = DOL_MAIN_URL_ROOT . '/' . $path; // Standard value
1689 }
1690 if ($type == 3) {
1691 $res = DOL_URL_ROOT . '/' . $path;
1692 }
1693
1694 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1695 if ($key == 'main') {
1696 if ($type == 3) {
1697 /*global $dolibarr_main_url_root;*/
1698
1699 // Define $urlwithroot
1700 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1701 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1702 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1703
1704 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . '/' . $path; // Test on start with http is for old conf syntax
1705 }
1706 continue;
1707 }
1708 $regs = array();
1709 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1710 if (!empty($regs[1])) {
1711 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1712 //if (file_exists($dirroot.'/'.$regs[1])) {
1713 if (@file_exists($dirroot . '/' . $regs[1])) { // avoid [php:warn]
1714 if ($type == 1) {
1715 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1716 } elseif ($type == 2) {
1717 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1718 } elseif ($type == 3) {
1719 /*global $dolibarr_main_url_root;*/
1720
1721 // Define $urlwithroot
1722 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1723 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1724 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1725
1726 $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
1727 }
1728 break;
1729 }
1730 }
1731 }
1732 }
1733
1734 return $res;
1735}
1736
1745function dolBuildUrl($url, $params = [], $addtoken = false)
1746{
1747 global $db, $hookmanager;
1748
1749 if (!is_object($hookmanager)) {
1750 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
1751 $hookmanager = new HookManager($db);
1752 }
1753 if ((!isset($params['mainmenu']) || empty($params['mainmenu'])) && GETPOSTISSET('mainmenu')) {
1754 $params = array_merge($params, ['mainmenu' => (GETPOST('mainmenu', 'restricthtml'))]);
1755 }
1756 if ((!isset($params['leftmenu'])/* || empty($params['leftmenu']) */) && GETPOSTISSET('leftmenu')) { // do not fill leftmenu if we have leftmenu=
1757 $params = array_merge($params, ['leftmenu' => (GETPOST('leftmenu', 'restricthtml'))]);
1758 }
1759 $parameters = [
1760 'path' => &$url,
1761 'params' => &$params,
1762 'addtoken' => &$addtoken,
1763 ];
1764 $hookmanager->executeHooks('buildurl', $parameters);
1765 if ($addtoken) {
1766 $params = array_merge($params, ['token' => newToken()]);
1767 }
1768 // TODO TO REMOVE
1769 if (getDolGlobalString('MAIN_DEBUG_DOL_BUILDURL')) {
1770 $params = array_merge($params, ['debug' => 'debug']);
1771 }
1772 if ($params) {
1773 $url .= '?' . http_build_query($params);
1774 }
1775
1776 return $url;
1777}
1778
1789function dol_get_object_properties($obj, $properties = [])
1790{
1791 // Get real properties using get_object_vars() if $properties is empty
1792 if (empty($properties)) {
1793 return get_object_vars($obj);
1794 }
1795
1796 $existingProperties = [];
1797 $realProperties = get_object_vars($obj);
1798
1799 // Get the real or magic property values
1800 foreach ($properties as $property) {
1801 if (array_key_exists($property, $realProperties)) {
1802 // Real property, add the value
1803 $existingProperties[$property] = $obj->{$property};
1804 } elseif (property_exists($obj, $property)) {
1805 // Magic property
1806 $existingProperties[$property] = $obj->{$property};
1807 }
1808 }
1809
1810 return $existingProperties;
1811}
1812
1813
1829function dol_clone($srcobject, $native = 2)
1830{
1831 if ($native == 0) {
1832 // deprecated method, use the method with native = 2 instead
1833 dol_syslog("Warning, call to dol_clone() with the deprecated parameter native=0, use 2 instead", LOG_WARNING);
1834
1835 $tmpsavdb = null;
1836 if (isset($srcobject->db) && isset($srcobject->db->db) && is_object($srcobject->db->db) && get_class($srcobject->db->db) == 'PgSql\Connection') {
1837 $tmpsavdb = $srcobject->db;
1838 unset($srcobject->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1839 }
1840
1841 $myclone = unserialize(serialize($srcobject)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1842
1843 if (!empty($tmpsavdb)) {
1844 $srcobject->db = $tmpsavdb;
1845 }
1846 } elseif ($native == 2) {
1847 // recommended method to have a full secured isolated cloned object
1848 $myclone = new stdClass();
1849 $tmparray = get_object_vars($srcobject); // return only public properties
1850
1851 if (is_array($tmparray)) {
1852 foreach ($tmparray as $propertykey => $propertyval) {
1853 if (is_scalar($propertyval) || is_array($propertyval)) {
1854 $myclone->$propertykey = $propertyval;
1855 }
1856 }
1857 }
1858 } else {
1859 $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)
1860 }
1861
1862 return $myclone;
1863}
1864
1865
1874function dol_clone_in_array($srcobject, $startlevel = 0)
1875{
1876 if (is_object($srcobject)) {
1877 $srcobject = get_object_vars($srcobject); // exclude private/protected properties
1878 }
1879
1880 if (is_array($srcobject)) {
1881 $result = [];
1882 foreach ($srcobject as $key => $value) {
1883 if (in_array($key, array('db', 'fields', 'error', 'errorhidden', 'errors', 'oldcopy', 'linkedObjects', 'linked_objects'))) {
1884 continue;
1885 }
1886 $result[$key] = dol_clone_in_array($value, $startlevel + 1);
1887 }
1888 return $result;
1889 }
1890
1891 return $srcobject;
1892}
1893
1894
1904function dol_size($size, $type = '')
1905{
1906 global $conf;
1907 if (empty($conf->dol_optimize_smallscreen)) {
1908 return $size;
1909 }
1910 if ($type == 'width' && $size > 250) {
1911 return 250;
1912 } else {
1913 return 10;
1914 }
1915}
1916
1917
1931function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0, $allowdash = 0)
1932{
1933 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1934 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1935 // Char '/' and '\' are file delimiters.
1936 // 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
1937 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1938 if ($includequotes) {
1939 $filesystem_forbidden_chars[] = "'";
1940 }
1941 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1942 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1943 if (empty($allowdash)) {
1944 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1945 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1946 }
1947 $tmp = str_replace('..', '', $tmp);
1948 $tmp = str_replace('~', $newstr, $tmp);
1949 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1950
1951 return $tmp;
1952}
1953
1954
1967function dol_sanitizePathName($str, $newstr = '_', $unaccent = 0, $allowdash = 0)
1968{
1969 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1970 // Char '>' '<' '|' '$' ';' and '`' are special chars for shells.
1971 // Char '?' and '*' are for wild card chars.
1972 // Char '"' is dangerous.
1973 // Char '°' is just not expected.
1974 // 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
1975 // Chars '--' and '~' can be used for path transversal
1976 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1977
1978 $tmp = $str;
1979 if ($unaccent) {
1980 $tmp = dol_string_unaccent($tmp);
1981 }
1982 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1983 $tmp = preg_replace('/\-\-+/', $newstr, $tmp);
1984 if (empty($allowdash)) {
1985 $tmp = preg_replace('/\s+\-([^\s])/', ' '.$newstr.'$1', $tmp);
1986 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1987 }
1988 $tmp = str_replace('..', $newstr, $tmp);
1989 $tmp = str_replace('~', $newstr, $tmp);
1990 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1991
1992 return $tmp;
1993}
1994
2002function dol_sanitizeUrl($stringtoclean, $type = 1)
2003{
2004 // 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)
2005 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
2006 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2007 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
2008 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
2009
2010 $stringtoclean = str_replace('\\', '/', $stringtoclean);
2011 if ($type == 1) {
2012 // removing : should disable links to external url like http:aaa)
2013 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
2014 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
2015 }
2016
2017 do {
2018 $oldstringtoclean = $stringtoclean;
2019 // removing '&colon' should disable links to external url like http:aaa)
2020 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
2021 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
2022 } while ($oldstringtoclean != $stringtoclean);
2023
2024 if ($type == 1) {
2025 // removing '//' should disable links to external url like //aaa or http//)
2026 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
2027 }
2028
2029 return $stringtoclean;
2030}
2031
2038function dol_sanitizeEmail($stringtoclean)
2039{
2040 do {
2041 $oldstringtoclean = $stringtoclean;
2042 $stringtoclean = str_ireplace(array('"', ':', '[', ']', "\n", "\r", '\\', '\/'), '', $stringtoclean);
2043 } while ($oldstringtoclean != $stringtoclean);
2044
2045 return $stringtoclean;
2046}
2047
2056function dol_sanitizeKeyCode($str)
2057{
2058 return preg_replace('/[^\w]+/', '', $str);
2059}
2060
2061
2070function dol_string_unaccent($str)
2071{
2072 if (is_null($str)) {
2073 return '';
2074 }
2075
2076 if (utf8_check($str)) {
2077 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
2078 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
2079 return $transliterator->transliterate($str);
2080 }
2081 // See http://www.utf8-chartable.de/
2082 $string = rawurlencode($str);
2083 $replacements = array(
2084 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
2085 '%C3%87' => 'C',
2086 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
2087 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
2088 '%C3%91' => 'N',
2089 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', '%C5%90' => 'O',
2090 '%C5%A0' => 'S',
2091 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', '%C5%B0' => 'U',
2092 '%C3%9D' => 'Y', '%C5%B8' => 'y',
2093 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
2094 '%C3%A7' => 'c',
2095 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
2096 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
2097 '%C3%B1' => 'n',
2098 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', '%C5%91' => 'o',
2099 '%C5%A1' => 's',
2100 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', '%C5%B1' => 'u',
2101 '%C3%BD' => 'y', '%C3%BF' => 'y',
2102 '%CC%80' => '',
2103 '%CC%81' => '',
2104 '%CC%82' => '',
2105 '%CC%83' => '',
2106 '%CC%84' => '',
2107 '%CC%85' => '',
2108 '%CC%86' => '',
2109 '%CC%87' => '',
2110 '%CC%88' => '',
2111 '%CC%89' => '',
2112 '%CC%8A' => '',
2113 '%CC%8B' => '',
2114 '%CC%8C' => '',
2115 '%CC%8D' => '',
2116 '%CC%8E' => '',
2117 '%CC%8F' => '',
2118 '%CC%90' => '',
2119 '%CC%91' => '',
2120 '%CC%A7' => ''
2121 );
2122 $string = strtr($string, $replacements);
2123 return rawurldecode($string);
2124 } else {
2125 // See http://www.ascii-code.com/
2126 $string = strtr(
2127 $str,
2128 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
2129 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
2130 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
2131 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
2132 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
2133 \xF9\xFA\xFB\xFC\xFD\xFF",
2134 "AAAAAAC
2135 EEEEIIIIDN
2136 OOOOOUUUY
2137 aaaaaaceeee
2138 iiiidnooooo
2139 uuuuyy"
2140 );
2141 $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"));
2142 return $string;
2143 }
2144}
2145
2159function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
2160{
2161 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
2162 if (empty($keepspaces)) {
2163 $forbidden_chars_to_replace[] = " ";
2164 }
2165 $forbidden_chars_to_remove = array();
2166 //$forbidden_chars_to_remove=array("(",")");
2167
2168 if (is_array($badcharstoreplace)) {
2169 $forbidden_chars_to_replace = $badcharstoreplace;
2170 }
2171 if (is_array($badcharstoremove)) {
2172 $forbidden_chars_to_remove = $badcharstoremove;
2173 }
2174
2175 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2176 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
2177}
2178
2179
2193function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2194{
2195 if ($removetabcrlf) {
2196 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2197 } else {
2198 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
2199 }
2200}
2201
2208function dolSlugify($stringtoslugify)
2209{
2210 $slug = dol_string_unaccent($stringtoslugify);
2211
2212 // Convert special characters to their ASCII equivalents
2213 if (function_exists('iconv')) {
2214 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2215 }
2216
2217 // Convert to lowercase
2218 $slug = strtolower($slug);
2219
2220 // Replace non-alphanumeric characters with hyphens
2221 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2222
2223 // Remove leading and trailing hyphens
2224 $slug = trim($slug, '-');
2225
2226 return $slug;
2227}
2228
2237function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2238{
2239 if (is_null($stringtoescape)) {
2240 return '';
2241 }
2242
2243 // escape quotes and backslashes, newlines, etc.
2244 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2245 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2246 if (empty($noescapebackslashn)) {
2247 $substitjs["\n"] = '\\n';
2248 $substitjs['\\'] = '\\\\';
2249 }
2250 if (empty($mode)) {
2251 $substitjs["'"] = "\\'";
2252 $substitjs['"'] = "\\'";
2253 } elseif ($mode == 1) {
2254 $substitjs["'"] = "\\'";
2255 } elseif ($mode == 2) {
2256 $substitjs['"'] = '\\"';
2257 } elseif ($mode == 3) {
2258 $substitjs["'"] = "\\'";
2259 $substitjs['"'] = "\\\"";
2260 }
2261 return strtr((string) $stringtoescape, $substitjs);
2262}
2263
2273function dol_escape_uri($stringtoescape)
2274{
2275 return rawurlencode($stringtoescape);
2276}
2277
2284function dol_escape_json($stringtoescape)
2285{
2286 return str_replace('"', '\"', $stringtoescape);
2287}
2288
2296function dol_escape_php($stringtoescape, $stringforquotes = 2)
2297{
2298 if (is_null($stringtoescape)) {
2299 return '';
2300 }
2301
2302 if ($stringforquotes == 2) {
2303 return str_replace('"', "'", $stringtoescape);
2304 } elseif ($stringforquotes == 1) {
2305 // We remove the \ char.
2306 // If we allow the \ char, we can have $stringtoescape =
2307 // abc\';phpcodedanger; so the escapement will become
2308 // abc\\';phpcodedanger; and injecting this into
2309 // $a='...' will give $ac='abc\\';phpcodedanger;
2310 $stringtoescape = str_replace('\\', '', $stringtoescape);
2311 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2312 }
2313
2314 return 'Bad parameter for stringforquotes in dol_escape_php';
2315}
2316
2323function dol_escape_all($stringtoescape)
2324{
2325 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2326}
2327
2334function dol_escape_xml($stringtoescape)
2335{
2336 return $stringtoescape;
2337}
2338
2348function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2349{
2350 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2351}
2352
2361function dolPrintText($s)
2362{
2363 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2364}
2365
2376function dolPrintHTML($s, $allowiframe = 0)
2377{
2378 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2379 //$isAlreadyHTML = dol_textishtml($s);
2380
2381 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2382 // encode only special char like é but not &, <, >, ", ' if already HTML.
2383 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2384
2385 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2386}
2387
2398function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2399{
2400 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2401 if (!empty($allowothertags) && is_array($allowothertags)) {
2402 $allowedtags = array_merge($allowedtags, $allowothertags);
2403 }
2404 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2405 // The dol_escape_htmltag will escape html tags.
2406 if ($escapeonlyhtmltags) {
2407 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2408 } else {
2409 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2410 }
2411}
2412
2421function dolPrintHTMLForAttributeUrl($s)
2422{
2423 // 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)
2424 // The dol_escape_htmltag will escape html chars.
2425 $escapeonlyhtmltags = 1;
2426 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2427}
2428
2438function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2439{
2440 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2441}
2442
2449function dolPrintPassword($s)
2450{
2451 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2452}
2453
2454
2471function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2472{
2473 if ($noescapetags == 'common') {
2474 $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';
2475 // Add also html5 tags
2476 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2477 }
2478 if ($cleanalsojavascript) {
2479 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2480 }
2481
2482 // escape quotes and backslashes, newlines, etc.
2483 if ($escapeonlyhtmltags) {
2484 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2485 } else {
2486 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2487 // Because content can be or not already HTML.
2488 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2489 // In a future, we should not need this
2490
2491 $tmp = (string) $stringtoescape;
2492
2493 // We protect the 6 special entities that we don't want to decode.
2494 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2495 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2496 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2497 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2498 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2499 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2500
2501 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2502
2503 // We restore the 6 special entities that we don't want to have been decoded by previous command
2504 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2505 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2506 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2507 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2508 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2509 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2510
2511 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2512 }
2513 if (!$keepb) {
2514 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2515 }
2516 if (!$keepn) {
2517 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2518 } elseif ($keepn == -1) {
2519 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2520 }
2521
2522 if ($escapeonlyhtmltags) {
2523 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2524 return $tmp;
2525 } else {
2526 // Now we protect all the tags we want to keep
2527 $tmparrayoftags = array();
2528 if ($noescapetags) {
2529 $tmparrayoftags = explode(',', $noescapetags);
2530 }
2531
2532 if (count($tmparrayoftags)) {
2533 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2534
2535 $reg = array();
2536 // Remove reserved keywords. They are forbidden in a source string
2537 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2538
2539 foreach ($tmparrayoftags as $tagtoreplace) {
2540 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2541 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . '>/', '__BEGINTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2542 $tmp = str_ireplace('</' . $tagtoreplace . '>', '__ENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2543 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . ' \/>/', '__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2544
2545 // For case of tag with attributes
2546 do {
2547 $tmpold = $tmp;
2548
2549 if (preg_match('/<' . preg_quote($tagtoreplace, '/') . '(\s+)([^>]+)>/', $tmp, $reg)) {
2550 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2551 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2552 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2553 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2554 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2555 $tmp = str_replace('<' . $tagtoreplace . $reg[1] . $reg[2] . '>', '__BEGINTAGTOREPLACE' . $tagtoreplace . '[' . $tmpattributes . ']__', $tmp);
2556 }
2557
2558 $diff = strcmp($tmpold, $tmp);
2559 } while ($diff);
2560 }
2561
2562 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2563 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2564 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2565 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2566 }
2567
2568 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2569 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2570
2571 //print $result;
2572
2573 if (count($tmparrayoftags)) {
2574 // Restore protected tags
2575 foreach ($tmparrayoftags as $tagtoreplace) {
2576 $result = str_ireplace('__BEGINTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . '>', $result);
2577 $result = preg_replace('/__BEGINTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1>', $result);
2578 $result = str_ireplace('__ENDTAGTOREPLACE' . $tagtoreplace . '__', '</' . $tagtoreplace . '>', $result);
2579 $result = str_ireplace('__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . ' />', $result);
2580 $result = preg_replace('/__BEGINENDTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1 />', $result);
2581 }
2582
2583 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2584
2585 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2586 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2587 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2588 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2589 }
2590
2591 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2592
2593 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2594
2595 return $result;
2596 }
2597}
2598
2606function dol_strtolower($string, $encoding = "UTF-8")
2607{
2608 if (function_exists('mb_strtolower')) {
2609 return mb_strtolower($string, $encoding);
2610 } else {
2611 return strtolower($string);
2612 }
2613}
2614
2623function dol_strtoupper($string, $encoding = "UTF-8")
2624{
2625 if (function_exists('mb_strtoupper')) {
2626 return mb_strtoupper($string, $encoding);
2627 } else {
2628 return strtoupper($string);
2629 }
2630}
2631
2640function dol_ucfirst($string, $encoding = "UTF-8")
2641{
2642 if (function_exists('mb_substr')) {
2643 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding) . mb_substr($string, 1, null, $encoding);
2644 } else {
2645 return ucfirst($string);
2646 }
2647}
2648
2657function dol_ucwords($string, $encoding = "UTF-8")
2658{
2659 if (function_exists('mb_convert_case')) {
2660 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2661 } else {
2662 return ucwords($string);
2663 }
2664}
2665
2666
2672function getCallerInfoString()
2673{
2674 $backtrace = debug_backtrace();
2675 $msg = "";
2676 if (count($backtrace) >= 1) {
2677 $pos = 1;
2678 if (count($backtrace) == 1) {
2679 $pos = 0;
2680 }
2681 $trace = $backtrace[$pos];
2682 if (isset($trace['file'], $trace['line'])) {
2683 $msg = " From {$trace['file']}:{$trace['line']}.";
2684 }
2685 }
2686 return $msg;
2687}
2688
2711function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2712{
2713 global $conf, $user, $debugbar;
2714
2715 // If syslog module enabled
2716 if (!isModEnabled('syslog')) {
2717 return;
2718 }
2719
2720 // Check if we are into execution of code of a website
2721 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2722 global $website, $websitekey;
2723 if (is_object($website) && !empty($website->ref)) {
2724 $suffixinfilename .= '_website_' . $website->ref;
2725 } elseif (!empty($websitekey)) {
2726 $suffixinfilename .= '_website_' . $websitekey;
2727 }
2728 }
2729
2730 // Check if we have a forced suffix
2731 if (defined('USESUFFIXINLOG')) {
2732 $suffixinfilename .= constant('USESUFFIXINLOG');
2733 }
2734
2735 if ($ident < 0) {
2736 foreach ($conf->loghandlers as $loghandlerinstance) {
2737 $loghandlerinstance->setIdent($ident);
2738 }
2739 }
2740
2741 if (!empty($message)) {
2742 // Test log level
2743 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2744 $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');
2745
2746 if (!array_key_exists($level, $logLevels)) {
2747 dol_syslog('Error Bad Log Level ' . $level, LOG_ERR);
2748 $level = LOG_ERR;
2749 }
2750 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2751 return;
2752 }
2753
2754 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2755 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2756 }
2757
2758 // If adding log inside HTML page is required
2759 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2760 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))
2761 ) {
2762 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2763 $osuser = " " . sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2764
2765 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S") . " " . sprintf("%-7s", $logLevels[$level]) . " " . $ospid . " " . $osuser . " " . $message;
2766 }
2767
2768 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2769 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2770 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2771 print "\n\n<!-- Log start\n";
2772 print dol_escape_htmltag($message) . "\n";
2773 print "Log end -->\n";
2774 }
2775
2776 $data = array(
2777 'message' => $message,
2778 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2779 'level' => $level,
2780 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2781 'ip' => '',
2782 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2783 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2784 );
2785
2786 // For log, we want the reliable IP first.
2787 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2788 if (!empty($remoteip)) {
2789 $data['ip'] = $remoteip;
2790 // This is when server run behind a reverse proxy
2791 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2792 // $data['ip'] is last
2793 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2794 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2795 $data['ip'] = '';
2796 $foundremoteip = 0;
2797 $j = 0;
2798 foreach ($tmpips as $tmpip) {
2799 $tmpip = trim($tmpip);
2800 if (strtolower($tmpip) == strtolower($remoteip)) {
2801 $foundremoteip = 1;
2802 }
2803 if (empty($data['ip'])) {
2804 $data['ip'] = $tmpip;
2805 } else {
2806 $j++;
2807 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2808 }
2809 }
2810 if (!$foundremoteip) {
2811 $j++;
2812 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2813 }
2814 $data['ip'] .= (($j > 0) ? ']' : '');
2815 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2816 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2817 $data['ip'] = '';
2818 $foundremoteip = 0;
2819 $j = 0;
2820 foreach ($tmpips as $tmpip) {
2821 $tmpip = trim($tmpip);
2822 if (strtolower($tmpip) == strtolower($remoteip)) {
2823 $foundremoteip = 1;
2824 }
2825 if (empty($data['ip'])) {
2826 $data['ip'] = $tmpip;
2827 } else {
2828 $j++;
2829 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2830 }
2831 }
2832 if (!$foundremoteip) {
2833 $j++;
2834 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2835 }
2836 $data['ip'] .= (($j > 0) ? ']' : '');
2837 }
2838 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2839 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2840 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2841 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2842 // 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).
2843 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2844 } else {
2845 $data['ip'] = '???';
2846 }
2847
2848 if (!empty($_SERVER['USERNAME'])) {
2849 // 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).
2850 $data['osuser'] = (string) $_SERVER['USERNAME'];
2851 } elseif (!empty($_SERVER['LOGNAME'])) {
2852 // 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).
2853 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2854 }
2855
2856 // Loop on each log handler and send output
2857 foreach ($conf->loghandlers as $loghandlerinstance) {
2858 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2859 continue;
2860 }
2861 $loghandlerinstance->export($data, $suffixinfilename);
2862 }
2863 unset($data);
2864 }
2865
2866 if ($ident > 0) {
2867 foreach ($conf->loghandlers as $loghandlerinstance) {
2868 $loghandlerinstance->setIdent($ident);
2869 }
2870 }
2871}
2872
2884function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2885{
2886 global $langs, $db;
2887
2888 $form = new Form($db);
2889
2890 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2891 if (empty($templatenameforexport)) {
2892 $templatenameforexport = 'website_' . $website->ref;
2893 }
2894
2895 $out = '';
2896 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="' . dol_escape_htmltag($buttonstring) . '"/>';
2897
2898 // for generate popup
2899 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2900 $out .= 'jQuery(document).ready(function () {';
2901 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2902 $out .= ' var dialogHtml = \'';
2903
2904 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2905 $dialogcontent .= ' <div style="margin-top: 20px;">';
2906 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>' . $langs->trans("ExportSiteLabel") . '...</label><br>';
2907 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2908 $dialogcontent .= ' </div>';
2909 $dialogcontent .= ' <br>';
2910 $dialogcontent .= ' <div style="margin-top: 20px;">';
2911 $dialogcontent .= ' <strong>' . $langs->trans("ExportSiteGitLabel") . ' ' . $form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '') . '</strong><br>';
2912 $dialogcontent .= ' <form action="' . dol_escape_htmltag($overwriteGitUrl) . '" method="POST">';
2913 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2914 $dialogcontent .= ' <input type="hidden" name="token" value="' . newToken() . '">';
2915 $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>';
2916 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2917 $dialogcontent .= ' </form>';
2918 $dialogcontent .= ' </div>';
2919 $dialogcontent .= ' </div>';
2920
2921 $out .= dol_escape_js($dialogcontent);
2922
2923 $out .= '\';';
2924
2925
2926 // Add the content of the dialog to the body of the page
2927 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2928 $out .= ' if ($dialog.length > 0) {
2929 $dialog.remove();
2930 }
2931 jQuery("body").append(dialogHtml);';
2932
2933 // Configuration of popup
2934 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2935 $out .= ' autoOpen: false,';
2936 $out .= ' modal: true,';
2937 $out .= ' height: 290,';
2938 $out .= ' width: "40%",';
2939 $out .= ' title: "' . dol_escape_js($label) . '",';
2940 $out .= ' });';
2941
2942 // Simulate a click on the original "submit" input to export the site.
2943 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2944 $out .= ' console.log("Clic on exportsite.");';
2945 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2946 $out .= ' console.log("element founded:", target.length > 0);';
2947 $out .= ' if (target.length > 0) { target.click(); }';
2948 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2949 $out .= ' });';
2950
2951 // open popup
2952 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2953 $out .= ' return false;';
2954 $out .= ' });';
2955 $out .= '});';
2956 $out .= '</script>';
2957
2958 return $out;
2959}
2960
2961
2978function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
2979{
2980 global $conf;
2981
2982 if (strpos($url, '?') > 0) {
2983 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
2984 } else {
2985 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
2986 }
2987
2988 if (preg_match('/^https/i', $url)) {
2989 $urltoopen = $url;
2990 } else {
2991 $urltoopen = DOL_URL_ROOT . $url;
2992 }
2993
2994 $out = '';
2995
2996 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2997 $out .= '<!-- a link for button to open url into a dialog popup -->';
2998 $out .= '<a ' . ($accesskey ? ' accesskey="' . $accesskey . '"' : '') . ' class="cursorpointer reposition button_' . $name . ($morecss ? ' ' . $morecss : '') . '"' . $disabled . ' title="' . dol_escape_htmltag($label) . '"';
2999 if (empty($conf->use_javascript_ajax)) {
3000 $out .= ' href="' . $urltoopen . '" target="_blank"';
3001 } elseif ($jsonopen) {
3002 $out .= ' href="#" onclick="' . $jsonopen . '"';
3003 } else {
3004 $out .= ' href="#"';
3005 }
3006 $out .= '>' . $buttonstring . '</a>';
3007
3008 if (!empty($conf->use_javascript_ajax)) {
3009 // Add code to open url using the popup.
3010 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
3011 $out .= '<div id="idfordialog' . $name . '" class="hidden">' . (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '') . '</div>';
3012
3013 $out .= '<!-- Add js code to open dialog popup on dialog -->';
3014 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">
3015 jQuery(document).ready(function () {
3016 jQuery(".button_' . $name . '").click(function () {
3017 console.log(\'Open popup with jQuery(...).dialog() on URL ' . dol_escape_js($urltoopen) . '\');
3018 var $tmpdialog = $(\'#idfordialog' . $name . '\');
3019 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog' . $name . '" style="border: 0px;" src="' . $urltoopen . '" width="100%" height="98%"></iframe>\');
3020 $tmpdialog.dialog({
3021 autoOpen: false,
3022 modal: true,
3023 height: (window.innerHeight - 150),
3024 width: \'80%\',
3025 title: \'' . dol_escape_js($label) . '\',
3026 open: function (event, ui) {
3027 console.log("open popup name=' . $name . '");
3028 },
3029 close: function (event, ui) {
3030 console.log("Popup is closed, run jsonclose = ' . $jsonclose . '");
3031 ' . (empty($jsonclose) ? '' : $jsonclose . ';') . '
3032 }
3033 });
3034
3035 $tmpdialog.dialog(\'open\');
3036 return false;
3037 });
3038 });
3039 </script>';
3040 }
3041 return $out;
3042}
3043
3060function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
3061{
3062 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
3063}
3064
3082function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
3083{
3084 global $conf, $langs, $hookmanager;
3085
3086 // Show title
3087 $showtitle = 1;
3088 if (!empty($conf->dol_optimize_smallscreen)) {
3089 $showtitle = 0;
3090 }
3091
3092 $out = "\n" . '<!-- dol_fiche_head - dol_get_fiche_head -->';
3093
3094 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3095 $out .= '<div class="tabs' . ($picto ? '' : ' nopaddingleft') . '" data-role="controlgroup" data-type="horizontal">' . "\n";
3096 }
3097
3098 // Show right part
3099 if ($morehtmlright) {
3100 $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.
3101 }
3102
3103 // Show tabs
3104
3105 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
3106 $maxkey = -1;
3107 if (is_array($links) && !empty($links)) {
3108 $keys = array_keys($links);
3109 if (count($keys)) {
3110 $maxkey = max($keys);
3111 }
3112 }
3113
3114 // Show tabs
3115 // if =0 we don't use the feature
3116 if (empty($limittoshow)) {
3117 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
3118 }
3119 if (!empty($conf->dol_optimize_smallscreen)) {
3120 $limittoshow = 2;
3121 }
3122
3123 $displaytab = 0;
3124 $nbintab = 0;
3125 $popuptab = 0;
3126 $outmore = '';
3127 for ($i = 0; $i <= $maxkey; $i++) {
3128 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3129 // If active tab is already present
3130 if ($i >= $limittoshow) {
3131 $limittoshow--;
3132 }
3133 }
3134 }
3135
3136 for ($i = 0; $i <= $maxkey; $i++) {
3137 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3138 $isactive = true;
3139 } else {
3140 $isactive = false;
3141 }
3142
3143 if ($i < $limittoshow || $isactive) {
3144 // Output entry with a visible tab
3145 $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])) . ' -->';
3146
3147 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3148 if (!empty($links[$i][0])) {
3149 $out .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3150 } else {
3151 $out .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3152 }
3153 } elseif (!empty($links[$i][1])) {
3154 //print "x $i $active ".$links[$i][2]." z";
3155 $out .= '<div class="tab tab' . ($isactive ? 'active' : 'unactive') . '" style="margin: 0 !important">';
3156
3157 if (!empty($links[$i][0])) {
3158 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
3159 $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) . '">';
3160 }
3161
3162 if ($displaytab == 0 && $picto) {
3163 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
3164 }
3165
3166 $out .= $links[$i][1];
3167 if (!empty($links[$i][0])) {
3168 $out .= '</a>' . "\n";
3169 }
3170 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
3171 $out .= '</div>';
3172 }
3173
3174 $out .= '</div>';
3175 } else {
3176 // Add entry into the combo popup with the other tabs
3177 if (!$popuptab) {
3178 $popuptab = 1;
3179 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
3180 }
3181 $outmore_content = '';
3182
3183 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3184 if (!empty($links[$i][0])) {
3185 $outmore_content .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3186 } else {
3187 $outmore_content .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3188 }
3189 } elseif (!empty($links[$i][1])) {
3190 $outmore_content .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="wordwrap inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
3191 $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.
3192 $outmore_content .= '</a>' . "\n";
3193 }
3194 if ($outmore_content !== '') {
3195 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
3196 }
3197
3198 $nbintab++;
3199 }
3200
3201 $displaytab = $i + 1;
3202 }
3203 if ($popuptab) {
3204 $outmore .= '</div>';
3205 }
3206
3207 if ($popuptab) { // If there is some tabs not shown
3208 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3209 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3210 $widthofpopup = 240;
3211
3212 $tabsname = $moretabssuffix;
3213 if (empty($tabsname)) {
3214 $tabsname = str_replace("@", "", $picto);
3215 }
3216 $out .= '<div id="moretabs' . $tabsname . '" class="inline-block tabsElem valignmiddle">';
3217 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3218 $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".
3219 }
3220 $out .= '<div id="moretabsList' . $tabsname . '" style="width: ' . $widthofpopup . 'px; position: absolute; ' . $left . ': -999em; text-align: ' . $left . '; margin:0px; padding:2px; z-index:10;">';
3221 $out .= $outmore;
3222 $out .= '</div>';
3223 $out .= '<div></div>';
3224 $out .= "</div>\n";
3225
3226 $out .= '<script nonce="' . getNonce() . '">';
3227 $out .= "$('#moretabs" . $tabsname . "').mouseenter( function() {
3228 var x = this.offsetLeft, y = this.offsetTop;
3229 console.log('mouseenter " . $left . " x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3230 if ((window.innerWidth - x) < " . ($widthofpopup + 10) . ") {
3231 $('#moretabsList" . $tabsname . "').css('" . $right . "','8px');
3232 }
3233 $('#moretabsList" . $tabsname . "').css('" . $left . "','auto');
3234 });
3235 ";
3236 $out .= "$('#moretabs" . $tabsname . "').mouseleave( function() { console.log('mouseleave " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','-999em');});";
3237 $out .= "</script>";
3238 }
3239
3240 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3241 $out .= "</div>\n";
3242 }
3243
3244 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3245 $out .= "\n" . '<div id="dragDropAreaTabBar" class="tabBar' . ($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '') . ($notab == -4 ? '' : ' tabBarWithBottom'))));
3246 $out .= ($morecssdiv ? ' ' . $morecssdiv : '');
3247 $out .= '">' . "\n";
3248 }
3249 if (!empty($dragdropfile)) {
3250 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3251 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3252 }
3253 $parameters = array('tabname' => $active, 'out' => $out);
3254 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3255 if ($reshook > 0) {
3256 $out = $hookmanager->resPrint;
3257 }
3258
3259 return $out;
3260}
3261
3269function dol_fiche_end($notab = 0)
3270{
3271 print dol_get_fiche_end($notab);
3272}
3273
3280function dol_get_fiche_end($notab = 0)
3281{
3282 if (!$notab || $notab == -1) {
3283 return "\n</div>\n";
3284 } else {
3285 return '';
3286 }
3287}
3288
3308function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3309{
3310 global $conf, $form, $user, $langs, $hookmanager, $action;
3311
3312 $error = 0;
3313
3314 $maxvisiblephotos = 1;
3315 $showimage = 1;
3316 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3317 // @phan-suppress-next-line PhanUndeclaredMethod
3318 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3319 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3320 $showbarcode = 0;
3321 }
3322 $modulepart = 'unknown';
3323
3324 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3325 $modulepart = $object->element;
3326 } elseif ($object->element == 'member') {
3327 $modulepart = 'memberphoto';
3328 } elseif ($object->element == 'user') {
3329 $modulepart = 'userphoto';
3330 }
3331
3332 if (class_exists("Imagick")) {
3333 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3334 $modulepart = $object->element;
3335 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3336 $modulepart = 'ficheinter';
3337 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3338 $modulepart = 'contract';
3339 } elseif ($object->element == 'order_supplier') {
3340 $modulepart = 'supplier_order';
3341 } elseif ($object->element == 'invoice_supplier') {
3342 $modulepart = 'supplier_invoice';
3343 }
3344 }
3345
3346 if ($object->element == 'product') {
3348 '@phan-var-force Product $object';
3349 $width = 80;
3350 $cssclass = 'photowithmargin photoref';
3351 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3352 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3353 if ($conf->browser->layout == 'phone') {
3354 $maxvisiblephotos = 1;
3355 }
3356 if ($showimage) {
3357 $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>';
3358 } else {
3359 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3360 $nophoto = '';
3361 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3362 } else { // Show no photo link
3363 $nophoto = '/public/theme/common/nophoto.png';
3364 $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>';
3365 }
3366 }
3367 } elseif ($object->element == 'category') {
3369 '@phan-var-force Categorie $object';
3370 $width = 80;
3371 $cssclass = 'photowithmargin photoref';
3372 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3373 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3374 if ($conf->browser->layout == 'phone') {
3375 $maxvisiblephotos = 1;
3376 }
3377 if ($showimage) {
3378 $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>';
3379 } else {
3380 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3381 $nophoto = '';
3382 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3383 } else { // Show no photo link
3384 $nophoto = '/public/theme/common/nophoto.png';
3385 $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>';
3386 }
3387 }
3388 } elseif ($object->element == 'bom') {
3390 '@phan-var-force Bom $object';
3391 $width = 80;
3392 $cssclass = 'photowithmargin photoref';
3393 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3394 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3395 if ($conf->browser->layout == 'phone') {
3396 $maxvisiblephotos = 1;
3397 }
3398 if ($showimage) {
3399 $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>';
3400 } else {
3401 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3402 $nophoto = '';
3403 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3404 } else { // Show no photo link
3405 $nophoto = '/public/theme/common/nophoto.png';
3406 $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>';
3407 }
3408 }
3409 } elseif ($object->element == 'ticket') {
3410 $width = 80;
3411 $cssclass = 'photoref';
3413 '@phan-var-force Ticket $object';
3414 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity] . '/' . $object->ref);
3415 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3416 if ($conf->browser->layout == 'phone') {
3417 $maxvisiblephotos = 1;
3418 }
3419
3420 if ($showimage) {
3421 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3422 if ($object->nbphoto > 0) {
3423 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $showphoto . '</div>';
3424 } else {
3425 $showimage = 0;
3426 }
3427 }
3428 if (!$showimage) {
3429 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3430 $nophoto = '';
3431 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3432 } else { // Show no photo link
3433 $nophoto = img_picto('No photo', 'object_ticket');
3434 $morehtmlleft .= '<!-- No photo to show -->';
3435 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3436 $morehtmlleft .= $nophoto;
3437 $morehtmlleft .= '</div></div>';
3438 }
3439 }
3440 } else {
3441 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3442 $phototoshow = '';
3443 // Check if a preview file is available
3444 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3445 $objectref = dol_sanitizeFileName($object->ref);
3446 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity]) . "/";
3447 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3448 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3449 $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
3450 } else {
3451 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3452 }
3453 if (empty($subdir)) {
3454 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3455 }
3456
3457 $filepath = $dir_output . $subdir . "/";
3458
3459 $filepdf = $filepath . $objectref . ".pdf";
3460 $relativepath = $subdir . '/' . $objectref . '.pdf';
3461
3462 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3463 $fileimage = $filepdf . '_preview.png';
3464 $relativepathimage = $relativepath . '_preview.png';
3465
3466 $pdfexists = file_exists($filepdf);
3467
3468 // If PDF file exists
3469 if ($pdfexists) {
3470 // Conversion du PDF en image png si fichier png non existent
3471 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3472 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3473 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3474 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3475 if ($ret < 0) {
3476 $error++;
3477 }
3478 }
3479 }
3480 }
3481
3482 if ($pdfexists && !$error) {
3483 $heightforphotref = 80;
3484 if (!empty($conf->dol_optimize_smallscreen)) {
3485 $heightforphotref = 60;
3486 }
3487 // If the preview file is found
3488 if (file_exists($fileimage)) {
3489 $phototoshow = '<div class="photoref">';
3490 $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithborder" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($relativepathimage) . '">';
3491 $phototoshow .= '</div>';
3492 }
3493 }
3494 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3495 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3496 }
3497
3498 if ($phototoshow) {
3499 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3500 $morehtmlleft .= $phototoshow;
3501 $morehtmlleft .= '</div>';
3502 }
3503 }
3504
3505 if (empty($phototoshow)) { // Show No photo link (picto of object)
3506 if ($object->element == 'action') {
3507 $width = 80;
3508 $cssclass = 'photorefcenter';
3509 $nophoto = img_picto('No photo', 'title_agenda');
3510 } else {
3511 $width = 14;
3512 $cssclass = 'photorefcenter';
3513 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3514 $prefix = 'object_';
3515 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3516 $picto = 'project'; // instead of projectpub
3517 }
3518 if (strpos($picto, 'fontawesome_') !== false) {
3519 $prefix = '';
3520 }
3521 $nophoto = img_picto('No photo', $prefix . $picto);
3522 }
3523 $morehtmlleft .= '<!-- No photo to show -->';
3524 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3525 $morehtmlleft .= $nophoto;
3526 $morehtmlleft .= '</div></div>';
3527 }
3528 }
3529
3530 // Show barcode
3531 if ($showbarcode) {
3532 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $form->showbarcode($object, 100, 'photoref valignmiddle') . '</div>';
3533 }
3534
3535 if ($object->element == 'societe') {
3537 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3538 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3539 } else {
3540 $morehtmlstatus .= $object->getLibStatut(6);
3541 }
3542 } elseif ($object->element == 'product') {
3544 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3545 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3546 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3547 } else {
3548 $morehtmlstatus .= '<span class="statusrefsell">' . $object->getLibStatut(6, 0) . '</span>';
3549 }
3550 $morehtmlstatus .= ' &nbsp; ';
3551 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3552 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3553 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'status_buy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3554 } else {
3555 $morehtmlstatus .= '<span class="statusrefbuy">' . $object->getLibStatut(6, 1) . '</span>';
3556 }
3557 } elseif (in_array($object->element, array('salary'))) {
3559 '@phan-var-force Salary $object';
3560 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3561 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3562 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3563 }
3564 $morehtmlstatus .= $tmptxt;
3565 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) {
3567 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3568 if (!isset($object->alreadypaid)) {
3569 $object->totalpaid = $object->getSommePaiement(0);
3570 $object->totalcreditnotes = $object->getSumCreditNotesUsed(0);
3571 $object->totaldeposits = $object->getSumDepositsUsed(0);
3572 $object->alreadypaid = $object->totalpaid + $object->totalcreditnotes + $object->totaldeposits;
3573 }
3574 $tmptxt = $object->getLibStatut(6, (float) $object->alreadypaid);
3575 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3576 $tmptxt = $object->getLibStatut(5, (float) $object->alreadypaid);
3577 }
3578 $morehtmlstatus .= $tmptxt;
3579 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid like for invoices
3581 '@phan-var-force ChargeSociales|Loan|Tva $object';
3582 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3583 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3584 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3585 }
3586 $morehtmlstatus .= $tmptxt;
3587 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3589 if ($object->status == 0) {
3590 $morehtmlstatus .= $object->getLibStatut(5);
3591 } else {
3592 $morehtmlstatus .= $object->getLibStatut(4);
3593 }
3594 } elseif ($object->element == 'facturerec') {
3596 '@phan-var-force FactureRec $object';
3597 if ($object->frequency == 0) {
3598 $morehtmlstatus .= $object->getLibStatut(2);
3599 } else {
3600 $morehtmlstatus .= $object->getLibStatut(5);
3601 }
3602 } elseif ($object->element == 'project_task') {
3604 $tmptxt = $object->getLibStatut(4);
3605 $morehtmlstatus .= $tmptxt;
3606 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3607 $tmptxt = $object->getLibStatut(6);
3608 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3609 $tmptxt = $object->getLibStatut(5);
3610 }
3611 $morehtmlstatus .= $tmptxt;
3612 }
3613
3614 // Say if object was dispatched/transferred "into accountancy"
3615 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3616 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3617 if (method_exists($object, 'getVentilExportCompta')) {
3618 $accounted = $object->getVentilExportCompta(1);
3619 $langs->load("accountancy");
3620 $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>';
3621 }
3622 }
3623
3624 // Add alias for thirdparty
3625 if (!empty($object->name_alias)) {
3627 '@phan-var-force Societe $object';
3628 $morehtmlref .= '<div class="refidno opacitymedium">' . dol_escape_htmltag($object->name_alias) . '</div>';
3629 }
3630
3631 // Add label
3632 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3634 if (!empty($object->label)) {
3635 $morehtmlref .= '<div class="refidno banner-object-label">' . $object->label . '</div>';
3636 }
3637 }
3638 // Show address and email
3639 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3640 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3641 if ($moreaddress) {
3642 $morehtmlref .= '<div class="refidno refaddress">';
3643 $morehtmlref .= $moreaddress;
3644 $morehtmlref .= '</div>';
3645 }
3646 }
3647 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)) {
3648 $morehtmlref .= '<div style="clear: both;"></div>';
3649 $morehtmlref .= '<div class="refidno opacitymedium">';
3650 $morehtmlref .= $langs->trans("TechnicalID") . ': ' . ((int) $object->id);
3651 $morehtmlref .= '</div>';
3652 }
3653
3654 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3655 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3656 if ($reshook < 0) {
3657 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3658 } elseif (empty($reshook)) {
3659 $morehtmlref .= $hookmanager->resPrint;
3660 } elseif ($reshook > 0) {
3661 $morehtmlref = $hookmanager->resPrint;
3662 }
3663
3664 // $morehtml is the right part (link "Back to list")
3665 // $morehtmlref is the part after the ref
3666 // $morehtmlleft is the picto or photo of banner
3667 // $morehtmlstatus is part under the status
3668 // $morehtmlright is part of htmlright
3669
3670 print '<div class="' . ($onlybanner ? 'arearefnobottom ' : 'arearef ') . 'heightref valignmiddle centpercent object-banner-tab-container" data-module-part="'.dolPrintHTMLForAttribute($modulepart).'">';
3671 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3672 print '</div>';
3673 print '<div class="underrefbanner clearboth"></div>';
3674}
3675
3685function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3686{
3687 global $langs;
3688 $ret = '';
3689 if ($fieldrequired) {
3690 $ret .= '<span class="fieldrequired">';
3691 }
3692 $ret .= '<label for="' . $fieldkey . '">';
3693 $ret .= $langs->trans($langkey);
3694 $ret .= '</label>';
3695 if ($fieldrequired) {
3696 $ret .= '</span>';
3697 }
3698 return $ret;
3699}
3700
3714function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3715{
3716 global $langs, $hookmanager;
3717
3718 $ret = '';
3719 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3720
3721 // See format of addresses on https://en.wikipedia.org/wiki/Address
3722 // Address
3723 if (empty($mode)) {
3724 $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)));
3725 }
3726 // Zip/Town/State
3727 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3728 // US: title firstname name \n address lines \n town, state, zip \n country
3729 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3730 $ret .= (($ret && $town) ? $sep : '') . $town;
3731
3732 if (!empty($object->state)) {
3733 $ret .= ($ret ? ($town ? ", " : $sep) : '') . $object->state;
3734 }
3735 if (!empty($object->zip)) {
3736 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '') . $object->zip;
3737 }
3738 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3739 // UK: title firstname name \n address lines \n town state \n zip \n country
3740 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3741 $ret .= ($ret ? $sep : '') . $town;
3742 if (!empty($object->state)) {
3743 $ret .= ($ret ? ", " : '') . $object->state;
3744 }
3745 if (!empty($object->zip)) {
3746 $ret .= ($ret ? $sep : '') . $object->zip;
3747 }
3748 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3749 // ES: title firstname name \n address lines \n zip town \n state \n country
3750 $ret .= ($ret ? $sep : '') . $object->zip;
3751 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3752 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3753 if (!empty($object->state)) {
3754 $ret .= $sep . $object->state;
3755 }
3756 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3757 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3758 // See https://www.sljfaq.org/afaq/addresses.html
3759 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3760 $ret .= ($ret ? $sep : '') . ($object->state ? $object->state . ', ' : '') . $town . ($object->zip ? ' ' : '') . $object->zip;
3761 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3762 // IT: title firstname name\n address lines \n zip town state_code \n country
3763 $ret .= ($ret ? $sep : '') . $object->zip;
3764 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3765 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3766 $ret .= (empty($object->state_code) ? '' : (' ' . $object->state_code));
3767 } else {
3768 // Other: title firstname name \n address lines \n zip town[, state] \n country
3769 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3770 $ret .= !empty($object->zip) ? (($ret ? $sep : '') . $object->zip) : '';
3771 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')) . $town) : '');
3772 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3773 $ret .= ($ret ? ", " : '') . $object->state;
3774 }
3775 }
3776
3777 if (!is_object($outputlangs)) {
3778 $outputlangs = $langs;
3779 }
3780 if ($withcountry) {
3781 $langs->load("dict");
3782 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '') . $outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country" . $object->country_code)));
3783 }
3784 if ($hookmanager) {
3785 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs, 'mode' => $mode, 'extralangcode' => $extralangcode);
3786 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3787 if ($reshook > 0) {
3788 $ret = '';
3789 }
3790 $ret .= $hookmanager->resPrint;
3791 }
3792
3793 return $ret;
3794}
3795
3796
3797
3807function dol_strftime($fmt, $ts = false, $is_gmt = false)
3808{
3809 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3810 return dol_print_date($ts, $fmt, $is_gmt);
3811 } else {
3812 return 'Error date outside supported range';
3813 }
3814}
3815
3838function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false, $decorate = 0)
3839{
3840 global $conf, $langs;
3841
3842 // If date undefined or "", we return ""
3843 if (dol_strlen((string) $time) == 0) {
3844 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3845 }
3846
3847 if ($tzoutput === 'auto') {
3848 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3849 }
3850
3851 // Clean parameters
3852 $to_gmt = false; // false if we want date in server timezone, true if we want to add offset
3853 $offsettz = $offsetdst = 0;
3854 if ($tzoutput) {
3855 $to_gmt = true; // For backward compatibility
3856 if (is_string($tzoutput)) {
3857 if ($tzoutput == 'tzserver') {
3858 $to_gmt = false;
3859 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3860 // @phan-suppress-next-line PhanPluginRedundantAssignment
3861 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3862 // @phan-suppress-next-line PhanPluginRedundantAssignment
3863 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3864 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3865 $to_gmt = true;
3866 // if no session (by example in cron) may use MAIN_DOLIBARR_USER_TIMEZONE instead UTC
3867 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? getDolGlobalString('MAIN_DOLIBARR_USER_TIMEZONE', 'UTC') : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3868
3869 if (class_exists('DateTimeZone')) {
3870 try {
3871 $user_date_tz = new DateTimeZone($offsettzstring);
3872 } catch (Exception $e) {
3873 // Bad value for $offsettzstring
3874 dol_syslog("DateInvalidTimeZoneException for timezone string '".$offsettzstring."'. Falling back to UTC.", LOG_ERR);
3875 $user_date_tz = new DateTimeZone('UTC'); // Force valid timezone as UTC
3876 }
3877 $user_dt = new DateTime();
3878 $user_dt->setTimezone($user_date_tz);
3879 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3880 $offsettz = $user_dt->getOffset(); // should include dst ?
3881 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3882 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3883 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3884 }
3885 }
3886 }
3887 }
3888 if (!is_object($outputlangs)) {
3889 $outputlangs = $langs;
3890 }
3891 if (!$format) {
3892 $format = 'daytextshort';
3893 }
3894
3895 // Do we have to reduce the length of date (year on 2 chars) to save space.
3896 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3897 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3898 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3899 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3900 if ($formatwithoutreduce != $format) {
3901 $format = $formatwithoutreduce;
3902 $reduceformat = 1;
3903 } // so format 'dayreduceformat' is processed like day
3904
3905 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3906 // TODO Add format daysmallyear and dayhoursmallyear
3907 if ($format == 'day') {
3908 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3909 } elseif ($format == 'hour') {
3910 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3911 } elseif ($format == 'hourduration') {
3912 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3913 } elseif ($format == 'daytext') {
3914 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3915 } elseif ($format == 'daytextshort') {
3916 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3917 } elseif ($format == 'dayhour') {
3918 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3919 } elseif ($format == 'dayhoursec') {
3920 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3921 } elseif ($format == 'dayhourtext') {
3922 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3923 } elseif ($format == 'dayhourtextshort') {
3924 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3925 } elseif ($format == 'dayhourlog') {
3926 // Format not sensitive to language
3927 $format = '%Y%m%d%H%M%S';
3928 } elseif ($format == 'dayhourlogsmall') {
3929 // Format not sensitive to language
3930 $format = '%y%m%d%H%M';
3931 } elseif ($format == 'dayhourldap') {
3932 $format = '%Y%m%d%H%M%SZ';
3933 } elseif ($format == 'dayhourxcard') {
3934 $format = '%Y%m%dT%H%M%SZ';
3935 } elseif ($format == 'dayxcard') {
3936 $format = '%Y%m%d';
3937 } elseif ($format == 'dayrfc') {
3938 $format = '%Y-%m-%d'; // DATE_RFC3339
3939 } elseif ($format == 'dayhourrfc') {
3940 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3941 } elseif ($format == 'standard') {
3942 $format = '%Y-%m-%d %H:%M:%S';
3943 }
3944
3945 if ($reduceformat) {
3946 $format = str_replace('%Y', '%y', $format);
3947 $format = str_replace('yyyy', 'yy', $format);
3948 }
3949
3950 // Clean format
3951 if (preg_match('/%b/i', $format)) { // There is some text to translate
3952 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3953 $format = str_replace('%b', '__b__', $format);
3954 $format = str_replace('%B', '__B__', $format);
3955 }
3956 if (preg_match('/%a/i', $format)) { // There is some text to translate
3957 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3958 $format = str_replace('%a', '__a__', $format);
3959 $format = str_replace('%A', '__A__', $format);
3960 }
3961
3962 // Analyze date
3963 $reg = array();
3964 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
3965 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString());
3966 return '';
3967 } 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
3968 // This part of code should not be used anymore.
3969 dol_syslog("Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString(), LOG_WARNING);
3970 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3971 $syear = (!empty($reg[1]) ? $reg[1] : '');
3972 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3973 $sday = (!empty($reg[3]) ? $reg[3] : '');
3974 $shour = (!empty($reg[4]) ? $reg[4] : '');
3975 $smin = (!empty($reg[5]) ? $reg[5] : '');
3976 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3977
3978 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3979
3980 if ($to_gmt) {
3981 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3982 } else {
3983 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3984 }
3985 $dtts = new DateTime();
3986 $dtts->setTimestamp($time);
3987 $dtts->setTimezone($tzo);
3988 $newformat = str_replace(
3989 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3990 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3991 $format
3992 );
3993 $ret = $dtts->format($newformat);
3994 $ret = str_replace(
3995 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3996 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3997 $ret
3998 );
3999 } else {
4000 // Date is a timestamps
4001 if ($time < 100000000000) { // Protection against bad date values
4002 $dtts = new DateTime();
4003 //var_dump($tzoutput.' '.$offsettzstring.' '.$offsettz.$offsetdst.' x '.$to_gmt);
4004 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
4005 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4006
4007 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4008 $dtts->setTimezone($tzo); // important: must be before the setTimestamp
4009 $dtts->setTimestamp($timetouse);
4010 } else {
4011 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4012
4013 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4014 $dtts->setTimestamp($timetouse); // TODO May be we can invert setTimestamp and setTimezone
4015 $dtts->setTimezone($tzo);
4016 }
4017
4018 $newformat = str_replace(
4019 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4020 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4021 $format
4022 );
4023
4024 $ret = $dtts->format($newformat);
4025 //var_dump($timetouse, $offsettz, $offsetdst, $tzo, $newformat, $ret);
4026 $ret = str_replace(
4027 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4028 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4029 $ret
4030 );
4031 } else {
4032 $ret = 'Bad value ' . $time . ' for date';
4033 }
4034 }
4035
4036 if (preg_match('/__b__/i', $format)) {
4037 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4038
4039 if ($to_gmt) {
4040 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4041 } else {
4042 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4043 }
4044 $dtts = new DateTime();
4045 $dtts->setTimestamp($timetouse);
4046 $dtts->setTimezone($tzo);
4047 $month = (int) $dtts->format("m");
4048 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
4049 if ($encodetooutput) {
4050 $monthtext = $outputlangs->transnoentities('Month' . $month);
4051 $monthtextshort = $outputlangs->transnoentities('MonthShort' . $month);
4052 } else {
4053 $monthtext = $outputlangs->transnoentitiesnoconv('Month' . $month);
4054 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort' . $month);
4055 }
4056 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
4057 $ret = str_replace('__b__', $monthtextshort, $ret);
4058 $ret = str_replace('__B__', $monthtext, $ret);
4059 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
4060 //return $ret;
4061 }
4062 if (preg_match('/__a__/i', $format)) {
4063 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
4064 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
4065
4066 if ($to_gmt) {
4067 $tzo = new DateTimeZone('UTC');
4068 } else {
4069 $tzo = new DateTimeZone(date_default_timezone_get());
4070 }
4071 $dtts = new DateTime();
4072 $dtts->setTimestamp($timetouse);
4073 $dtts->setTimezone($tzo);
4074 $w = $dtts->format("w");
4075 $dayweek = $outputlangs->transnoentitiesnoconv('Day' . $w);
4076
4077 $ret = str_replace('__A__', $dayweek, $ret);
4078 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
4079 }
4080
4081 if ($decorate) {
4082 $ret = preg_replace('/(\d\d:\d\d [AP]M)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4083 $ret = preg_replace('/(\d\d:\d\d)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4084 }
4085
4086 return $ret;
4087}
4088
4089
4110function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
4111{
4112 if ($timestamp === '') {
4113 return array();
4114 }
4115
4116 $datetimeobj = new DateTime();
4117 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
4118 if ($forcetimezone) {
4119 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
4120 }
4121 $arrayinfo = array(
4122 'year' => ((int) date_format($datetimeobj, 'Y')),
4123 'mon' => ((int) date_format($datetimeobj, 'm')),
4124 'mday' => ((int) date_format($datetimeobj, 'd')),
4125 'wday' => ((int) date_format($datetimeobj, 'w')),
4126 'yday' => ((int) date_format($datetimeobj, 'z')),
4127 'hours' => ((int) date_format($datetimeobj, 'H')),
4128 'minutes' => ((int) date_format($datetimeobj, 'i')),
4129 'seconds' => ((int) date_format($datetimeobj, 's')),
4130 '0' => $timestamp
4131 );
4132
4133 return $arrayinfo;
4134}
4135
4157function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
4158{
4159 global $conf;
4160 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
4161
4162 if ($gm === 'auto') {
4163 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
4164 }
4165 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
4166
4167 // Clean parameters
4168 if ($hour == -1 || empty($hour)) {
4169 $hour = 0;
4170 }
4171 if ($minute == -1 || empty($minute)) {
4172 $minute = 0;
4173 }
4174 if ($second == -1 || empty($second)) {
4175 $second = 0;
4176 }
4177
4178 // Check parameters
4179 if ($check) {
4180 if (!$month || !$day) {
4181 return '';
4182 }
4183 if ($day > 31) {
4184 return '';
4185 }
4186 if ($month > 12) {
4187 return '';
4188 }
4189 if ($hour < 0 || $hour > 24) {
4190 return '';
4191 }
4192 if ($minute < 0 || $minute > 60) {
4193 return '';
4194 }
4195 if ($second < 0 || $second > 60) {
4196 return '';
4197 }
4198 }
4199
4200 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
4201 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
4202 $localtz = new DateTimeZone($default_timezone);
4203 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
4204 // We use dol_tz_string first because it is more reliable.
4205 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
4206 try {
4207 $localtz = new DateTimeZone($default_timezone);
4208 } catch (Exception $e) {
4209 dol_syslog("Warning dol_tz_string contains an invalid value " . json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
4210 $default_timezone = @date_default_timezone_get();
4211 }
4212 } elseif (strrpos($gm, "tz,") !== false) {
4213 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
4214 try {
4215 $localtz = new DateTimeZone($timezone);
4216 } catch (Exception $e) {
4217 dol_syslog("Warning passed timezone contains an invalid value " . $timezone, LOG_WARNING);
4218 }
4219 }
4220
4221 if (empty($localtz)) {
4222 $localtz = new DateTimeZone('UTC');
4223 }
4224 $dt = new DateTime('now', $localtz);
4225 $dt->setDate((int) $year, (int) $month, (int) $day);
4226 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4227 $date = $dt->getTimestamp(); // should include daylight saving time
4228
4229 return $date;
4230}
4231
4232
4243function dol_now($mode = 'gmt')
4244{
4245 $ret = 0;
4246
4247 if ($mode === 'auto') {
4248 $mode = 'gmt';
4249 }
4250
4251 if ($mode == 'gmt') {
4252 $ret = time(); // Time for now at greenwich.
4253 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4254 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
4255 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4256 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4257 // } elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4258 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4259 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4260 // $ret=dol_now('gmt')+($tzsecond*3600);
4261 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4262 // Time for now with user timezone added
4263 // print 'time: '.time();
4264 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4265 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4266 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4267 }
4268
4269 return $ret;
4270}
4271
4272
4281function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4282{
4283 global $conf, $langs;
4284 $level = 1024;
4285
4286 if (!empty($conf->dol_optimize_smallscreen)) {
4287 $shortunit = 1;
4288 }
4289
4290 // Set value text
4291 if (empty($shortvalue) || $size < ($level * 10)) {
4292 $ret = $size;
4293 $textunitshort = $langs->trans("b");
4294 $textunitlong = $langs->trans("Bytes");
4295 } else {
4296 $ret = round($size / $level, 0);
4297 $textunitshort = $langs->trans("Kb");
4298 $textunitlong = $langs->trans("KiloBytes");
4299 }
4300 // Use long or short text unit
4301 if (empty($shortunit)) {
4302 $ret .= ' ' . $textunitlong;
4303 } else {
4304 $ret .= ' ' . $textunitshort;
4305 }
4306
4307 return $ret;
4308}
4309
4320function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4321{
4322 global $langs;
4323
4324 if (empty($url)) {
4325 return '';
4326 }
4327
4328 $linkstart = '<a href="';
4329 if (!preg_match('/^http/i', $url)) {
4330 $linkstart .= 'http://';
4331 }
4332 $linkstart .= $url;
4333 $linkstart .= '"';
4334 if ($target) {
4335 $linkstart .= ' target="' . $target . '"';
4336 }
4337 $linkstart .= ' title="' . $langs->trans("URL") . ': ' . $url . '"';
4338 $linkstart .= '>';
4339
4340 $link = '';
4341 if (!preg_match('/^http/i', $url)) {
4342 $link .= 'http://';
4343 }
4344 $link .= dol_trunc($url, $max);
4345
4346 $linkend = '</a>';
4347
4348 if ($morecss == 'float') { // deprecated
4349 return '<div class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '') . $link . '</div>';
4350 } else {
4351 return $linkstart . '<span class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '') . $link . '</span>' . $linkend;
4352 }
4353}
4354
4368function dol_print_email($email, $contactid = 0, $socid = 0, $addlink = 0, $max = 0, $showinvalid = 2, $withpicto = 0, $morecss = 'paddingrightonly')
4369{
4370 global $user, $langs, $hookmanager;
4371
4372 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4373 //$showinvalid = 1; $email = 'rrrrr';
4374
4375 $newemail = dol_escape_htmltag($email);
4376
4377 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4378 $withpicto = 0;
4379 }
4380
4381 if (empty($email)) {
4382 return '&nbsp;';
4383 }
4384
4385 if ($addlink == 1) {
4386 $newemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="';
4387 if (!preg_match('/^mailto:/i', $email)) {
4388 $newemail .= 'mailto:';
4389 }
4390 $newemail .= $email;
4391 $newemail .= '" target="_blank">';
4392
4393 $newemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4394
4395 if ($max > 0) {
4396 $newemail .= dol_trunc($email, $max);
4397 } else {
4398 $newemail .= $email;
4399 }
4400 $newemail .= '</a>';
4401
4402 if ($showinvalid) {
4403 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4404 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4405 $emailonly = CMailFile::getValidAddress($email, 2);
4406 if (!isValidEmail($emailonly)) {
4407 $langs->load("errors");
4408 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $emailonly), '', 'paddingrightonly');
4409 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4410 $langs->load("errors");
4411 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly), '', 'paddingrightonly');
4412 }
4413 }
4414
4415 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4416 $type = 'AC_EMAIL';
4417 $linktoaddaction = '';
4418 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4419 $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>';
4420 }
4421 if ($linktoaddaction) {
4422 $newemail = '<div>' . $newemail . ' ' . $linktoaddaction . '</div>';
4423 }
4424 }
4425 } elseif ($addlink === 'thirdparty') {
4426 $tmpnewemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="' . DOL_URL_ROOT . '/societe/card.php?socid=' . $socid . '&action=presend&mode=init#formmailbeforetitle">';
4427 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4428 if ($withpicto == 1) {
4429 $tmpnewemail .= $newemail;
4430 }
4431 $tmpnewemail .= '</a>';
4432
4433 $newemail = $tmpnewemail;
4434 } else {
4435 $newemail = ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '') . $newemail;
4436
4437 if ($showinvalid) {
4438 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4439 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4440 $emailonly = CMailFile::getValidAddress($email, 2);
4441 if (!isValidEmail($emailonly)) {
4442 $langs->load("errors");
4443 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $email));
4444 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4445 $langs->load("errors");
4446 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly));
4447 }
4448 }
4449 }
4450
4451 //$rep = '<div class="nospan" style="margin-right: 10px">';
4452 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4453 //$rep .= '</div>';
4454 $rep = $newemail;
4455
4456 if ($hookmanager) {
4457 $parameters = array('cid' => $contactid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4458
4459 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4460 if ($reshook > 0) {
4461 $rep = '';
4462 }
4463 $rep .= $hookmanager->resPrint;
4464 }
4465
4466 return $rep;
4467}
4468
4474function getArrayOfSocialNetworks()
4475{
4476 global $db;
4477
4478 $socialnetworks = array();
4479 // Enable caching of array
4480 require_once DOL_DOCUMENT_ROOT . '/core/lib/memory.lib.php';
4481 $cachekey = dol_sanitizeKeyCode(str_replace(',', '_', 'socialnetworks_'.getEntity('c_socialnetworks')));
4482 $dataretrieved = dol_getcache($cachekey);
4483
4484 if (!is_null($dataretrieved)) {
4485 $socialnetworks = $dataretrieved;
4486 } else {
4487 $sql = "SELECT rowid, code, label, url, icon, active FROM " . MAIN_DB_PREFIX . "c_socialnetworks";
4488 $sql .= " WHERE entity IN (" . getEntity('c_socialnetworks').")";
4489
4490 $resql = $db->query($sql);
4491 if ($resql) {
4492 while ($obj = $db->fetch_object($resql)) {
4493 $socialnetworks[$obj->code] = array(
4494 'rowid' => $obj->rowid,
4495 'label' => $obj->label,
4496 'url' => $obj->url,
4497 'icon' => $obj->icon,
4498 'active' => $obj->active,
4499 );
4500 }
4501 }
4502 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4503 }
4504
4505 return (is_array($socialnetworks) ? $socialnetworks : array());
4506}
4507
4518function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4519{
4520 global $hookmanager, $langs, $user;
4521
4522 $htmllink = $value;
4523
4524 if (empty($value)) {
4525 return '&nbsp;';
4526 }
4527
4528 if (!empty($type)) {
4529 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4530 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4531 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4532 if ($type == 'skype') {
4533 $htmllink .= dol_escape_htmltag($value);
4534 $htmllink .= '&nbsp; <a href="skype:';
4535 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4536 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4537 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4538 $htmllink .= '</a><a href="skype:';
4539 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4540 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4541 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4542 $htmllink .= '</a>';
4543 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4544 $addlink = 'AC_SKYPE';
4545 $link = '';
4546 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4547 $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>';
4548 }
4549 $htmllink .= ($link ? ' ' . $link : '');
4550 }
4551 } else {
4552 if (!empty($dictsocialnetworks[$type]['url'])) {
4553 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4554 if ($tmpvirginurl) {
4555 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4556 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4557
4558 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4559 if ($tmpvirginurl3) {
4560 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4561 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4562 }
4563
4564 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4565 if ($tmpvirginurl2) {
4566 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4567 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4568 }
4569 }
4570 if (preg_match('/^https?:\/\//i', $value)) {
4571 $link = $value;
4572 } else {
4573 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4574 }
4575 $valuetoshow = $value;
4576 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4577 if (preg_match('/^https?:\/\//i', $link)) {
4578 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4579 } else {
4580 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4581 }
4582 } else {
4583 $htmllink .= dol_escape_htmltag($value);
4584 }
4585 }
4586 $htmllink .= '</div>';
4587 } else {
4588 $langs->load("errors");
4589 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4590 }
4591
4592 if ($hookmanager) {
4593 $parameters = array(
4594 'value' => $value,
4595 'cid' => $contactid,
4596 'socid' => $socid,
4597 'type' => $type,
4598 'dictsocialnetworks' => $dictsocialnetworks,
4599 );
4600
4601 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4602 if ($reshook > 0) {
4603 $htmllink = '';
4604 }
4605 $htmllink .= $hookmanager->resPrint;
4606 }
4607
4608 return $htmllink;
4609}
4610
4620function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4621{
4622 global $mysoc;
4623
4624 if (empty($profID) || empty($profIDtype)) {
4625 return '';
4626 }
4627 if (empty($countrycode)) {
4628 $countrycode = $mysoc->country_code;
4629 }
4630 $newProfID = $profID;
4631 $id = substr($profIDtype, -1);
4632 $ret = '';
4633 if (strtoupper($countrycode) == 'FR') {
4634 // France
4635 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4636
4637 if ($id == 1 && dol_strlen($newProfID) == 9) {
4638 // SIREN (ex: 123 123 123)
4639 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3);
4640 }
4641 if ($id == 2 && dol_strlen($newProfID) == 14) {
4642 // SIRET (ex: 123 123 123 12345)
4643 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3) . ' ' . substr($newProfID, 9, 5);
4644 }
4645 if ($id == 3 && dol_strlen($newProfID) == 5) {
4646 // NAF/APE (ex: 69.20Z)
4647 $newProfID = substr($newProfID, 0, 2) . '.' . substr($newProfID, 2, 3);
4648 }
4649 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4650 // TVA intracommunautaire (ex: FR12 123 123 123)
4651 $newProfID = substr($newProfID, 0, 4) . ' ' . substr($newProfID, 4, 3) . ' ' . substr($newProfID, 7, 3) . ' ' . substr($newProfID, 10, 3);
4652 }
4653 }
4654 if (!empty($addcpButton)) {
4655 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4656 } else {
4657 $ret = $newProfID;
4658 }
4659 return $ret;
4660}
4661
4677function dol_print_phone($phone, $countrycode = '', $contactid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4678{
4679 global $conf, $user, $langs, $mysoc, $hookmanager;
4680
4681 // Clean phone parameter
4682 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4683 if (empty($phone)) {
4684 return '';
4685 }
4686 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4687 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4688 }
4689 if (empty($countrycode) && is_object($mysoc)) {
4690 $countrycode = $mysoc->country_code;
4691 }
4692
4693 // Short format for small screens
4694 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4695 $separ = '';
4696 }
4697
4698 $newphone = $phone;
4699 $newphonewa = $phone;
4700 if (strtoupper($countrycode) == "FR") {
4701 // France
4702 if (dol_strlen($phone) == 10) {
4703 $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);
4704 } elseif (dol_strlen($phone) == 7) {
4705 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2);
4706 } elseif (dol_strlen($phone) == 9) {
4707 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2);
4708 } elseif (dol_strlen($phone) == 11) {
4709 $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);
4710 } elseif (dol_strlen($phone) == 12) {
4711 $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);
4712 } elseif (dol_strlen($phone) == 13) {
4713 $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);
4714 }
4715 } elseif (strtoupper($countrycode) == "CA") {
4716 if (dol_strlen($phone) == 10) {
4717 $newphone = ($separ != '' ? '(' : '') . substr($newphone, 0, 3) . ($separ != '' ? ')' : '') . $separ . substr($newphone, 3, 3) . ($separ != '' ? '-' : '') . substr($newphone, 6, 4);
4718 }
4719 } elseif (strtoupper($countrycode) == "PT") { //Portugal
4720 if (dol_strlen($phone) == 13) { //ex: +351_ABC_DEF_GHI
4721 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4722 }
4723 } elseif (strtoupper($countrycode) == "SR") { //Suriname
4724 if (dol_strlen($phone) == 10) { //ex: +597_ABC_DEF
4725 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3);
4726 } elseif (dol_strlen($phone) == 11) { //ex: +597_ABC_DEFG
4727 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
4728 }
4729 } elseif (strtoupper($countrycode) == "DE") { //Allemagne
4730 if (dol_strlen($phone) == 14) { //ex: +49_ABCD_EFGH_IJK
4731 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 4) . $separ . substr($newphone, 11, 3);
4732 } elseif (dol_strlen($phone) == 13) { //ex: +49_ABC_DEFG_HIJ
4733 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 4) . $separ . substr($newphone, 10, 3);
4734 }
4735 } elseif (strtoupper($countrycode) == "ES") { //Espagne
4736 if (dol_strlen($phone) == 12) { //ex: +34_ABC_DEF_GHI
4737 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4738 }
4739 } elseif (strtoupper($countrycode) == "BF") { // Burkina Faso
4740 if (dol_strlen($phone) == 12) { //ex : +22 A BC_DE_FG_HI
4741 $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);
4742 }
4743 } elseif (strtoupper($countrycode) == "RO") { // Roumanie
4744 if (dol_strlen($phone) == 12) { //ex : +40 AB_CDE_FG_HI
4745 $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);
4746 }
4747 } elseif (strtoupper($countrycode) == "TR") { //Turquie
4748 if (dol_strlen($phone) == 13) { //ex : +90 ABC_DEF_GHIJ
4749 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4750 }
4751 } elseif (strtoupper($countrycode) == "US") { //Etat-Unis
4752 if (dol_strlen($phone) == 12) { //ex: +1 ABC_DEF_GHIJ
4753 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4754 }
4755 } elseif (strtoupper($countrycode) == "MX") { //Mexique
4756 if (dol_strlen($phone) == 12) { //ex: +52 ABCD_EFG_HI
4757 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4758 } elseif (dol_strlen($phone) == 11) { //ex: +52 AB_CD_EF_GH
4759 $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);
4760 } elseif (dol_strlen($phone) == 13) { //ex: +52 ABC_DEF_GHIJ
4761 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4762 }
4763 } elseif (strtoupper($countrycode) == "ML") { //Mali
4764 if (dol_strlen($phone) == 12) { //ex: +223 AB_CD_EF_GH
4765 $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);
4766 }
4767 } elseif (strtoupper($countrycode) == "TH") { //Thaïlande
4768 if (dol_strlen($phone) == 11) { //ex: +66_ABC_DE_FGH
4769 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4770 } elseif (dol_strlen($phone) == 12) { //ex: +66_A_BCD_EF_GHI
4771 $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);
4772 }
4773 } elseif (strtoupper($countrycode) == "MU") {
4774 //Maurice
4775 if (dol_strlen($phone) == 11) { //ex: +230_ABC_DE_FG
4776 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4777 } elseif (dol_strlen($phone) == 12) { //ex: +230_ABCD_EF_GH
4778 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4779 }
4780 } elseif (strtoupper($countrycode) == "ZA") { //Afrique du sud
4781 if (dol_strlen($phone) == 12) { //ex: +27_AB_CDE_FG_HI
4782 $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);
4783 }
4784 } elseif (strtoupper($countrycode) == "SY") { //Syrie
4785 if (dol_strlen($phone) == 12) { //ex: +963_AB_CD_EF_GH
4786 $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);
4787 } elseif (dol_strlen($phone) == 13) { //ex: +963_AB_CD_EF_GHI
4788 $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);
4789 }
4790 } elseif (strtoupper($countrycode) == "AE") { //Emirats Arabes Unis
4791 if (dol_strlen($phone) == 12) { //ex: +971_ABC_DEF_GH
4792 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4793 } elseif (dol_strlen($phone) == 13) { //ex: +971_ABC_DEF_GHI
4794 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4795 } elseif (dol_strlen($phone) == 14) { //ex: +971_ABC_DEF_GHIK
4796 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 4);
4797 }
4798 } elseif (strtoupper($countrycode) == "DZ") { //Algérie
4799 if (dol_strlen($phone) == 13) { //ex: +213_ABC_DEF_GHI
4800 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4801 }
4802 } elseif (strtoupper($countrycode) == "BE") { //Belgique
4803 if (dol_strlen($phone) == 11) { //ex: +32_ABC_DE_FGH
4804 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4805 } elseif (dol_strlen($phone) == 12) { //ex: +32_ABC_DEF_GHI
4806 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4807 }
4808 } elseif (strtoupper($countrycode) == "PF") { //Polynésie française
4809 if (dol_strlen($phone) == 12) { //ex: +689_AB_CD_EF_GH
4810 $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);
4811 }
4812 } elseif (strtoupper($countrycode) == "CO") { //Colombie
4813 if (dol_strlen($phone) == 13) { //ex: +57_ABC_DEF_GH_IJ
4814 $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);
4815 }
4816 } elseif (strtoupper($countrycode) == "JO") { //Jordanie
4817 if (dol_strlen($phone) == 12) { //ex: +962_A_BCD_EF_GH
4818 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 1) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4819 }
4820 } elseif (strtoupper($countrycode) == "JM") { //Jamaïque
4821 if (dol_strlen($newphone) == 12) { //ex: +1867_ABC_DEFG
4822 $newphone = substr($newphone, 0, 5) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4823 }
4824 } elseif (strtoupper($countrycode) == "MG") { //Madagascar
4825 if (dol_strlen($phone) == 13) { //ex: +261_AB_CD_EFG_HI
4826 $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);
4827 }
4828 } elseif (strtoupper($countrycode) == "GB") { //Royaume uni
4829 if (dol_strlen($phone) == 13) { //ex: +44_ABCD_EFG_HIJ
4830 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4831 }
4832 } elseif (strtoupper($countrycode) == "CH") { //Suisse
4833 if (dol_strlen($phone) == 12) { //ex: +41_AB_CDE_FG_HI
4834 $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);
4835 } elseif (dol_strlen($phone) == 15) { // +41_AB_CDE_FGH_IJKL
4836 $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);
4837 }
4838 } elseif (strtoupper($countrycode) == "TN") { //Tunisie
4839 if (dol_strlen($phone) == 12) { //ex: +216_AB_CDE_FGH
4840 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4841 }
4842 } elseif (strtoupper($countrycode) == "GF") { //Guyane francaise
4843 if (dol_strlen($phone) == 13) { //ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4844 $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);
4845 }
4846 } elseif (strtoupper($countrycode) == "GP") { //Guadeloupe
4847 if (dol_strlen($phone) == 13) { //ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4848 $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);
4849 }
4850 } elseif (strtoupper($countrycode) == "MQ") { //Martinique
4851 if (dol_strlen($phone) == 13) { //ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4852 $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);
4853 }
4854 } elseif (strtoupper($countrycode) == "IT") { //Italie
4855 if (dol_strlen($phone) == 12) { //ex: +39_ABC_DEF_GHI
4856 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4857 } elseif (dol_strlen($phone) == 13) { //ex: +39_ABC_DEF_GH_IJ
4858 $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);
4859 }
4860 } elseif (strtoupper($countrycode) == "AU") {
4861 //Australie
4862 if (dol_strlen($phone) == 12) {
4863 //ex: +61_A_BCDE_FGHI
4864 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 4);
4865 }
4866 } elseif (strtoupper($countrycode) == "LU") {
4867 // Luxembourg
4868 if (dol_strlen($phone) == 10) { // fix 6 digits +352_AA_BB_CC
4869 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2);
4870 } elseif (dol_strlen($phone) == 11) { // fix 7 digits +352_AA_BB_CC_D
4871 $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);
4872 } elseif (dol_strlen($phone) == 12) { // fix 8 digits +352_AA_BB_CC_DD
4873 $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);
4874 } elseif (dol_strlen($phone) == 13) { // mobile +352_AAA_BB_CC_DD
4875 $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);
4876 }
4877 } elseif (strtoupper($countrycode) == "PE") {
4878 // Peru
4879 if (dol_strlen($phone) == 7) { // fix 7 chiffres without code AAA_BBBB
4880 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4);
4881 } elseif (dol_strlen($phone) == 9) { // mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4882 $newphonewa = '+51' . $newphone;
4883 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3);
4884 } elseif (dol_strlen($phone) == 11) { // fix 11 chiffres +511_AAA_BBBB
4885 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
4886 } elseif (dol_strlen($phone) == 12) { // mobile +51_AAA_BBB_CCC
4887 $newphonewa = $newphone;
4888 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4889 }
4890 } elseif (strtoupper($countrycode) == "IN") { //India
4891 if (dol_strlen($phone) == 13) {
4892 if ($withpicto == 'phone') { //ex: +91_AB_CDEF_GHIJ
4893 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 4) . $separ . substr($newphone, 9, 4);
4894 } else { //ex: +91_ABCDE_FGHIJ
4895 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 5) . $separ . substr($newphone, 8, 5);
4896 }
4897 }
4898 }
4899
4900 $newphoneastart = $newphoneaend = '';
4901 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4902 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
4903 $newphoneastart = '<a href="tel:' . urlencode($phone) . '">';
4904 $newphoneaend .= '</a>';
4905 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4906 if (empty($user->clicktodial_loaded)) {
4907 $user->fetch_clicktodial();
4908 }
4909
4910 // Define urlmask
4911 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4912 if (!empty($user->clicktodial_url)) {
4913 $urlmask = $user->clicktodial_url;
4914 }
4915
4916 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4917 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4918 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4919 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4920 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4921 // Those lines are for substitution
4922 $substitarray = array(
4923 '__PHONEFROM__' => $clicktodial_poste,
4924 '__PHONETO__' => urlencode($phone),
4925 '__LOGIN__' => $clicktodial_login,
4926 '__PASS__' => $clicktodial_password
4927 );
4928 $url = make_substitutions($url, $substitarray);
4929 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4930 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4931 $newphoneastart = '<a href="' . $url . '" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4932 $newphoneaend = '</a>';
4933 } else {
4934 // Old method
4935 $newphoneastart = '<a href="' . $url . '"';
4936 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4937 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4938 }
4939 $newphoneastart .= '>';
4940 $newphoneaend .= '</a>';
4941 }
4942 }
4943
4944 //if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4945 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4946 $type = 'AC_TEL';
4947 $addlinktoagenda = '';
4948 if ($addlink == 'AC_FAX') {
4949 $type = 'AC_FAX';
4950 }
4951 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4952 $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>';
4953 }
4954 if ($addlinktoagenda) {
4955 $newphone = '<span>' . $newphone . ' ' . $addlinktoagenda . '</span>';
4956 }
4957 }
4958 }
4959
4960 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4961 // Link to Whatsapp
4962 $newphone .= ' <a href="https://wa.me/' . $newphonewa . '" target="_blank"'; // Use api to whatasapp contacts
4963 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4964 }
4965
4966 if (empty($titlealt)) {
4967 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4968 }
4969 $rep = '';
4970
4971 if ($hookmanager) {
4972 $parameters = array('countrycode' => $countrycode, 'cid' => $contactid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4973 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4974 $rep .= $hookmanager->resPrint;
4975 }
4976 if (empty($reshook)) {
4977 $picto = '';
4978 if ($withpicto) {
4979 if ($withpicto == 'fax') {
4980 $picto = 'phoning_fax';
4981 } elseif ($withpicto == 'phone') {
4982 $picto = 'phone';
4983 } elseif ($withpicto == 'mobile') {
4984 $picto = 'phoning_mobile';
4985 } else {
4986 $picto = '';
4987 }
4988 }
4989 if ($adddivfloat == 1) {
4990 $rep .= '<div class="nospan float' . ($morecss ? ' ' . $morecss : '') . '">';
4991 } elseif (empty($adddivfloat)) {
4992 $rep .= '<span' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
4993 }
4994
4995 $rep .= $newphoneastart;
4996 $rep .= ($withpicto ? img_picto($titlealt, $picto) : '');
4997 if ($separ != 'hidenum') {
4998 $rep .= ($withpicto ? ' ' : '') . $newphone;
4999 }
5000 $rep .= $newphoneaend;
5001
5002 if ($adddivfloat == 1) {
5003 $rep .= '</div>';
5004 } elseif (empty($adddivfloat)) {
5005 $rep .= '</span>';
5006 }
5007 }
5008
5009 return $rep;
5010}
5011
5020function dol_print_ip($ip, $mode = 0, $showname = 0)
5021{
5022 global $conf;
5023
5024 $ret = '';
5025 if (!isset($conf->cache['resolveips'])) {
5026 $conf->cache['resolveips'] = array();
5027 }
5028
5029 if ($mode != 2) {
5030 $countrycode = dolGetCountryCodeFromIp($ip);
5031 if ($countrycode) { // If success, countrycode is us, fr, ...
5032 if (file_exists(DOL_DOCUMENT_ROOT . '/theme/common/flags/' . $countrycode . '.png')) {
5033 $ret .= picto_from_langcode($countrycode);
5034 } else {
5035 $ret .= '(' . $countrycode . ')';
5036 }
5037 $ret .= '&nbsp;';
5038 } else {
5039 // Nothing
5040 }
5041 }
5042
5043 if (in_array($mode, [0, 2])) {
5044 $domain = '';
5045 if ($showname) {
5046 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
5047 $domain = gethostbyaddr($ip);
5048 $conf->cache['resolveips'][$ip] = $domain; // false or domain
5049 } else {
5050 $domain = $conf->cache['resolveips'][$ip];
5051 }
5052 }
5053 if ($domain) {
5054 $ret .= $domain;
5055 } else {
5056 $ret .= $ip;
5057 }
5058 }
5059
5060 return $ret;
5061}
5062
5075function getUserRemoteIP($trusted = 0)
5076{
5077 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
5078 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
5079 // 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)
5080 // This can happen if the proxy were added in the list of trusted proxy.
5081 return $ip;
5082 }
5083
5084 // Try to guess the real IP of client (but this may not be reliable)
5085 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
5086 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
5087 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
5088 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
5089 } else {
5090 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
5091 }
5092 } else {
5093 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
5094 }
5095 } else {
5096 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
5097 }
5098 return $ip;
5099}
5100
5109function isHTTPS()
5110{
5111 $isSecure = false;
5112 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
5113 $isSecure = true;
5114 } 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') {
5115 $isSecure = true;
5116 }
5117 return $isSecure;
5118}
5119
5126function dolGetCountryCodeFromIp($ip)
5127{
5128 $countrycode = '';
5129
5130 if (isModEnabled('geoipmaxmind')) {
5131 if (getDolGlobalString('GEOIP_VERSION') == 'php') {
5132 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5133 } else {
5134 $diroffile = getMultidirOutput(null, 'geoipmaxmind');
5135 $datafile = $diroffile . '/' . getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE_EMBEDDED');
5136 }
5137 //$ip='24.24.24.24';
5138 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
5139 if ($datafile) {
5140 try {
5141 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5142 $geoip = new DolGeoIP('country', $datafile);
5143 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
5144 $countrycode = $geoip->getCountryCodeFromIP($ip);
5145 } catch (Exception $e) {
5146 //print 'Error with GeoIP database: '.$e->getMessage();
5147 }
5148 }
5149 }
5150
5151 return $countrycode;
5152}
5153
5154
5161function dol_user_country()
5162{
5163 global $conf, $langs, $user;
5164
5165 //$ret=$user->xxx;
5166 $ret = '';
5167 if (isModEnabled('geoipmaxmind')) {
5168 $ip = getUserRemoteIP();
5169 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5170 //$ip='24.24.24.24';
5171 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
5172 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5173 $geoip = new DolGeoIP('country', $datafile);
5174 $countrycode = $geoip->getCountryCodeFromIP($ip);
5175 $ret = $countrycode;
5176 }
5177 return $ret;
5178}
5179
5192function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
5193{
5194 global $hookmanager;
5195
5196 $out = '';
5197
5198 if ($address) {
5199 if ($hookmanager) {
5200 $parameters = array('element' => $element, 'id' => $id);
5201 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
5202 $out .= $hookmanager->resPrint;
5203 }
5204 if (empty($reshook)) {
5205 if (empty($charfornl)) {
5206 $out .= nl2br((string) $address);
5207 } else {
5208 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
5209 }
5210
5211 // TODO Remove this block, we can add this using the hook now
5212 $showgmap = $showomap = 0;
5213 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
5214 $showgmap = 1;
5215 }
5216 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
5217 $showgmap = 1;
5218 }
5219 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
5220 $showgmap = 1;
5221 }
5222 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
5223 $showgmap = 1;
5224 }
5225 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
5226 $showomap = 1;
5227 }
5228 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
5229 $showomap = 1;
5230 }
5231 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
5232 $showomap = 1;
5233 }
5234 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
5235 $showomap = 1;
5236 }
5237 if ($showgmap) {
5238 $url = dol_buildpath('/google/gmaps.php?mode=' . $element . '&id=' . $id, 1);
5239 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5240 }
5241 if ($showomap) {
5242 $url = dol_buildpath('/openstreetmap/maps.php?mode=' . $element . '&id=' . $id, 1);
5243 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '_openstreetmap" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5244 }
5245 }
5246 }
5247 if ($noprint) {
5248 return $out;
5249 } else {
5250 print $out;
5251 return null;
5252 }
5253}
5254
5255
5265function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5266{
5267 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5268 return true;
5269 }
5270 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5271 return true;
5272 }
5273 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5274 return true;
5275 }
5276
5277 return false;
5278}
5279
5289function isValidMXRecord($domain)
5290{
5291 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5292 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5293 return 0;
5294 }
5295 if (function_exists('getmxrr')) {
5296 $mxhosts = array();
5297 $weight = array();
5298 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5299 if (count($mxhosts) > 1) {
5300 return 1;
5301 }
5302 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5303 return 1;
5304 }
5305
5306 return 0;
5307 }
5308 }
5309
5310 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5311 return -1;
5312}
5313
5321function isValidPhone($phone)
5322{
5323 return true;
5324}
5325
5326
5336function dolGetFirstLetters($s, $nbofchar = 1)
5337{
5338 $ret = '';
5339 $tmparray = explode(' ', $s);
5340 foreach ($tmparray as $tmps) {
5341 $ret .= dol_substr($tmps, 0, $nbofchar);
5342 }
5343
5344 return $ret;
5345}
5346
5347
5355function dol_strlen($string, $stringencoding = 'UTF-8')
5356{
5357 if (is_null($string)) {
5358 return 0;
5359 }
5360
5361 if (function_exists('mb_strlen')) {
5362 return mb_strlen($string, $stringencoding);
5363 } else {
5364 return strlen($string);
5365 }
5366}
5367
5378function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5379{
5380 global $langs;
5381
5382 if (empty($stringencoding)) {
5383 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5384 }
5385
5386 $ret = '';
5387 if (empty($trunconbytes)) {
5388 if (function_exists('mb_substr')) {
5389 $ret = mb_substr($string, $start, $length, $stringencoding);
5390 } else {
5391 $ret = substr($string, $start, $length);
5392 }
5393 } else {
5394 if (function_exists('mb_strcut')) {
5395 $ret = mb_strcut($string, $start, $length, $stringencoding);
5396 } else {
5397 $ret = substr($string, $start, $length);
5398 }
5399 }
5400 return $ret;
5401}
5402
5403
5417function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5418{
5419 global $conf;
5420
5421 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5422 return $string;
5423 }
5424
5425 if (empty($stringencoding)) {
5426 $stringencoding = 'UTF-8';
5427 }
5428 // reduce for small screen
5429 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5430 $size = round($size / 3);
5431 }
5432
5433 // We go always here
5434 if ($trunc == 'right') {
5435 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5436 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5437 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5438 return dol_substr($newstring, 0, $size, $stringencoding) . ($nodot ? '' : '…');
5439 } else {
5440 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5441 return $string;
5442 }
5443 } elseif ($trunc == 'middle') {
5444 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5445 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5446 $size1 = (int) round($size / 2);
5447 $size2 = (int) round($size / 2);
5448 return dol_substr($newstring, 0, $size1, $stringencoding) . '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5449 } else {
5450 return $string;
5451 }
5452 } elseif ($trunc == 'left') {
5453 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5454 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5455 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5456 return '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5457 } else {
5458 return $string;
5459 }
5460 } elseif ($trunc == 'wrap') {
5461 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5462 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5463 return dol_substr($newstring, 0, $size, $stringencoding) . "\n" . dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5464 } else {
5465 return $string;
5466 }
5467 } else {
5468 return 'BadParam3CallingDolTrunc';
5469 }
5470}
5471
5479function getPictoForType($key, $morecss = '')
5480{
5481 // Set array with type -> picto
5482 $type2picto = array(
5483 'varchar' => 'font',
5484 'text' => 'font',
5485 'html' => 'code',
5486 'int' => 'sort-numeric-down',
5487 'double' => 'sort-numeric-down',
5488 'price' => 'currency',
5489 'pricecy' => 'multicurrency',
5490 'password' => 'key',
5491 'boolean' => 'check-square',
5492 'date' => 'calendar',
5493 'datetime' => 'calendar',
5494 'duration' => 'hourglass',
5495 'phone' => 'phone',
5496 'mail' => 'email',
5497 'url' => 'url',
5498 'ip' => 'country',
5499 'select' => 'list',
5500 'sellist' => 'list',
5501 'stars' => 'fontawesome_star_fas',
5502 'radio' => 'check-circle',
5503 'checkbox' => 'list',
5504 'chkbxlst' => 'list',
5505 'link' => 'link',
5506 'icon' => "question",
5507 'point' => "country",
5508 'multipts' => 'country',
5509 'linestrg' => "country",
5510 'polygon' => "country",
5511 'separate' => 'minus'
5512 );
5513
5514 if (!empty($type2picto[$key])) {
5515 return img_picto('', $type2picto[$key], 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5516 }
5517
5518 return img_picto('', 'generic', 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5519}
5520
5521
5545function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5546{
5547 global $conf;
5548
5549 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5550 $url = DOL_URL_ROOT;
5551 $theme = isset($conf->theme) ? $conf->theme : null;
5552 $path = 'theme/' . $theme;
5553 if (empty($picto)) {
5554 $picto = 'generic';
5555 }
5556
5557 // Define fullpathpicto to use into src
5558 if ($pictoisfullpath) {
5559 // Clean parameters
5560 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5561 $picto .= '.png';
5562 }
5563 $fullpathpicto = $picto;
5564 $reg = array();
5565 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5566 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5567 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5568 }
5569 } else {
5570 // $picto can not be null since replaced with 'generic' in that case
5571 // $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5572 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5573 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5574 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5575
5576 // Fix some values of $pictowithouttext
5577 $pictoconvertkey = array(
5578 'facture' => 'bill',
5579 'shipping' => 'shipment',
5580 'fichinter' => 'intervention',
5581 'agenda' => 'calendar',
5582 'invoice_supplier' => 'supplier_invoice',
5583 'order_supplier' => 'supplier_order');
5584 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5585 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5586 }
5587
5588 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5589 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5590 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5591 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5592
5593 // Compatibility with old fontawesome versions
5594 if ($pictowithouttext == 'file-o') {
5595 $pictowithouttext = 'file';
5596 }
5597
5598 $pictowithouttextarray = explode('_', $pictowithouttext);
5599 $marginleftonlyshort = 0;
5600
5601 if (!empty($pictowithouttextarray[1])) {
5602 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5603 $fakey = 'fa-' . $pictowithouttextarray[0];
5604 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5605 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5606 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5607 } else {
5608 $fakey = 'fa-' . $pictowithouttext;
5609 $faprefix = 'fas';
5610 $facolor = '';
5611 $fasize = '';
5612 }
5613
5614 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5615 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5616 $morestyle = '';
5617 $reg = array();
5618 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5619 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5620 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5621 }
5622 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5623 $morestyle = $reg[1];
5624 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5625 }
5626 $moreatt = trim($moreatt);
5627
5628 $enabledisablehtml = '<span class="' . $faprefix . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5629 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5630 $enabledisablehtml .= '</span>';
5631
5632 return $enabledisablehtml;
5633 }
5634
5635 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
5636 $fakey = $pictowithouttext;
5637 $facolor = '';
5638 $fasize = '';
5639 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5640 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'))) {
5641 $fa = 'far';
5642 }
5643 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'))) {
5644 $fa = 'fab';
5645 }
5646
5647 $arrayconvpictotofa = getImgPictoConv('fa');
5648
5649 if ($pictowithouttext == 'off') {
5650 $fakey = 'fa-square';
5651 $fasize = '1.3em';
5652 } elseif ($pictowithouttext == 'on') {
5653 $fakey = 'fa-check-square';
5654 $fasize = '1.3em';
5655 } elseif ($pictowithouttext == 'listlight') {
5656 $fakey = 'fa-download';
5657 $marginleftonlyshort = 1;
5658 } elseif ($pictowithouttext == 'printer') {
5659 $fakey = 'fa-print';
5660 $fasize = '1.2em';
5661 } elseif ($pictowithouttext == 'note') {
5662 $fakey = 'fa-sticky-note';
5663 $marginleftonlyshort = 1;
5664 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5665 $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');
5666 $fakey = 'fa-' . $convertarray[$pictowithouttext];
5667 if (preg_match('/selected/', $pictowithouttext)) {
5668 $facolor = '#888';
5669 }
5670 $marginleftonlyshort = 1;
5671 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5672 $fakey = 'fa-' . $arrayconvpictotofa[$pictowithouttext];
5673 } else {
5674 $fakey = 'fa-' . $pictowithouttext;
5675 }
5676
5677 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5678 $morecss .= ' em092';
5679 }
5680 if (in_array($pictowithouttext, array('conferenceorbooth', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5681 $morecss .= ' em088';
5682 }
5683 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5684 $morecss .= ' em080';
5685 }
5686
5687 // Define $marginleftonlyshort
5688 $arrayconvpictotomarginleftonly = array(
5689 'bank',
5690 'check',
5691 'delete',
5692 'generic',
5693 'grip',
5694 'grip_title',
5695 'jabber',
5696 'grip_title',
5697 'grip',
5698 'listlight',
5699 'note',
5700 'on',
5701 'off',
5702 'playdisabled',
5703 'printer',
5704 'resize',
5705 'sign-out',
5706 'stats',
5707 'switch_on',
5708 'switch_on_grey',
5709 'switch_on_red',
5710 'switch_off',
5711 'switch_off_grey',
5712 'switch_off_red',
5713 'uparrow',
5714 '1uparrow',
5715 '1downarrow',
5716 '1leftarrow',
5717 '1rightarrow',
5718 '1uparrow_selected',
5719 '1downarrow_selected',
5720 '1leftarrow_selected',
5721 '1rightarrow_selected'
5722 );
5723 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5724 $marginleftonlyshort = 0;
5725 }
5726
5727 // Add CSS
5728 $arrayconvpictotomorcess = array(
5729 'action' => 'infobox-action',
5730 'account' => 'infobox-bank_account',
5731 'accounting_account' => 'infobox-bank_account',
5732 'accountline' => 'infobox-bank_account',
5733 'accountancy' => 'infobox-bank_account',
5734 'admin' => 'opacitymedium',
5735 'asset' => 'infobox-bank_account',
5736 'bank_account' => 'infobox-bank_account',
5737 'bill' => 'infobox-commande',
5738 'billa' => 'infobox-commande',
5739 'billr' => 'infobox-commande',
5740 'billd' => 'infobox-commande',
5741 'bookcal' => 'infobox-portal',
5742 'margin' => 'infobox-bank_account',
5743 'conferenceorbooth' => 'infobox-project',
5744 'cash-register' => 'infobox-portal',
5745 'contract' => 'infobox-contrat',
5746 'check' => 'font-status4',
5747 'conversation' => 'infobox-contrat',
5748 'donation' => 'infobox-commande',
5749 'dolly' => 'infobox-commande',
5750 'dollyrevert' => 'flip infobox-order_supplier',
5751 'ecm' => 'infobox-action',
5752 'eventorganization' => 'infobox-project',
5753 'hrm' => 'infobox-adherent',
5754 'group' => 'infobox-adherent',
5755 'intervention' => 'infobox-contrat',
5756 'incoterm' => 'infobox-supplier_proposal',
5757 'intracommreport' => 'infobox-bank_account',
5758 'currency' => 'infobox-bank_account',
5759 'multicurrency' => 'infobox-bank_account',
5760 'members' => 'infobox-adherent',
5761 'member' => 'infobox-adherent',
5762 'money-bill-alt' => 'infobox-bank_account',
5763 'order' => 'infobox-commande',
5764 'user' => 'infobox-adherent',
5765 'users' => 'infobox-adherent',
5766 'error' => 'pictoerror',
5767 'warning' => 'pictowarning',
5768 'switch_on' => 'font-status4',
5769 'switch_on_warning' => 'font-status4 warning',
5770 'switch_on_red' => 'font-status8',
5771 'switch_off_warning' => 'font-status4 warning',
5772 'switch_off_red' => 'font-status8',
5773 'holiday' => 'infobox-holiday',
5774 'info' => 'opacityhigh',
5775 'info_black' => 'purple',
5776 'invoice' => 'infobox-commande',
5777 'knowledgemanagement' => 'infobox-contrat rotate90',
5778 'loan' => 'infobox-commande',
5779 'payment' => 'infobox-bank_account',
5780 'payment_vat' => 'infobox-bank_account',
5781 'poll' => 'infobox-portal',
5782 'pos' => 'infobox-bank_account',
5783 'project' => 'infobox-project',
5784 'projecttask' => 'infobox-project',
5785 'propal' => 'infobox-propal',
5786 'proposal' => 'infobox-propal',
5787 'private' => 'infobox-project',
5788 'reception' => 'flip infobox-order_supplier',
5789 'recruitmentjobposition' => 'infobox-adherent',
5790 'recruitmentcandidature' => 'infobox-adherent',
5791 'resource' => 'infobox-action',
5792 'salary' => 'infobox-commande',
5793 'shapes' => 'infobox-adherent',
5794 'shipment' => 'infobox-commande',
5795 'store' => 'infobox-portal',
5796 'stripe' => 'infobox-bank_account',
5797 'supplier_invoice' => 'infobox-order_supplier',
5798 'supplier_invoicea' => 'infobox-order_supplier',
5799 'supplier_invoiced' => 'infobox-order_supplier',
5800 'supplier' => 'infobox-order_supplier',
5801 'supplier_order' => 'infobox-order_supplier',
5802 'supplier_proposal' => 'infobox-supplier_proposal',
5803 'ticket' => 'infobox-contrat',
5804 'title_accountancy' => 'infobox-bank_account',
5805 'title_hrm' => 'infobox-holiday',
5806 'expensereport' => 'infobox-expensereport',
5807 'trip' => 'infobox-expensereport',
5808 'title_agenda' => 'infobox-action',
5809 'vat' => 'infobox-bank_account',
5810 'webportal' => 'infobox-portal',
5811 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5812 'list-alt' => 'imgforviewmode',
5813 'calendar' => 'imgforviewmode',
5814 'calendarweek' => 'imgforviewmode',
5815 'calendarmonth' => 'imgforviewmode',
5816 'calendarday' => 'imgforviewmode',
5817 'calendarperuser' => 'imgforviewmode',
5818 'calendarpertype' => 'imgforviewmode'
5819 );
5820 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5821 $morecss .= ($morecss ? ' ' : '') . $arrayconvpictotomorcess[$pictowithouttext];
5822 }
5823
5824 // Define $color
5825 $arrayconvpictotocolor = array(
5826 'address' => '#6c6aa8',
5827 'building' => '#6c6aa8',
5828 'bom' => '#a69944',
5829 'clone' => '#999',
5830 'cog' => '#999',
5831 'companies' => '#6c6aa8',
5832 'company' => '#6c6aa8',
5833 'contact' => '#6c6aa8',
5834 'cron' => '#555',
5835 'dynamicprice' => '#a69944',
5836 'edit' => '#444',
5837 'note' => '#999',
5838 'error' => '',
5839 'help' => '#bbb',
5840 'listlight' => '#999',
5841 'language' => '#555',
5842 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5843 'lock' => '#ddd',
5844 'lot' => '#a69944',
5845 'map-marker-alt' => '#aaa',
5846 'mrp' => '#a69944',
5847 'product' => '#a69944',
5848 'service' => '#a69944',
5849 'inventory' => '#a69944',
5850 'stock' => '#a69944',
5851 'movement' => '#a69944',
5852 'other' => '#ddd',
5853 'world' => '#986c6a',
5854 'partnership' => '#6c6aa8',
5855 'playdisabled' => '#ccc',
5856 'printer' => '#444',
5857 'projectpub' => '#986c6a',
5858 'resize' => '#444',
5859 'rss' => '#cba',
5860 //'shipment'=>'#a69944',
5861 'search-plus' => '#808080',
5862 'security' => '#999',
5863 'square' => '#888',
5864 'stop-circle' => '#888',
5865 'stats' => '#444',
5866 'superadmin' => '#600',
5867 'switch_off' => '#999',
5868 'technic' => '#999',
5869 'tick' => '#282',
5870 'timespent' => '#555',
5871 'uncheck' => '#800',
5872 'uparrow' => '#555',
5873 'user-cog' => '#999',
5874 'country' => '#aaa',
5875 'globe-americas' => '#aaa',
5876 'region' => '#aaa',
5877 'state' => '#aaa',
5878 'website' => '#304',
5879 'workstation' => '#a69944'
5880 );
5881 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5882 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5883 }
5884
5885 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5886 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5887 $morestyle = '';
5888 $reg = array();
5889 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5890 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5891 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5892 }
5893 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5894 $morestyle = $reg[1];
5895 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5896 }
5897 $moreatt = trim($moreatt);
5898
5899 $enabledisablehtml = '<span class="' . $fa . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5900 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5901 $enabledisablehtml .= '</span>';
5902
5903 return $enabledisablehtml;
5904 }
5905
5906 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5907 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/' . $theme; // If the theme does not have the same name as the module
5908 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5909 $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
5910 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5911 $path = $theme . '/theme/' . $theme; // If the theme have the same name as the module
5912 }
5913
5914 // If we ask an image into $url/$mymodule/img (instead of default path)
5915 $regs = array();
5916 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5917 $picto = $regs[1];
5918 $path = $regs[2]; // $path is $mymodule
5919 }
5920
5921 // Clean parameters
5922 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5923 $picto .= '.png';
5924 }
5925 // If alt path are defined, define url where img file is, according to physical path
5926 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5927 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5928 if ($type == 'main') {
5929 continue;
5930 }
5931 // This consumes a lot of time, that's why enabling alternative dir like "custom" dir should be avoid
5932 if (file_exists($dirroot . '/' . $path . '/img/' . $picto) && !empty($conf->file->dol_url_root)) {
5933 $url = DOL_URL_ROOT . $conf->file->dol_url_root[$type];
5934 break;
5935 }
5936 }
5937
5938 // $url is '' or '/custom', $path is current theme or
5939 $fullpathpicto = $url . '/' . $path . '/img/' . $picto;
5940 }
5941
5942 if ($srconly) {
5943 return $fullpathpicto;
5944 }
5945
5946 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5947 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
5948}
5949
5957function getImgPictoConv($mode = 'fa')
5958{
5959 global $conf;
5960
5961 if (empty($mode) || $mode == 'fa') {
5962 // Array when the fa picto key is different than the Dolibarr picto key.
5963 $arrayconvpictotofa = array(
5964 'account' => 'university',
5965 'accounting_account' => 'clipboard-list',
5966 'accountline' => 'receipt',
5967 'accountancy' => 'search-dollar',
5968 'action' => 'calendar-alt',
5969 'add' => 'plus-circle',
5970 'address' => 'address-book',
5971 'ai' => 'magic',
5972 'admin' => 'star',
5973 'asset' => 'money-check-alt',
5974 'autofill' => 'fill',
5975 'back' => 'arrow-left',
5976 'bank_account' => 'university',
5977 'bill' => 'file-invoice-dollar',
5978 'billa' => 'file-excel',
5979 'billr' => 'file-invoice-dollar',
5980 'billd' => 'file-medical',
5981 'blockedlog' => 'file-archive',
5982 'bookcal' => 'calendar-check',
5983 'supplier_invoice' => 'file-invoice-dollar',
5984 'supplier_invoicea' => 'file-excel',
5985 'supplier_invoicer' => 'file-invoice-dollar',
5986 'supplier_invoiced' => 'file-medical',
5987 'bom' => 'shapes',
5988 'card' => 'address-card',
5989 'chart' => 'chart-line',
5990 'company' => 'building',
5991 'contact' => 'address-book',
5992 'contract' => 'suitcase',
5993 'collab' => 'people-arrows',
5994 'conversation' => 'comments',
5995 'country' => 'globe-americas',
5996 'cron' => 'business-time',
5997 'cross' => 'times',
5998 'chevron-double-left' => 'angle-double-left',
5999 'chevron-double-right' => 'angle-double-right',
6000 'chevron-double-down' => 'angle-double-down',
6001 'chevron-double-top' => 'angle-double-up',
6002 'donation' => 'gift',
6003 'dynamicprice' => 'hand-holding-usd',
6004 'setup' => 'cog',
6005 'companies' => 'building',
6006 'products' => 'cube',
6007 'commercial' => 'suitcase',
6008 'invoicing' => 'coins',
6009 'accounting' => 'search-dollar',
6010 'category' => 'tag',
6011 'dollyrevert' => 'dolly',
6012 'file-o' => 'file',
6013 'generate' => 'plus-square',
6014 'hrm' => 'user-tie',
6015 'incoterm' => 'truck-loading',
6016 'margin' => 'calculator',
6017 'members' => 'user-friends',
6018 'ticket' => 'ticket-alt',
6019 'globe' => 'external-link-alt',
6020 'lot' => 'barcode',
6021 'email' => 'at',
6022 'establishment' => 'building',
6023 'edit' => 'pencil-alt',
6024 'entity' => 'globe',
6025 'graph' => 'chart-line',
6026 'grip_title' => 'arrows-alt',
6027 'grip' => 'arrows-alt',
6028 'help' => 'question-circle',
6029 'generic' => 'file',
6030 'holiday' => 'umbrella-beach',
6031 'info' => 'info-circle',
6032 'info_black' => 'info-circle',
6033 'inventory' => 'boxes',
6034 'intracommreport' => 'globe-europe',
6035 'jobprofile' => 'cogs',
6036 'knowledgemanagement' => 'ticket-alt',
6037 'label' => 'layer-group',
6038 'layout' => 'columns',
6039 'line' => 'bars',
6040 'loan' => 'money-bill-alt',
6041 'member' => 'user-alt',
6042 'meeting' => 'chalkboard-teacher',
6043 'mrp' => 'cubes',
6044 'next' => 'arrow-alt-circle-right',
6045 'trip' => 'wallet',
6046 'expensereport' => 'wallet',
6047 'group' => 'users',
6048 'movement' => 'people-carry',
6049 'sign-out' => 'sign-out-alt',
6050 'superadmin' => 'star',
6051 'switch_off' => 'toggle-off',
6052 'switch_off_grey' => 'toggle-off',
6053 'switch_off_warning' => 'toggle-off',
6054 'switch_off_red' => 'toggle-off',
6055 'switch_on' => 'toggle-on',
6056 'switch_on_grey' => 'toggle-on',
6057 'switch_on_warning' => 'toggle-on',
6058 'switch_on_red' => 'toggle-on',
6059 'check' => 'check',
6060 'bookmark' => 'star',
6061 'bank' => 'university',
6062 'close_title' => 'times',
6063 'delete' => 'trash',
6064 'filter' => 'filter',
6065 'list-alt' => 'list-alt',
6066 'calendarlist' => 'bars',
6067 'calendar' => 'calendar-alt',
6068 'calendarmonth' => 'calendar-alt',
6069 'calendarweek' => 'calendar-week',
6070 'calendarday' => 'calendar-day',
6071 'calendarperuser' => 'table',
6072 'calendarpertype' => 'table',
6073 'intervention' => 'ambulance',
6074 'invoice' => 'file-invoice-dollar',
6075 'order' => 'file-invoice',
6076 'error' => 'exclamation-triangle',
6077 'warning' => 'exclamation-triangle',
6078 'other' => 'square',
6079 'playdisabled' => 'play',
6080 'pdf' => 'file-pdf',
6081 'poll' => 'check-double',
6082 'pos' => 'cash-register',
6083 'preview' => 'binoculars',
6084 'project' => 'project-diagram',
6085 'projectpub' => 'project-diagram',
6086 'projecttask' => 'tasks',
6087 'propal' => 'file-signature',
6088 'proposal' => 'file-signature',
6089 'partnership' => 'handshake',
6090 'payment' => 'money-check-alt',
6091 'payment_vat' => 'money-check-alt',
6092 'pictoconfirm' => 'check-square',
6093 'phoning' => 'phone',
6094 'phoning_mobile' => 'mobile-alt',
6095 'phoning_fax' => 'fax',
6096 'previous' => 'arrow-alt-circle-left',
6097 'printer' => 'print',
6098 'product' => 'cube',
6099 'puce' => 'angle-right',
6100 'recent' => 'check-square',
6101 'reception' => 'dolly',
6102 'recruitmentjobposition' => 'id-card-alt',
6103 'recruitmentcandidature' => 'id-badge',
6104 'resize' => 'crop',
6105 'supplier_order' => 'dol-order_supplier',
6106 'supplier_proposal' => 'file-signature',
6107 'refresh' => 'redo',
6108 'region' => 'map-marked',
6109 'replacement' => 'exchange-alt',
6110 'resource' => 'laptop-house',
6111 'recurring' => 'history',
6112 'service' => 'concierge-bell',
6113 'skill' => 'shapes',
6114 'state' => 'map-marked-alt',
6115 'security' => 'key',
6116 'salary' => 'wallet',
6117 'shipment' => 'dolly',
6118 'stock' => 'box-open',
6119 'stats' => 'chart-bar',
6120 'split' => 'code-branch',
6121 'status' => 'stop-circle',
6122 'stripe' => 'stripe-s',
6123 'supplier' => 'building',
6124 'technic' => 'cogs',
6125 'tick' => 'check',
6126 'timespent' => 'clock',
6127 'title_setup' => 'tools',
6128 'title_accountancy' => 'money-check-alt',
6129 'title_bank' => 'university',
6130 'title_hrm' => 'umbrella-beach',
6131 'title_agenda' => 'calendar-alt',
6132 'uncheck' => 'times',
6133 'uparrow' => 'share',
6134 'url' => 'external-link-alt',
6135 'vat' => 'money-check-alt',
6136 'vcard' => 'arrow-alt-circle-down',
6137 'jabber' => 'comment',
6138 'website' => 'globe-americas',
6139 'workstation' => 'pallet',
6140 'webhook' => 'bullseye',
6141 'world' => 'globe',
6142 'private' => 'user-lock',
6143 'conferenceorbooth' => 'chalkboard-teacher',
6144 'eventorganization' => 'project-diagram',
6145 'webportal' => 'door-open'
6146 );
6147
6148 if ($conf->currency == 'EUR') {
6149 $arrayconvpictotofa['currency'] = 'euro-sign';
6150 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
6151 } else {
6152 $arrayconvpictotofa['currency'] = 'dollar-sign';
6153 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
6154 }
6155 } else {
6156 $arrayconvpictotofa = array();
6157 }
6158
6159 return $arrayconvpictotofa;
6160}
6161
6162
6177function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
6178{
6179 if (strpos($picto, '^') === 0) {
6180 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6181 } else {
6182 return img_picto($titlealt, 'object_' . $picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6183 }
6184}
6185
6197function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
6198{
6199 global $conf;
6200
6201 if (is_numeric($picto)) {
6202 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
6203 //$picto = $leveltopicto[$picto];
6204 return '<i class="fa fa-weather-level' . $picto . '"></i>';
6205 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6206 $picto .= '.png';
6207 }
6208
6209 $path = DOL_URL_ROOT . '/theme/' . $conf->theme . '/img/weather/' . $picto;
6210
6211 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
6212}
6213
6225function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
6226{
6227 global $conf;
6228
6229 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6230 $picto .= '.png';
6231 }
6232
6233 if ($pictoisfullpath) {
6234 $path = $picto;
6235 } else {
6236 $path = DOL_URL_ROOT . '/theme/common/' . $picto;
6237
6238 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
6239 $themepath = DOL_DOCUMENT_ROOT . '/theme/' . $conf->theme . '/img/' . $picto;
6240
6241 if (file_exists($themepath)) {
6242 $path = $themepath;
6243 }
6244 }
6245 }
6246
6247 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
6248}
6249
6263function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
6264{
6265 global $langs;
6266
6267 if (empty($titlealt) || $titlealt == 'default') {
6268 if ($numaction == '-1' || $numaction == 'ST_NO') {
6269 $numaction = -1;
6270 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
6271 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
6272 $numaction = 0;
6273 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
6274 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
6275 $numaction = 1;
6276 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
6277 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
6278 $numaction = 2;
6279 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
6280 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
6281 $numaction = 3;
6282 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
6283 } else {
6284 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus ' . $numaction);
6285 $numaction = 0;
6286 }
6287 }
6288 if (!is_numeric($numaction)) {
6289 $numaction = 0;
6290 }
6291
6292 return img_picto($titlealt, (empty($picto) ? 'stcomm' . $numaction . '.png' : $picto), $moreatt);
6293}
6294
6302function img_edit_add($titlealt = 'default', $other = '')
6303{
6304 global $langs;
6305
6306 if ($titlealt == 'default') {
6307 $titlealt = $langs->trans('Add');
6308 }
6309
6310 return img_picto($titlealt, 'edit_add.png', $other);
6311}
6319function img_edit_remove($titlealt = 'default', $other = '')
6320{
6321 global $langs;
6322
6323 if ($titlealt == 'default') {
6324 $titlealt = $langs->trans('Remove');
6325 }
6326
6327 return img_picto($titlealt, 'edit_remove.png', $other);
6328}
6329
6338function img_edit($titlealt = 'default', $float = 0, $other = '')
6339{
6340 global $langs;
6341
6342 if ($titlealt == 'default') {
6343 $titlealt = $langs->trans('Modify');
6344 }
6345
6346 return img_picto($titlealt, 'edit', ($float ? 'style="float: ' . ($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right') . '"' : "") . ($other ? ' ' . $other : ''));
6347}
6348
6357function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
6358{
6359 global $langs;
6360
6361 if ($titlealt == 'default') {
6362 $titlealt = $langs->trans('View');
6363 }
6364
6365 $moreatt = ($float ? 'style="float: right" ' : '') . $other;
6366
6367 return img_picto($titlealt, 'eye', $moreatt);
6368}
6369
6378function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
6379{
6380 global $langs;
6381
6382 if ($titlealt == 'default') {
6383 $titlealt = $langs->trans('Delete');
6384 }
6385
6386 return img_picto($titlealt, 'delete', $other, 0, 0, 0, '', $morecss);
6387}
6388
6396function img_printer($titlealt = "default", $other = '')
6397{
6398 global $langs;
6399 if ($titlealt == "default") {
6400 $titlealt = $langs->trans("Print");
6401 }
6402 return img_picto($titlealt, 'printer', $other);
6403}
6404
6412function img_split($titlealt = 'default', $other = 'class="pictosplit"')
6413{
6414 global $langs;
6415
6416 if ($titlealt == 'default') {
6417 $titlealt = $langs->trans('Split');
6418 }
6419
6420 return img_picto($titlealt, 'split', $other);
6421}
6422
6430function img_help($usehelpcursor = 1, $usealttitle = 1)
6431{
6432 global $langs;
6433
6434 if ($usealttitle) {
6435 if (is_string($usealttitle)) {
6436 $usealttitle = dol_escape_htmltag($usealttitle);
6437 } else {
6438 $usealttitle = $langs->trans('Info');
6439 }
6440 }
6441
6442 return img_picto($usealttitle, 'info', 'style="vertical-align: middle;' . ($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')) . '"');
6443}
6444
6451function img_info($titlealt = 'default')
6452{
6453 global $langs;
6454
6455 if ($titlealt == 'default') {
6456 $titlealt = $langs->trans('Informations');
6457 }
6458
6459 return img_picto($titlealt, 'info', 'style="vertical-align: middle;"');
6460}
6461
6470function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6471{
6472 global $langs;
6473
6474 if ($titlealt == 'default') {
6475 $titlealt = $langs->trans('Warning');
6476 }
6477
6478 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6479 return img_picto($titlealt, 'warning', 'class="' . $morecss . '"' . ($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' ' . $moreatt) : ''));
6480}
6481
6488function img_error($titlealt = 'default')
6489{
6490 global $langs;
6491
6492 if ($titlealt == 'default') {
6493 $titlealt = $langs->trans('Error');
6494 }
6495
6496 return img_picto($titlealt, 'error');
6497}
6498
6506function img_next($titlealt = 'default', $moreatt = '')
6507{
6508 global $langs;
6509
6510 if ($titlealt == 'default') {
6511 $titlealt = $langs->trans('Next');
6512 }
6513
6514 //return img_picto($titlealt, 'next.png', $moreatt);
6515 return '<span class="fa fa-chevron-right paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6516}
6517
6525function img_previous($titlealt = 'default', $moreatt = '')
6526{
6527 global $langs;
6528
6529 if ($titlealt == 'default') {
6530 $titlealt = $langs->trans('Previous');
6531 }
6532
6533 //return img_picto($titlealt, 'previous.png', $moreatt);
6534 return '<span class="fa fa-chevron-left paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6535}
6536
6545function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6546{
6547 global $langs;
6548
6549 if ($titlealt == 'default') {
6550 $titlealt = $langs->trans('Down');
6551 }
6552
6553 return img_picto($titlealt, ($selected ? '1downarrow_selected' : '1downarrow'), 'class="imgdown' . ($moreclass ? " " . $moreclass : "") . '"');
6554}
6555
6564function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6565{
6566 global $langs;
6567
6568 if ($titlealt == 'default') {
6569 $titlealt = $langs->trans('Up');
6570 }
6571
6572 return img_picto($titlealt, ($selected ? '1uparrow_selected' : '1uparrow'), 'class="imgup' . ($moreclass ? " " . $moreclass : "") . '"');
6573}
6574
6583function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6584{
6585 global $langs;
6586
6587 if ($titlealt == 'default') {
6588 $titlealt = $langs->trans('Left');
6589 }
6590
6591 return img_picto($titlealt, ($selected ? '1leftarrow_selected' : '1leftarrow'), $moreatt);
6592}
6593
6602function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6603{
6604 global $langs;
6605
6606 if ($titlealt == 'default') {
6607 $titlealt = $langs->trans('Right');
6608 }
6609
6610 return img_picto($titlealt, ($selected ? '1rightarrow_selected' : '1rightarrow'), $moreatt);
6611}
6612
6620function img_allow($allow, $titlealt = 'default')
6621{
6622 global $langs;
6623
6624 if ($titlealt == 'default') {
6625 $titlealt = $langs->trans('Active');
6626 }
6627
6628 if ($allow == 1) {
6629 return img_picto($titlealt, 'tick');
6630 }
6631
6632 return '-';
6633}
6634
6642function img_credit_card($brand, $morecss = 'fa-2x inline-block valignmiddle')
6643{
6644 if (is_null($morecss)) {
6645 $morecss = 'fa-2x';
6646 }
6647
6648 if ($brand == 'visa' || $brand == 'Visa') {
6649 $brand = 'cc-visa';
6650 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6651 $brand = 'cc-mastercard';
6652 } elseif ($brand == 'amex' || $brand == 'American Express') {
6653 $brand = 'cc-amex';
6654 } elseif ($brand == 'discover' || $brand == 'Discover') {
6655 $brand = 'cc-discover';
6656 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6657 $brand = 'cc-jcb';
6658 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6659 $brand = 'cc-diners-club';
6660 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6661 $brand = 'credit-card';
6662 }
6663
6664 return '<span class="fa fa-' . $brand . ' fa-fw' . ($morecss ? ' ' . $morecss : '') . '"></span>';
6665}
6666
6675function img_mime($file, $titlealt = '', $morecss = '')
6676{
6677 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
6678
6679 $mimetype = dol_mimetype($file, '', 1);
6680 //$mimeimg = dol_mimetype($file, '', 2);
6681 $mimefa = dol_mimetype($file, '', 4);
6682
6683 if (empty($titlealt)) {
6684 $titlealt = 'Mime type: ' . $mimetype;
6685 }
6686
6687 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6688 return '<i class="fa fa-' . $mimefa . ' ' . (preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ') . ($morecss ? ' ' . $morecss : '') . '"' . ($titlealt ? ' title="' . dolPrintHTMLForAttribute($titlealt) . '"' : '') . '></i>';
6689}
6690
6691
6699function img_search($titlealt = 'default', $other = '')
6700{
6701 global $langs;
6702
6703 if ($titlealt == 'default') {
6704 $titlealt = $langs->trans('Search');
6705 }
6706
6707 $img = img_picto($titlealt, 'search', $other, 0, 1);
6708
6709 $input = '<input type="image" class="liste_titre" name="button_search" src="' . $img . '" ';
6710 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6711
6712 return $input;
6713}
6714
6722function img_searchclear($titlealt = 'default', $other = '')
6723{
6724 global $langs;
6725
6726 if ($titlealt == 'default') {
6727 $titlealt = $langs->trans('Search');
6728 }
6729
6730 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6731
6732 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="' . $img . '" ';
6733 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6734
6735 return $input;
6736}
6737
6750function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6751{
6752 global $conf, $langs;
6753
6754 if ($infoonimgalt) {
6755 $result = img_picto($text, 'info', 'class="' . ($morecss ? ' ' . $morecss : '') . '"');
6756 } else {
6757 if (empty($conf->use_javascript_ajax)) {
6758 $textfordropdown = '';
6759 }
6760
6761 $class = (empty($admin) ? 'undefined' : ((string) $admin == '1' ? 'info' : $admin));
6762 $fa = 'info-circle';
6763 if ($picto == 'warning') {
6764 $fa = 'exclamation-triangle';
6765 }
6766 $result = ($nodiv ? '' : '<div class="wordbreak ' . $class . ($morecss ? ' ' . $morecss : '') . ($textfordropdown ? ' hidden' : '') . '">');
6767 $result .= img_picto((string) $admin ? $langs->trans('InfoAdmin') : $langs->trans('Note'), $fa);
6768 $result .= ' ';
6769 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6770 $result .= ($nodiv ? '' : '</div>');
6771
6772 if ($textfordropdown) {
6773 $tmpresult = '<span class="' . $class . 'text opacitymedium cursorpointer">' . $langs->trans($textfordropdown) . ' ' . img_picto($langs->trans($textfordropdown), '1downarrow') . '</span>';
6774 $tmpresult .= '<script nonce="' . getNonce() . '" type="text/javascript">
6775 jQuery(document).ready(function() {
6776 jQuery(".' . $class . 'text").click(function() {
6777 console.log("toggle text");
6778 jQuery(".' . $class . '").toggle();
6779 });
6780 });
6781 </script>';
6782
6783 $result = $tmpresult . $result;
6784 }
6785 }
6786
6787 return $result;
6788}
6789
6790
6802function dol_print_error($db = null, $error = '', $errors = null)
6803{
6804 global $conf, $langs, $user, $argv;
6805 global $dolibarr_main_prod;
6806
6807 $out = '';
6808 $syslog = '';
6809
6810 // If error occurs before the $lang object was loaded
6811 if (!$langs) {
6812 require_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
6813 $langs = new Translate('', $conf);
6814 $langs->load("main");
6815 }
6816
6817 // Load translation files required by the error messages
6818 $langs->loadLangs(array('main', 'errors'));
6819
6820 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6821 $out .= $langs->trans("DolibarrHasDetectedError") . ".<br>\n";
6822 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6823 $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";
6824 }
6825 $out .= $langs->trans("InformationToHelpDiagnose") . ":<br>\n";
6826
6827 $out .= "<b>" . $langs->trans("Date") . ":</b> " . dol_print_date(time(), 'dayhourlog') . "<br>\n";
6828 $out .= "<b>" . $langs->trans("Dolibarr") . ":</b> " . DOL_VERSION . " - https://www.dolibarr.org<br>\n";
6829 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6830 $out .= "<b>" . $langs->trans("LevelOfFeature") . ":</b> " . getDolGlobalInt('MAIN_FEATURES_LEVEL') . "<br>\n";
6831 }
6832 if ($user instanceof User) {
6833 $out .= "<b>" . $langs->trans("Login") . ":</b> " . $user->login . "<br>\n";
6834 }
6835 if (function_exists("phpversion")) {
6836 $out .= "<b>" . $langs->trans("PHP") . ":</b> " . phpversion() . "<br>\n";
6837 }
6838 $out .= "<b>" . $langs->trans("Server") . ":</b> " . (isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '') . "<br>\n";
6839 if (function_exists("php_uname")) {
6840 $out .= "<b>" . $langs->trans("OS") . ":</b> " . php_uname() . "<br>\n";
6841 }
6842 $out .= "<b>" . $langs->trans("UserAgent") . ":</b> " . (isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '') . "<br>\n";
6843 $out .= "<br>\n";
6844 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6845 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6846 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6847 $out .= "<br>\n";
6848 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6849 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6850 } else { // Mode CLI
6851 $out .= '> ' . $langs->transnoentities("ErrorInternalErrorDetected") . ":\n" . $argv[0] . "\n";
6852 $syslog .= "pid=" . dol_getmypid();
6853 }
6854
6855 if (!empty($conf->modules)) {
6856 $out .= "<b>" . $langs->trans("Modules") . ":</b> " . implode(', ', $conf->modules) . "<br>\n";
6857 }
6858
6859 if (is_object($db)) {
6860 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6861 $out .= "<b>" . $langs->trans("DatabaseTypeManager") . ":</b> " . $db->type . "<br>\n";
6862 $lastqueryerror = $db->lastqueryerror();
6863 if (!utf8_check($lastqueryerror)) {
6864 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6865 }
6866 $out .= "<b>" . $langs->trans("RequestLastAccessInError") . ":</b> " . ($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6867 $out .= "<b>" . $langs->trans("ReturnCodeLastAccessInError") . ":</b> " . ($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6868 $out .= "<b>" . $langs->trans("InformationLastAccessInError") . ":</b> " . ($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6869 $out .= "<br>\n";
6870 } else { // Mode CLI
6871 // No dol_escape_htmltag for output, we are in CLI mode
6872 $out .= '> ' . $langs->transnoentities("DatabaseTypeManager") . ":\n" . $db->type . "\n";
6873 $out .= '> ' . $langs->transnoentities("RequestLastAccessInError") . ":\n" . ($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6874 $out .= '> ' . $langs->transnoentities("ReturnCodeLastAccessInError") . ":\n" . ($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6875 $out .= '> ' . $langs->transnoentities("InformationLastAccessInError") . ":\n" . ($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6876 }
6877 $syslog .= ", sql=" . $db->lastquery();
6878 $syslog .= ", db_error=" . $db->lasterror();
6879 }
6880
6881 if ($error || $errors) {
6882 // Merge all into $errors array
6883 if (is_array($error) && is_array($errors)) {
6884 $errors = array_merge($error, $errors);
6885 } elseif (is_array($error)) { // deprecated, use second parameters
6886 $errors = $error;
6887 } elseif (is_array($errors) && !empty($error)) {
6888 $errors = array_merge(array($error), $errors);
6889 } elseif (!empty($error)) {
6890 $errors = array_merge(array($error), array($errors));
6891 }
6892
6893 $langs->load("errors");
6894
6895 foreach ($errors as $msg) {
6896 if (empty($msg)) {
6897 continue;
6898 }
6899 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6900 $out .= "<b>" . $langs->trans("Message") . ":</b> " . dol_escape_htmltag($msg) . "<br>\n";
6901 } else { // Mode CLI
6902 $out .= '> ' . $langs->transnoentities("Message") . ":\n" . $msg . "\n";
6903 }
6904 $syslog .= ", msg=" . $msg;
6905 }
6906 }
6907 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6908 xdebug_print_function_stack();
6909 $out .= '<b>XDebug information:</b>' . "<br>\n";
6910 $out .= 'File: ' . xdebug_call_file() . "<br>\n";
6911 $out .= 'Line: ' . xdebug_call_line() . "<br>\n";
6912 $out .= 'Function: ' . xdebug_call_function() . "<br>\n";
6913 $out .= "<br>\n";
6914 }
6915
6916 // Return a http header with error code if possible
6917 if (!headers_sent()) {
6918 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6919 top_httphead();
6920 }
6921 //http_response_code(500); // If we use 500, message is not output with some command line tools
6922 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
6923 }
6924
6925 if (empty($dolibarr_main_prod)) {
6926 print $out;
6927 } else {
6928 if (empty($langs->defaultlang)) {
6929 $langs->setDefaultLang();
6930 }
6931 $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.
6932 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6933 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";
6934 print $langs->trans("DolibarrHasDetectedError") . '. ';
6935 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6936 if (!defined("MAIN_CORE_ERROR")) {
6937 define("MAIN_CORE_ERROR", 1);
6938 }
6939 }
6940
6941 dol_syslog("Error " . $syslog, LOG_ERR);
6942}
6943
6954function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6955{
6956 global $langs;
6957
6958 if (empty($email)) {
6959 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6960 }
6961
6962 $langs->load("errors");
6963 $now = dol_now();
6964
6965 print '<br><div class="center login_main_message"><div class="' . $morecss . '">';
6966 print $langs->trans("ErrorContactEMail", $email, $prefixcode . '-' . dol_print_date($now, '%Y%m%d%H%M%S'));
6967 if ($errormessage) {
6968 print '<br><br>' . $errormessage;
6969 }
6970 if (is_array($errormessages) && count($errormessages)) {
6971 foreach ($errormessages as $mesgtoshow) {
6972 print '<br><br>' . $mesgtoshow;
6973 }
6974 }
6975 print '</div></div>';
6976}
6977
6994function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6995{
6996 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6997}
6998
7017function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
7018{
7019 global $langs, $form;
7020 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
7021
7022 if ($moreattrib == 'class="right"') {
7023 $prefix .= 'right '; // For backward compatibility
7024 }
7025
7026 $tooltip = (string) $tooltip; // In case $tooltip is null
7027
7028 $sortorder = strtoupper((string) $sortorder);
7029 $out = '';
7030 $sortimg = '';
7031
7032 $tag = 'th';
7033 if ($thead == 2) {
7034 $tag = 'div';
7035 }
7036
7037 $tmpsortfield = explode(',', (string) $sortfield);
7038 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
7039 $tmpfield = explode(',', $field);
7040 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
7041
7042 if (strpos((string) $tooltip, ':') !== false) {
7043 $tmptooltip = explode(':', (string) $tooltip);
7044 } else {
7045 $tmptooltip = array($tooltip);
7046 }
7047
7048 $wrapcolumntitle = (empty($forcenowrapcolumntitle) || (!empty($tmptooltip[2]) && $tmptooltip[2] == '-1'));
7049
7050 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle) {
7051 $prefix = 'wrapcolumntitle ' . $prefix;
7052 }
7053
7054 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
7055 // If field is used as sort criteria we use a specific css class liste_titre_sel
7056 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
7057 $liste_titre = 'liste_titre';
7058 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
7059 $liste_titre = 'liste_titre_sel';
7060 }
7061
7062 $tagstart = '<' . $tag . ' class="' . $prefix . $liste_titre . '" ' . $moreattrib;
7063 //$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)).'"' : '');
7064 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle && !dol_textishtml($name)) ? ' title="' . dolPrintHTMLForAttribute($langs->trans($name)) . '"' : '';
7065 $tagstart .= '>';
7066
7067 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7068 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7069 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7070 $options = preg_replace('/&+/i', '&', $options);
7071 if (!preg_match('/^&/', $options)) {
7072 $options = '&' . $options;
7073 }
7074
7075 $sortordertouseinlink = '';
7076 if ($field1 != $sortfield1) { // We are on another field than current sorted field
7077 if (preg_match('/^DESC/i', $sortorder)) {
7078 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7079 } else { // We reverse the var $sortordertouseinlink
7080 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7081 }
7082 } else { // We are on field that is the first current sorting criteria
7083 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
7084 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7085 } else {
7086 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7087 }
7088 }
7089 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
7090 $out .= '<a class="reposition" href="' . dolBuildUrl($file, ['sortfield' => $field, 'sortorder' => $sortordertouseinlink, 'begin' => $begin]) . $options . '"';
7091 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
7092 $out .= '>';
7093 }
7094 if ($tooltip && $tmptooltip[0]) {
7095 // You can also use 'TranslationString:[keyfortooltiponclick]:[tooltipdirection]' for a tooltip on click or to change tooltip position.
7096 $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]));
7097 } else {
7098 $out .= $langs->trans((string) $name);
7099 }
7100
7101 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7102 $out .= '</a>';
7103 }
7104
7105 if (empty($thead) && $field) { // If this is a sort field
7106 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7107 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7108 $options = preg_replace('/&+/i', '&', $options);
7109 if (!preg_match('/^&/', $options)) {
7110 $options = '&' . $options;
7111 }
7112
7113 if (!$sortorder || ($field1 != $sortfield1)) {
7114 // Nothing
7115 } else {
7116 if (preg_match('/^DESC/', $sortorder)) {
7117 $sortimg .= '<span class="nowrap">' . img_up("Z-A", 0, 'paddingright') . '</span>';
7118 }
7119 if (preg_match('/^ASC/', $sortorder)) {
7120 $sortimg .= '<span class="nowrap">' . img_down("A-Z", 0, 'paddingright') . '</span>';
7121 }
7122 }
7123 }
7124
7125 $tagend = '</' . $tag . '>';
7126
7127 $out = $tagstart . $sortimg . $out . $tagend;
7128
7129 return $out;
7130}
7131
7140function print_titre($title)
7141{
7142 dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
7143
7144 print '<div class="titre">' . $title . '</div>';
7145}
7146
7158function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
7159{
7160 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
7161}
7162
7177function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '', $morecssonpicto = 'widthpictotitle')
7178{
7179 $return = '';
7180
7181 if ($picto == 'setup') {
7182 $picto = 'generic';
7183 }
7184
7185 $return .= "\n";
7186 $return .= '<table ' . ($id ? 'id="' . $id . '" ' : '') . 'class="centpercent notopnoleftnoright table-fiche-title' . ($morecssontable ? ' ' . $morecssontable : '') . '">'; // margin bottom must be same than into print_barre_list
7187 $return .= '<tr class="toptitle">';
7188 if ($picto) {
7189 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">' . img_picto('', $picto, 'class="valignmiddle pictotitle'.($morecssonpicto ? ' '.$morecssonpicto : '').'"', $pictoisfullpath) . '</td>';
7190 }
7191 $return .= '<td class="nobordernopadding valignmiddle col-title">';
7192 $return .= '<div class="titre inline-block">';
7193 $return .= '<span class="inline-block valignmiddle">' . $title . '</span>'; // $title is already HTML sanitized content
7194 $return .= '</div>';
7195 $return .= '</td>';
7196 if (dol_strlen($morehtmlcenter)) {
7197 $return .= '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7198 }
7199 if (dol_strlen($morehtmlright)) {
7200 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">' . $morehtmlright . '</td>';
7201 }
7202 $return .= '</tr></table>' . "\n";
7203
7204 return $return;
7205}
7206
7230function 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 = '')
7231{
7232 global $conf, $langs;
7233
7234 $savlimit = $limit;
7235 $savtotalnboflines = $totalnboflines;
7236 if (is_numeric($totalnboflines)) {
7237 $totalnboflines = abs($totalnboflines);
7238 }
7239
7240 // Detect if there is a subtitle
7241 $subtitle = '';
7242 $tmparray = preg_split('/<br>/i', $title, 2);
7243 if (!empty($tmparray[1])) {
7244 $title = $tmparray[0];
7245 $subtitle = $tmparray[1];
7246 }
7247
7248 $page = (int) $page;
7249
7250 if ($picto == 'setup') {
7251 $picto = 'title_setup';
7252 }
7253 if (($conf->browser->name == 'ie') && $picto == 'generic') {
7254 $picto = 'title.gif';
7255 }
7256 if ($limit < 0) {
7257 $limit = $conf->liste_limit;
7258 }
7259
7260 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
7261 $nextpage = 1;
7262 } else {
7263 $nextpage = 0;
7264 }
7265 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
7266
7267 print "\n";
7268 print "<!-- Begin print_barre_liste -->\n";
7269 print '<table class="centpercent notopnoleftnoright table-fiche-title' . ($morecss ? ' ' . $morecss : '') . '">';
7270 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
7271
7272 // Left
7273
7274 if ($picto && $title) {
7275 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
7276 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
7277 print '</td>';
7278 }
7279
7280 print '<td class="nobordernopadding valignmiddle col-title">';
7281 print '<div class="titre inline-block">';
7282 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()
7283 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
7284 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="' . $langs->trans("NbRecordQualified") . '">(' . $totalnboflines . ')</span>';
7285 }
7286 print '</div>';
7287 if (!empty($subtitle)) {
7288 print '<br><div class="subtitle inline-block hideonsmartphone">' . $subtitle . '</div>';
7289 }
7290 print '</td>';
7291
7292 // Center
7293 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
7294 print '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7295 }
7296
7297 // Right
7298 print '<td class="nobordernopadding valignmiddle right col-right">';
7299 print '<input type="hidden" name="pageplusoneold" value="' . ((int) $page + 1) . '">';
7300 $query = [];
7301 parse_str($options, $query);
7302 if ($sortfield) {
7303 $query += ['sortfield' => $sortfield];
7304 }
7305 if ($sortorder) {
7306 $query += ['sortorder' => $sortorder];
7307 }
7308
7309 $options = '&' . http_build_query($query);
7310 if ($page) {
7311 $query = array_merge($query, ['page' => $page]);
7312 }
7313 // Show navigation bar
7314 $pagelist = '';
7315 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
7316 if ($totalnboflines) { // If we know total nb of lines
7317 // Define nb of extra page links before and after selected page + ... + first or last
7318 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
7319
7320 if ($limit > 0) {
7321 $nbpages = ceil($totalnboflines / $limit);
7322 } else {
7323 $nbpages = 1;
7324 }
7325 $cpt = ($page - $maxnbofpage);
7326 if ($cpt < 0) {
7327 $cpt = 0;
7328 }
7329
7330 if ($cpt >= 1) {
7331 if (empty($pagenavastextinput)) {
7332 $query['page'] = 0;
7333 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">1</a></li>';
7334 if ($cpt > 2) {
7335 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7336 } elseif ($cpt == 2) {
7337 $query['page'] = 0;
7338 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">2</a></li>';
7339 }
7340 }
7341 }
7342
7343 do {
7344 if ($pagenavastextinput) {
7345 if ($cpt == $page) {
7346 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="' . ($totalnboflines > 100 ? 'width40' : 'width25') . ' center pageplusone heightofcombo" name="pageplusone" value="' . ($page + 1) . '"></li>';
7347 $pagelist .= '/';
7348 }
7349 } else {
7350 if ($cpt == $page) {
7351 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . '</span></li>';
7352 } else {
7353 $query['page'] = $cpt;
7354 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($cpt + 1) . '</a></li>';
7355 }
7356 }
7357 $cpt++;
7358 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
7359
7360 if (empty($pagenavastextinput)) {
7361 if ($cpt < $nbpages) {
7362 if ($cpt < $nbpages - 2) {
7363 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7364 } elseif ($cpt == $nbpages - 2) {
7365 $query['page'] = ($nbpages - 2);
7366 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($nbpages - 1) . '</a></li>';
7367 }
7368 $query['page'] = ($nbpages - 1);
7369 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7370 }
7371 } else {
7372 $query['page'] = ($nbpages - 1);
7373 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7374 }
7375 } else {
7376 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . "</li>";
7377 }
7378 }
7379
7380 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
7381 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
7382 }
7383
7384 // js to autoselect page field on focus
7385 if ($pagenavastextinput) {
7386 print ajax_autoselect('.pageplusone');
7387 }
7388
7389 print '</td>';
7390 print '</tr>';
7391
7392 print "</table>\n";
7393
7394 // Center
7395 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
7396 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">' . $morehtmlcenter . '</div>';
7397 }
7398
7399 print "<!-- End title -->\n\n";
7400}
7401
7418function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
7419{
7420 global $conf, $langs;
7421
7422 print '<div class="pagination"><ul>';
7423 if ($beforearrows) {
7424 print '<li class="paginationbeforearrows">';
7425 print $beforearrows;
7426 print '</li>';
7427 }
7428
7429 if (empty($hidenavigation)) {
7430 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
7431 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
7432 $pagesizechoices .= ',5000:5000';
7433 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
7434 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
7435 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
7436 //$pagesizechoices .= ',2:2';
7437 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
7438 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
7439 }
7440
7441 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7442 print '<li class="pagination">';
7443 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 . '">';
7444 print '<datalist id="limitlist">';
7445 } else {
7446 print '<li class="paginationcombolimit valignmiddle">';
7447 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")) . '">';
7448 }
7449 $tmpchoice = explode(',', $pagesizechoices);
7450 $tmpkey = $limit . ':' . $limit;
7451 if (!in_array($tmpkey, $tmpchoice)) {
7452 $tmpchoice[$tmpkey] = $tmpkey;
7453 }
7454 $tmpkey = $conf->liste_limit . ':' . $conf->liste_limit;
7455 if (!in_array($tmpkey, $tmpchoice)) {
7456 $tmpchoice[$tmpkey] = $tmpkey;
7457 }
7458 asort($tmpchoice, SORT_NUMERIC);
7459 foreach ($tmpchoice as $val) {
7460 $selected = '';
7461 $tmp = explode(':', $val);
7462 $key = $tmp[0];
7463 $val = $tmp[1];
7464 if ($key != '' && $val != '') {
7465 if ((int) $key == (int) $limit) {
7466 $selected = ' selected="selected"';
7467 }
7468 print '<option name="' . $key . '"' . $selected . '>' . dol_escape_htmltag($val) . '</option>' . "\n";
7469 }
7470 }
7471 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7472 print '</datalist>';
7473 } else {
7474 print '</select>';
7475 print ajax_combobox("limit" . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
7476 //print ajax_combobox("limit");
7477 }
7478
7479 if ($conf->use_javascript_ajax) {
7480 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
7481 <script>
7482 jQuery(document).ready(function () {
7483 jQuery(".selectlimit").change(function() {
7484 console.log("We change limit so we submit the form");
7485 $(this).parents(\'form:first\').submit();
7486 });
7487 });
7488 </script>
7489 ';
7490 }
7491 print '</li>';
7492 }
7493 if ($page > 0) {
7494 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>';
7495 }
7496 if ($betweenarrows) {
7497 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7498 print $betweenarrows;
7499 print '<!--</div>-->';
7500 }
7501 if ($nextpage > 0) {
7502 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>';
7503 }
7504 if ($afterarrows) {
7505 print '<li class="paginationafterarrows">';
7506 print $afterarrows;
7507 print '</li>';
7508 }
7509 }
7510 print '</ul></div>' . "\n";
7511}
7512
7513
7525function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7526{
7527 $morelabel = '';
7528
7529 if (preg_match('/%/', $rate)) {
7530 $rate = str_replace('%', '', $rate);
7531 $addpercent = true;
7532 }
7533 $reg = array();
7534 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7535 $morelabel = ' (' . $reg[1] . ')';
7536 $rate = preg_replace('/\s*' . preg_quote($morelabel, '/') . '/', '', $rate);
7537 $morelabel = ' ' . ($html ? '<span class="opacitymedium">' : '') . '(' . $reg[1] . ')' . ($html ? '</span>' : '');
7538 }
7539 if (preg_match('/\*/', $rate)) {
7540 $rate = str_replace('*', '', $rate);
7541 $info_bits |= 1;
7542 }
7543
7544 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7545 if (!preg_match('/\//', $rate)) {
7546 $ret = price($rate, 0, '', 0, 0) . ($addpercent ? '%' : '');
7547 } else {
7548 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7549 $ret = $rate . ($addpercent ? '%' : '');
7550 }
7551 if (($info_bits & 1) && $usestarfornpr >= 0) {
7552 $ret .= ' *';
7553 }
7554 $ret .= $morelabel;
7555 return $ret;
7556}
7557
7558
7574function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7575{
7576 global $langs, $conf;
7577
7578 // Clean parameters
7579 if (empty($amount)) {
7580 $amount = 0; // To have a numeric value if amount not defined or = ''
7581 }
7582 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7583 if ($rounding == -1) {
7584 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7585 }
7586 $nbdecimal = $rounding;
7587
7588 if ($outlangs === 'none') {
7589 // Use international separators
7590 $dec = '.';
7591 $thousand = '';
7592 } else {
7593 // Output separators by default (french)
7594 $dec = ',';
7595 $thousand = ' ';
7596
7597 // If $outlangs not forced, we use use language
7598 if (!($outlangs instanceof Translate)) {
7599 $outlangs = $langs;
7600 }
7601
7602 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7603 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7604 }
7605 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7606 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7607 }
7608 if ($thousand == 'None') {
7609 $thousand = '';
7610 } elseif ($thousand == 'Space') {
7611 $thousand = ' ';
7612 }
7613 }
7614 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7615
7616 //print "amount=".$amount."-";
7617 $amount = str_replace(',', '.', $amount); // should be useless
7618 //print $amount."-";
7619 $data = explode('.', $amount);
7620 $decpart = isset($data[1]) ? $data[1] : '';
7621 $decpart = preg_replace('/0+$/i', '', $decpart); // Remove 0 at end of decimal part
7622 //print "decpart=".$decpart."<br>";
7623 $end = '';
7624
7625 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7626 if (dol_strlen($decpart) > $nbdecimal) {
7627 $nbdecimal = dol_strlen($decpart);
7628 }
7629
7630 // If nbdecimal is higher than max to show
7631 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7632 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7633 $nbdecimal = $nbdecimalmaxshown;
7634 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7635 // If output is truncated, we show ...
7636 $end = '...';
7637 }
7638 }
7639
7640 // If force rounding
7641 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7642 if ($forcerounding === 'MU') {
7643 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7644 } elseif ($forcerounding === 'MT') {
7645 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7646 } elseif ($forcerounding >= 0) {
7647 $nbdecimal = (int) $forcerounding;
7648 }
7649 }
7650
7651 // Format number
7652 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7653 // Add symbol of currency if requested
7654 $cursymbolbefore = $cursymbolafter = '';
7655 if ($currency_code && is_object($outlangs)) {
7656 if ($currency_code == 'auto') {
7657 $currency_code = $conf->currency;
7658 }
7659
7660 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7661 $listoflanguagesbefore = array('nl_NL');
7662 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7663 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7664 } else {
7665 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7666 $cursymbolafter .= ($tmpcur == $currency_code ? ' ' . $tmpcur : $tmpcur);
7667 }
7668 }
7669 $output = $cursymbolbefore . $output . $end . ($cursymbolafter ? ' ' : '') . $cursymbolafter;
7670 if ($form) {
7671 $output = preg_replace('/\s/', '&nbsp;', $output);
7672 $output = preg_replace('/\'/', '&#039;', $output);
7673 }
7674
7675 return $output;
7676}
7677
7703function price2num($amount, $rounding = '', $option = 0)
7704{
7705 global $langs;
7706
7707 // Clean parameters
7708 if (is_null($amount)) {
7709 $amount = '';
7710 }
7711
7712 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7713 // Numbers must be '1234.56'
7714 // Decimal delimiter for PHP and database SQL requests must be '.'
7715 $dec = ',';
7716 $thousand = ' ';
7717 if (is_null($langs)) { // $langs is not defined, we use english values.
7718 $dec = '.';
7719 $thousand = ',';
7720 } else {
7721 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7722 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7723 }
7724 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7725 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7726 }
7727 }
7728 if ($thousand == 'None') {
7729 $thousand = '';
7730 } elseif ($thousand == 'Space') {
7731 $thousand = ' ';
7732 }
7733 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7734
7735 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7736 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7737 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7738 if (!is_numeric($amount)) {
7739 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7740 }
7741
7742 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
7743 $amount = str_replace($thousand, '', $amount);
7744 }
7745
7746 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7747 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7748 // So if number was already a good number, it is converted into local Dolibarr setup.
7749 if (is_numeric($amount)) {
7750 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7751 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7752 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7753 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7754 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7755 }
7756 //print "QQ".$amount."<br>\n";
7757
7758 // Now make replaceents (the main goal of function)
7759
7760 if ($thousand != ',' && $thousand != '.') {
7761 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7762
7763 // REGEX: Find the integral and decimal parts.
7764 //
7765 // We require that the decimal point only appears once in $amount.
7766 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7767 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7768 // It covers two cases:
7769 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7770 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7771 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7772 $matches = array();
7773 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7774 $intPart = $matches['int'];
7775 $decPart = $matches['dec'];
7776
7777 // Remove all commas and dots from intPart
7778 $intPart = str_replace(['.', ','], '', $intPart);
7779
7780 // Combine intPart and decPart with a dot
7781 $amount = $intPart . $dec . $decPart;
7782 }
7783 }
7784
7785 $amount = str_replace(' ', '', $amount); // To avoid spaces
7786 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7787 $amount = str_replace($dec, '.', $amount);
7788
7789 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7790 }
7791 //print ' XX'.$amount.' '.$rounding;
7792
7793 // Now, $amount is a real PHP float number. We make a rounding if required.
7794 if ($rounding) {
7795 $nbofdectoround = '';
7796 if ($rounding == 'MU') {
7797 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7798 } elseif ($rounding == 'MT') {
7799 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7800 } elseif ($rounding == 'MS') {
7801 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK', 5);
7802 } elseif ($rounding == 'CU') {
7803 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7804 } elseif ($rounding == 'CT') {
7805 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7806 } elseif (is_numeric($rounding)) {
7807 $nbofdectoround = (int) $rounding;
7808 }
7809
7810 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7811 if (dol_strlen($nbofdectoround)) {
7812 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7813 } else {
7814 return 'ErrorBadParameterProvidedToFunction';
7815 }
7816 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7817
7818 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7819 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7820 if (is_numeric($amount)) {
7821 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7822 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7823 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7824 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7825 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7826 }
7827 //print "TT".$amount.'<br>';
7828
7829 // Always make replace because each math function (like round) replace
7830 // with local values and we want a number that has a SQL string format x.y
7831 if ($thousand != ',' && $thousand != '.') {
7832 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7833 }
7834
7835 $amount = str_replace(' ', '', $amount); // To avoid spaces
7836 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7837 $amount = str_replace($dec, '.', $amount);
7838
7839 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7840 }
7841
7842 return $amount;
7843}
7844
7857function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7858{
7859 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
7860
7861 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7862 $dimension *= 1000000;
7863 $unit -= 6;
7864 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7865 $dimension *= 1000;
7866 $unit -= 3;
7867 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7868 $dimension /= 1000000;
7869 $unit += 6;
7870 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7871 $dimension /= 1000;
7872 $unit += 3;
7873 }
7874 // Special case when we want output unit into pound or ounce
7875 /* TODO
7876 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7877 {
7878 $dimension = // convert dimension from standard unit into ounce or pound
7879 $unit = $forceunitoutput;
7880 }
7881 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7882 {
7883 $dimension = // convert dimension from standard unit into ounce or pound
7884 $unit = $forceunitoutput;
7885 }*/
7886
7887 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7888 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7889 $ret .= ' ' . measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
7890
7891 return $ret;
7892}
7893
7894
7907function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7908{
7909 global $db, $conf, $mysoc;
7910
7911 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7912 $thirdparty_seller = $mysoc;
7913 }
7914
7915 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);
7916
7917 $vatratecleaned = $vatrate;
7918 $reg = array();
7919 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7920 $vatratecleaned = trim($reg[1]);
7921 $vatratecode = $reg[2];
7922 }
7923
7924 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7925 {
7926 return 0;
7927 }*/
7928
7929 // Some test to guess with no need to make database access
7930 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7931 if ($local == 1) {
7932 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7933 return 0;
7934 }
7935 if ($thirdparty_seller->id == $mysoc->id) {
7936 if (!$thirdparty_buyer->localtax1_assuj) {
7937 return 0;
7938 }
7939 } else {
7940 if (!$thirdparty_seller->localtax1_assuj) {
7941 return 0;
7942 }
7943 }
7944 }
7945
7946 if ($local == 2) {
7947 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7948 if (!$mysoc->localtax2_assuj) {
7949 return 0; // If main vat is 0, IRPF may be different than 0.
7950 }
7951 if ($thirdparty_seller->id == $mysoc->id) {
7952 if (!$thirdparty_buyer->localtax2_assuj) {
7953 return 0;
7954 }
7955 } else {
7956 if (!$thirdparty_seller->localtax2_assuj) {
7957 return 0;
7958 }
7959 }
7960 }
7961 } else {
7962 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7963 return 0;
7964 }
7965 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7966 return 0;
7967 }
7968 }
7969
7970 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7971 if (in_array($mysoc->country_code, array('ES'))) {
7972 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7973 }
7974
7975 // Search local taxes
7976 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7977 if ($local == 1) {
7978 if ($thirdparty_seller != $mysoc) {
7979 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7980 return $thirdparty_seller->localtax1_value;
7981 }
7982 } else { // i am the seller
7983 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7984 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7985 }
7986 }
7987 }
7988 if ($local == 2) {
7989 if ($thirdparty_seller != $mysoc) {
7990 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7991 // TODO We should also return value defined on thirdparty only if defined
7992 return $thirdparty_seller->localtax2_value;
7993 }
7994 } else { // i am the seller
7995 if (in_array($mysoc->country_code, array('ES'))) {
7996 return $thirdparty_buyer->localtax2_value;
7997 } else {
7998 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7999 }
8000 }
8001 }
8002 }
8003
8004 // By default, search value of local tax on line of common tax
8005 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
8006 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8007 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdparty_seller->country_code) . "'";
8008 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8009 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8010 if (!empty($vatratecode)) {
8011 $sql .= " AND t.code ='" . $db->escape($vatratecode) . "'"; // If we have the code, we use it in priority
8012 } else {
8013 $sql .= " AND t.recuperableonly = '" . $db->escape((string) $vatnpr) . "'";
8014 }
8015
8016 $resql = $db->query($sql);
8017
8018 if ($resql) {
8019 $obj = $db->fetch_object($resql);
8020 if ($obj) {
8021 if ($local == 1) {
8022 return $obj->localtax1;
8023 } elseif ($local == 2) {
8024 return $obj->localtax2;
8025 }
8026 }
8027 }
8028
8029 return 0;
8030}
8031
8032
8041function isOnlyOneLocalTax($local)
8042{
8043 $tax = get_localtax_by_third($local);
8044
8045 $valors = explode(":", $tax);
8046
8047 if (count($valors) > 1) {
8048 return false;
8049 } else {
8050 return true;
8051 }
8052}
8053
8060function get_localtax_by_third($local)
8061{
8062 global $db, $mysoc;
8063
8064 $sql = " SELECT t.localtax" . $local . " as localtax";
8065 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t INNER JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = t.fk_pays";
8066 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.active = 1 AND t.entity IN (" . getEntity('c_tva') . ") AND t.taux = (";
8067 $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";
8068 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.entity IN (" . getEntity('c_tva') . ") AND tt.active = 1)";
8069 $sql .= " AND t.localtax" . $local . "_type <> '0'";
8070 $sql .= " ORDER BY t.rowid DESC";
8071
8072 $resql = $db->query($sql);
8073 if ($resql) {
8074 $obj = $db->fetch_object($resql);
8075 if ($obj) {
8076 return $obj->localtax;
8077 } else {
8078 return '0';
8079 }
8080 }
8081
8082 return 'Error';
8083}
8084
8085
8097function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
8098{
8099 global $db;
8100
8101 dol_syslog("getTaxesFromId vat id or rate = " . $vatrate);
8102
8103 // Search local taxes
8104 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
8105 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
8106 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8107 if ($firstparamisid) {
8108 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8109 } else {
8110 $vatratecleaned = $vatrate;
8111 $vatratecode = '';
8112 $reg = array();
8113 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
8114 $vatratecleaned = $reg[1];
8115 $vatratecode = $reg[2];
8116 }
8117
8118 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8119 /*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 ??
8120 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
8121 $sql .= " WHERE t.fk_pays = c.rowid";
8122 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8123 $sql .= " AND c.code = '" . $db->escape($buyer->country_code) . "'";
8124 } else {
8125 $sql .= " AND c.code = '" . $db->escape($seller->country_code) . "'";
8126 }
8127 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8128 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8129 if ($vatratecode) {
8130 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8131 }
8132 }
8133
8134 $resql = $db->query($sql);
8135 if ($resql) {
8136 $obj = $db->fetch_object($resql);
8137 if ($obj) {
8138 return array(
8139 'rowid' => $obj->rowid,
8140 'code' => $obj->code,
8141 'rate' => $obj->rate,
8142 'localtax1' => $obj->localtax1,
8143 'localtax1_type' => $obj->localtax1_type,
8144 'localtax2' => $obj->localtax2,
8145 'localtax2_type' => $obj->localtax2_type,
8146 'npr' => $obj->npr,
8147 'accountancy_code_sell' => $obj->accountancy_code_sell,
8148 'accountancy_code_buy' => $obj->accountancy_code_buy
8149 );
8150 } else {
8151 return array();
8152 }
8153 } else {
8154 dol_print_error($db);
8155 }
8156
8157 return array();
8158}
8159
8176function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
8177{
8178 global $db, $mysoc;
8179
8180 dol_syslog("getLocalTaxesFromRate vatrate=" . $vatrate . " local=" . $local);
8181
8182 // Search local taxes
8183 $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";
8184 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8185 if ($firstparamisid) {
8186 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8187 } else {
8188 $vatratecleaned = $vatrate;
8189 $vatratecode = '';
8190 $reg = array();
8191 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
8192 $vatratecleaned = $reg[1];
8193 $vatratecode = $reg[2];
8194 }
8195
8196 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8197 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
8198 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
8199 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'"; // local tax in spain use the buyer country ??
8200 } else {
8201 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
8202 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'";
8203 }
8204 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8205 if ($vatratecode) {
8206 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8207 }
8208 }
8209
8210 $resql = $db->query($sql);
8211 if ($resql) {
8212 $obj = $db->fetch_object($resql);
8213
8214 if ($obj) {
8215 $vateratestring = $obj->rate . ($obj->code ? ' (' . $obj->code . ')' : '');
8216
8217 if ($local == 1) {
8218 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8219 } elseif ($local == 2) {
8220 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8221 } else {
8222 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);
8223 }
8224 }
8225 }
8226
8227 return array();
8228}
8229
8240function get_product_vat_for_country($idprod, $thirdpartytouseforcountry, $idprodfournprice = 0)
8241{
8242 global $db, $mysoc;
8243
8244 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8245
8246 $ret = 0;
8247 $found = 0;
8248
8249 if ($idprod > 0) {
8250 // Load product
8251 $product = new Product($db);
8252 $product->fetch($idprod);
8253
8254 if (($mysoc->country_code == $thirdpartytouseforcountry->country_code)
8255 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouseforcountry->country_code, array('FR', 'MC')))
8256 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouseforcountry->country_code, array('MQ', 'GP')))
8257 ) {
8258 // If country of thirdparty to consider is ours
8259 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
8260 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
8261 if ($result > 0) {
8262 $ret = $product->vatrate_supplier;
8263 if ($product->default_vat_code_supplier) {
8264 $ret .= ' (' . $product->default_vat_code_supplier . ')';
8265 }
8266 $found = 1;
8267 }
8268 }
8269 if (!$found) {
8270 $ret = $product->tva_tx; // Default sales vat of product
8271 if ($product->default_vat_code) {
8272 $ret .= ' (' . $product->default_vat_code . ')';
8273 }
8274 $found = 1;
8275 }
8276 } else {
8277 // TODO Read default product vat according to product and an other countrycode.
8278 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8279 }
8280 }
8281
8282 if (!$found) {
8283 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8284 // 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).
8285 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
8286 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8287 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8288 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8289 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8290 $sql .= $db->plimit(1);
8291
8292 $resql = $db->query($sql);
8293 if ($resql) {
8294 $obj = $db->fetch_object($resql);
8295 if ($obj) {
8296 $ret = $obj->vat_rate;
8297 if ($obj->default_vat_code) {
8298 $ret .= ' (' . $obj->default_vat_code . ')';
8299 }
8300 }
8301 $db->free($resql);
8302 } else {
8303 dol_print_error($db);
8304 }
8305 } else {
8306 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
8307 // '1.23'
8308 // or '1.23 (CODE)'
8309 $defaulttx = '';
8310 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8311 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8312 }
8313 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8314 $defaultcode = $reg[1];
8315 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8316 }*/
8317
8318 $ret = $defaulttx;
8319 }
8320 }
8321
8322 dol_syslog("get_product_vat_for_country: ret=" . $ret);
8323
8324 return $ret;
8325}
8326
8336function get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
8337{
8338 global $db, $mysoc;
8339
8340 if (!class_exists('Product')) {
8341 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8342 }
8343
8344 $ret = 0;
8345 $found = 0;
8346
8347 if ($idprod > 0) {
8348 // Load product
8349 $product = new Product($db);
8350 $result = $product->fetch($idprod);
8351
8352 if ($mysoc->country_code == $thirdpartytouseforcountry->country_code) { // If selling country is ours
8353 /* Not defined yet, so we don't use this
8354 if ($local==1) $ret=$product->localtax1_tx;
8355 elseif ($local==2) $ret=$product->localtax2_tx;
8356 $found=1;
8357 */
8358 } else {
8359 // TODO Read default product vat according to product and another countrycode.
8360 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8361 }
8362 }
8363
8364 if (!$found) {
8365 // If vat of product for the country not found or not defined, we return higher vat of country.
8366 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
8367 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8368 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8369 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8370 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
8371 $sql .= $db->plimit(1);
8372
8373 $resql = $db->query($sql);
8374 if ($resql) {
8375 $obj = $db->fetch_object($resql);
8376 if ($obj) {
8377 if ($local == 1) {
8378 $ret = $obj->localtax1;
8379 } elseif ($local == 2) {
8380 $ret = $obj->localtax2;
8381 }
8382 }
8383 } else {
8384 dol_print_error($db);
8385 }
8386 }
8387
8388 dol_syslog("get_product_localtax_for_country: ret=" . $ret);
8389 return $ret;
8390}
8391
8410function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8411{
8412 global $mysoc, $db, $hookmanager;
8413
8414 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8415
8416 // Note: possible values for tva_assuj are 0/1 or franchise/reel
8417 $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;
8418
8419 if (empty($thirdparty_seller->country_code)) {
8420 $thirdparty_seller->country_code = $mysoc->country_code;
8421 }
8422 $seller_country_code = $thirdparty_seller->country_code;
8423 $seller_in_cee = isInEEC($thirdparty_seller);
8424
8425 if (empty($thirdparty_buyer->country_code)) {
8426 $thirdparty_buyer->country_code = $mysoc->country_code;
8427 }
8428 $buyer_country_code = $thirdparty_buyer->country_code;
8429 $buyer_in_cee = isInEEC($thirdparty_buyer);
8430
8431 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'));
8432
8433 $vatvalue = 0;
8434 $vatrule = '';
8435
8436 // 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)
8437 // we use the buyer VAT.
8438 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8439 if ($seller_in_cee && $buyer_in_cee) {
8440 $isacompany = $thirdparty_buyer->isACompany();
8441 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8442 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8443 if (!isValidVATID($thirdparty_buyer)) {
8444 $isacompany = 0;
8445 }
8446 }
8447
8448 if (!$isacompany) {
8449 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
8450 $vatrule = 'VATRULE 0';
8451 }
8452 }
8453 }
8454
8455 // If seller does not use VAT, default VAT is 0. End of rule.
8456 if (empty($vatrule) && !$seller_use_vat) {
8457 //print 'VATRULE 1';
8458 // TODO get the VAT Code of exemption asked into setup if country isInEEC (from an array list of possible
8459 // values like VATEX-EU-132-*, VATEX-FR-FRANCHISE, VATEX-EU-AE...
8460 // When we had recorded it, we also added a corresponding entry into table of vat code if it does not exists yet.
8461 // Here we test if entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-132-xx)'
8462 // If not, we add it and we return '0 (VATEX-EU-132-xx)'
8463 $vatvalue = 0;
8464 $vatrule = 'VATRULE 1';
8465 }
8466
8467 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
8468 if (empty($vatrule) && !empty($thirdparty_buyer->state_id)) {
8469 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
8470 $sql .= " FROM " . $db->prefix() . "c_tva as t";
8471 $sql .= " INNER JOIN " . $db->prefix() . "c_departements as d ON t.fk_department_buyer = d.rowid";
8472 $sql .= " WHERE d.rowid = " . ((int) $thirdparty_buyer->state_id);
8473 $sql .= " AND t.active > 0";
8474 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
8475 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8476
8477 $res = $db->query($sql);
8478 if ($res) {
8479 if ($db->num_rows($res)) {
8480 $obj = $db->fetch_object($res);
8481
8482 $vatvalue = $obj->vat_default_rate . ' (' . $obj->vat_default_code . ')';
8483 $vatrule = 'VATRULE 2';
8484 }
8485 $db->free($res);
8486 }
8487 }
8488
8489 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
8490 if (empty($vatrule) && (
8491 ($seller_country_code == $buyer_country_code)
8492 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
8493 || (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.
8494 )) { // Warning ->country_code not always defined
8495 //print 'VATRULE 3';
8496 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8497
8498 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
8499 // Special case for india.
8500 //print 'VATRULE 3b';
8501 $reg = array();
8502 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
8503 // we must revert the C+S into I
8504 $tmpvat = str_replace("C+S", "I", $tmpvat);
8505 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8506 // we must revert the I into C+S
8507 $tmpvat = str_replace("I", "C+S", $tmpvat);
8508 }
8509 }
8510
8511 $vatvalue = $tmpvat;
8512 $vatrule = 'VATRULE 3b';
8513 }
8514
8515 // 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.
8516 // 'VATRULE 4' - Not supported
8517
8518 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8519 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8520 if (empty($vatrule) && ($seller_in_cee && $buyer_in_cee)) {
8521 $isacompany = $thirdparty_buyer->isACompany();
8522 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8523 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8524 if (!isValidVATID($thirdparty_buyer)) {
8525 $isacompany = 0;
8526 }
8527 }
8528
8529 if (!$isacompany) {
8530 //print 'VATRULE 5';
8531 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8532 $vatrule = 'VATRULE 5';
8533 } else {
8534 //print 'VATRULE 6';
8535 // TODO This is the case of VAT exemption 'VATEX-EU-IC'
8536 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-IC)'
8537 // If not, we add it and we return '0 (VATEX-EU-IC)'
8538 $vatvalue = 0;
8539 $vatrule = 'VATRULE 6';
8540 }
8541 }
8542
8543 // 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
8544 // 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
8545 if (empty($vatrule) && getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8546 $isacompany = $thirdparty_buyer->isACompany();
8547 if (!$isacompany) {
8548 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8549 $vatrule = 'VATRULE extra';
8550 //print 'VATRULE extra';
8551 }
8552 }
8553
8554 // Otherwise the VAT proposed by default=0. End of rule.
8555 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8556 //print 'VATRULE 7';
8557 // TODO This is the case of VAT exemption 'VATEX-EU-G'
8558 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-xxx)'
8559 // If not, we add it and we return '0 (VATEX-xxx)'
8560
8561 // Allow an external module to bypass the calculation of prices
8562 $parameters = array('vatvalue' => $vatvalue, 'vatrule' => $vatrule);
8563 $tmpobject = null;
8564 $tmpaction = '';
8565 // @phan-suppress-next-line PhanPluginConstantVariableNull
8566 $reshook = $hookmanager->executeHooks('get_default_tva', $parameters, $tmpobject, $tmpaction); // @phan-suppress-current-line PhanPluginConstantVariableNull
8567 if ($reshook > 0 && !empty($hookmanager->resArray['vatvalue'])) {
8568 $vatvalue = $hookmanager->resArray['vatvalue'];
8569 $vatrule = $hookmanager->resArray['vatrule']; // For information
8570 }
8571
8572 return $vatvalue;
8573}
8574
8575
8586function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8587{
8588 global $db;
8589
8590 if ($idprodfournprice > 0) {
8591 if (!class_exists('ProductFournisseur')) {
8592 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
8593 }
8594 $prodprice = new ProductFournisseur($db);
8595 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8596 return $prodprice->fourn_tva_npr;
8597 } elseif ($idprod > 0) {
8598 if (!class_exists('Product')) {
8599 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8600 }
8601 $prod = new Product($db);
8602 $prod->fetch($idprod);
8603 return $prod->tva_npr;
8604 }
8605
8606 return 0;
8607}
8608
8622function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8623{
8624 global $mysoc;
8625
8626 if (!is_object($thirdparty_seller)) {
8627 return -1;
8628 }
8629 if (!is_object($thirdparty_buyer)) {
8630 return -1;
8631 }
8632
8633 if (empty($thirdparty_seller->country_code)) {
8634 $thirdparty_seller->country_code = $mysoc->country_code;
8635 }
8636 $seller_country_code = $thirdparty_seller->country_code;
8637 //$seller_in_cee = isInEEC($thirdparty_seller);
8638
8639 if (empty($thirdparty_buyer->country_code)) {
8640 $thirdparty_buyer->country_code = $mysoc->country_code;
8641 }
8642 $buyer_country_code = $thirdparty_buyer->country_code;
8643 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8644
8645 if ($local == 1) { // Localtax 1
8646 if ($mysoc->country_code == 'ES') {
8647 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8648 return 0;
8649 }
8650 } else {
8651 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8652 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8653 return 0;
8654 }
8655 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8656 return 0;
8657 }
8658 }
8659 } elseif ($local == 2) { //I Localtax 2
8660 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8661 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8662 return 0;
8663 }
8664 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8665 return 0;
8666 }
8667 }
8668
8669 if ($seller_country_code == $buyer_country_code) {
8670 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8671 }
8672
8673 return 0;
8674}
8675
8684function yn($yesno, $format = 1, $color = 0)
8685{
8686 global $langs;
8687
8688 $result = 'unknown';
8689 $classname = '';
8690 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'
8691 $result = $langs->trans('yes');
8692 if ($format == 1 || $format == 3) {
8693 $result = $langs->trans("Yes");
8694 }
8695 if ($format == 2) {
8696 $result = '<input type="checkbox" value="1" checked disabled>';
8697 }
8698 if ($format == 3) {
8699 $result = '<input type="checkbox" value="1" checked disabled> ' . $result;
8700 }
8701 if ($format == 4 || !is_numeric($format)) {
8702 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8703 }
8704
8705 $classname = 'ok';
8706 } else {
8707 $result = $langs->trans("no");
8708 if ($format == 1 || $format == 3) {
8709 $result = $langs->trans("No");
8710 }
8711 if ($format == 2) {
8712 $result = '<input type="checkbox" value="0" disabled>';
8713 }
8714 if ($format == 3) {
8715 $result = '<input type="checkbox" value="0" disabled> ' . $result;
8716 }
8717 if ($format == 4 || !is_numeric($format)) {
8718 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8719 }
8720
8721 if ($color == 2) {
8722 $classname = 'ok';
8723 } else {
8724 $classname = 'error';
8725 }
8726 }
8727 if ($color) {
8728 return '<span class="' . $classname . '">' . $result . '</span>';
8729 }
8730 return $result;
8731}
8732
8751function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8752{
8753 if (empty($modulepart) && is_object($object)) {
8754 if (!empty($object->module)) {
8755 $modulepart = $object->module;
8756 } elseif (!empty($object->element)) {
8757 $modulepart = $object->element;
8758 }
8759 }
8760
8761 $path = '';
8762
8763 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8764 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8765 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8766 $arrayforoldpath['product'] = 2;
8767 }
8768
8769 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8770 $level = $arrayforoldpath[$modulepart];
8771 }
8772 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8773 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8774 if (empty($num) && is_object($object)) {
8775 $num = $object->id;
8776 }
8777 if (empty($alpha)) {
8778 $num = preg_replace('/([^0-9])/i', '', $num);
8779 } else {
8780 $num = preg_replace('/^.*\-/i', '', $num);
8781 }
8782 $num = substr("000" . $num, -$level);
8783 if ($level == 1) {
8784 $path = substr($num, 0, 1);
8785 }
8786 if ($level == 2) {
8787 $path = substr($num, 1, 1) . '/' . substr($num, 0, 1);
8788 }
8789 if ($level == 3) {
8790 $path = substr($num, 2, 1) . '/' . substr($num, 1, 1) . '/' . substr($num, 0, 1);
8791 }
8792 } else {
8793 // We will enhance here a common way of forging path for document storage.
8794 // In a future, we may distribute directories on several levels depending on setup and object.
8795 // Here, $object->id, $object->ref and $modulepart are required.
8796 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceof Societe) {
8797 // 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
8798 $path = dol_sanitizeFileName((string) $object->id);
8799 } else {
8800 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8801 }
8802 }
8803
8804 if (empty($withoutslash) && !empty($path)) {
8805 $path .= '/';
8806 }
8807
8808 return $path;
8809}
8810
8819function dol_mkdir($dir, $dataroot = '', $newmask = '')
8820{
8821 dol_syslog("functions.lib::dol_mkdir: dir=" . $dir, LOG_INFO);
8822
8823 $dir = dol_sanitizePathName($dir, '_', 0);
8824
8825 $dir_osencoded = dol_osencode($dir);
8826 if (@is_dir($dir_osencoded)) {
8827 return 0;
8828 }
8829
8830 $nberr = 0;
8831 $nbcreated = 0;
8832
8833 $ccdir = '';
8834 if (!empty($dataroot)) {
8835 // Remove data root from loop
8836 $dir = str_replace($dataroot . '/', '', $dir);
8837 $ccdir = $dataroot . '/';
8838 }
8839
8840 $cdir = explode("/", $dir);
8841 $num = count($cdir);
8842 for ($i = 0; $i < $num; $i++) {
8843 if ($i > 0) {
8844 $ccdir .= '/' . $cdir[$i];
8845 } else {
8846 $ccdir .= $cdir[$i];
8847 }
8848 $regs = array();
8849 if (preg_match("/^.:$/", $ccdir, $regs)) {
8850 continue; // If the Windows path is incomplete, continue with next directory
8851 }
8852
8853 // Attention, is_dir() can fail event if the directory exists
8854 // (i.e. according the open_basedir configuration)
8855 if ($ccdir) {
8856 $ccdir_osencoded = dol_osencode($ccdir);
8857 if (!@is_dir($ccdir_osencoded)) {
8858 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8859
8860 umask(0);
8861 $dirmaskdec = octdec((string) $newmask);
8862 if (empty($newmask)) {
8863 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8864 }
8865 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8866 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8867 // If the is_dir has returned a false information, we arrive here
8868 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '" . $ccdir . "' (no permission to write into parent or directory already exists).", LOG_WARNING);
8869 $nberr++;
8870 } else {
8871 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' created", LOG_DEBUG);
8872 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8873 $nbcreated++;
8874 }
8875 } else {
8876 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8877 }
8878 }
8879 }
8880 return ($nberr ? -$nberr : $nbcreated);
8881}
8882
8883
8891function dolChmod($filepath, $newmask = '')
8892{
8893 if (!empty($newmask)) {
8894 @chmod($filepath, octdec($newmask));
8895 } elseif (getDolGlobalString('MAIN_UMASK')) {
8896 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8897 }
8898}
8899
8900
8906function picto_required()
8907{
8908 return '<span class="fieldrequired">*</span>';
8909}
8910
8911
8928function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8929{
8930 if (is_null($stringtoclean)) {
8931 return '';
8932 }
8933
8934 if ($removelinefeed == 2) {
8935 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8936 }
8937 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8938
8939 // 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)
8940 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8941
8942 $temp = str_replace('< ', '__ltspace__', $temp);
8943 $temp = str_replace('<:', '__lttwopoints__', $temp);
8944
8945 if ($strip_tags) {
8946 $temp = strip_tags($temp);
8947 } else {
8948 // 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).
8949 $pattern = "/<[^<>]+>/";
8950 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8951 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8952 // pass 2 - $temp after pass 2: 0000-021
8953 $tempbis = $temp;
8954 do {
8955 $temp = $tempbis;
8956 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8957 $tempbis = preg_replace($pattern, '', $tempbis);
8958 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8959 } while ($tempbis != $temp);
8960
8961 $temp = $tempbis;
8962
8963 // 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).
8964 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8965 }
8966
8967 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8968
8969 // Remove also carriage returns
8970 if ($removelinefeed == 1) {
8971 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8972 }
8973
8974 // And double spaces
8975 if ($removedoublespaces) {
8976 while (strpos($temp, " ") !== false) {
8977 $temp = str_replace(" ", " ", $temp);
8978 }
8979 }
8980
8981 $temp = str_replace('__ltspace__', '< ', $temp);
8982 $temp = str_replace('__lttwopoints__', '<:', $temp);
8983
8984 return trim($temp);
8985}
8986
9006function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
9007{
9008 if (empty($allowed_tags)) {
9009 $allowed_tags = array(
9010 // HTML 4
9011 "html",
9012 "head",
9013 "body",
9014 "article",
9015 "a",
9016 "abbr",
9017 "b",
9018 "blockquote",
9019 "br",
9020 "cite",
9021 "div",
9022 "dl",
9023 "dd",
9024 "dt",
9025 "em",
9026 "font",
9027 "img",
9028 "ins",
9029 "hr",
9030 "i",
9031 "li",
9032 "ol",
9033 "p",
9034 "q",
9035 "s",
9036 "span",
9037 "strike",
9038 "strong",
9039 "title",
9040 "table",
9041 "tr",
9042 "th",
9043 "td",
9044 "u",
9045 "ul",
9046 "sup",
9047 "sub",
9048 "blockquote",
9049 "pre",
9050 "h1",
9051 "h2",
9052 "h3",
9053 "h4",
9054 "h5",
9055 "h6",
9056
9057 // HTML 5
9058 "footer",
9059 "header",
9060 "menu",
9061 "menuitem",
9062 "nav",
9063 "section"
9064 );
9065 }
9066 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
9067 if ($allowiframe) {
9068 if (!in_array('iframe', $allowed_tags)) {
9069 $allowed_tags[] = "iframe";
9070 }
9071 }
9072 if ($allowlink) {
9073 if (!in_array('link', $allowed_tags)) {
9074 $allowed_tags[] = "link";
9075 }
9076 if (!in_array('meta', $allowed_tags)) {
9077 $allowed_tags[] = "meta";
9078 }
9079 }
9080 if ($allowscript) {
9081 if (!in_array('script', $allowed_tags)) {
9082 $allowed_tags[] = "script";
9083 }
9084 }
9085 if ($allowstyle) {
9086 if (!in_array('style', $allowed_tags)) {
9087 $allowed_tags[] = "style";
9088 }
9089 }
9090
9091 $allowed_tags_string = implode("><", $allowed_tags);
9092 $allowed_tags_string = '<' . $allowed_tags_string . '>';
9093
9094 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
9095
9096 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
9097
9098 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
9099 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
9100
9101 if ($allowphp) {
9102 $allowed_tags[] = "commentphp";
9103 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
9104 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
9105 }
9106
9107 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
9108 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
9109
9110 // Remove all HTML tags
9111 $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
9112
9113 if ($cleanalsosomestyles) { // Clean for remaining html tags
9114 $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
9115 }
9116 if ($removeclassattribute) { // Clean for remaining html tags
9117 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
9118 }
9119
9120 // Remove 'javascript:' that we should not find into a text
9121 // 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)).
9122 if ($cleanalsojavascript) {
9123 $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);
9124 }
9125
9126 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
9127
9128 if ($allowphp) {
9129 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
9130 }
9131
9132 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
9133
9134
9135 return $temp;
9136}
9137
9138
9151function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
9152{
9153 if (is_null($allowed_attributes)) {
9154 $allowed_attributes = array(
9155 // HTML 4
9156 "allow",
9157 "allowfullscreen",
9158 "alt",
9159 "async",
9160 "class",
9161 "contenteditable",
9162 "crossorigin",
9163 "data-html",
9164 "frameborder",
9165 "height",
9166 "href",
9167 "id",
9168 "name",
9169 "property",
9170 "rel",
9171 "src",
9172 "style",
9173 "target",
9174 "title",
9175 "type",
9176 "width",
9177
9178 // HTML5
9179 "footer",
9180 "header",
9181 "menu",
9182 "menuitem",
9183 "nav",
9184 "section"
9185 );
9186 }
9187 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
9188 if (!in_array("content", $allowed_attributes)) {
9189 $allowed_attributes[] = "content";
9190 }
9191 if (!in_array("http-equiv", $allowed_attributes)) {
9192 $allowed_attributes[] = "http-equiv";
9193 }
9194
9195 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
9196 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
9197 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . $stringtoclean . '</body></html>';
9198
9199 // Warning: loadHTML does not support HTML5 on old libxml versions.
9200 $dom = new DOMDocument('', 'UTF-8');
9201 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
9202 $savwarning = error_reporting();
9203 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
9204 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
9205 error_reporting($savwarning);
9206
9207 if ($dom instanceof DOMDocument) {
9208 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
9209 $el = $els->item($i);
9210 if (!$el instanceof DOMElement) {
9211 continue;
9212 }
9213 $attrs = $el->attributes;
9214 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
9215 //var_dump($attrs->item($ii));
9216 if (!empty($attrs->item($ii)->name)) {
9217 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
9218 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
9219 $els->item($i)->removeAttribute($attrs->item($ii)->name);
9220 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
9221 // If attribute is 'style'
9222 $valuetoclean = $attrs->item($ii)->value;
9223
9224 if (isset($valuetoclean)) {
9225 do {
9226 $oldvaluetoclean = $valuetoclean;
9227 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
9228 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
9229 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
9230 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
9231 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
9232 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
9233 }
9234
9235 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
9236 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
9237 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
9238 } while ($oldvaluetoclean != $valuetoclean);
9239 }
9240
9241 $attrs->item($ii)->value = $valuetoclean;
9242 }
9243 }
9244 }
9245 }
9246 }
9247
9248 $dom->encoding = 'UTF-8';
9249
9250 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
9251 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
9252
9253 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
9254 $return = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]*' . preg_quote('></head><body>', '/') . '/', '', $return);
9255 $return = preg_replace('/' . preg_quote('</body></html>', '/') . '$/', '', trim($return));
9256
9257 return trim($return);
9258 } else {
9259 return $stringtoclean;
9260 }
9261}
9262
9274function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
9275{
9276 $temp = $stringtoclean;
9277 foreach ($disallowed_tags as $tagtoremove) {
9278 $temp = preg_replace('/<\/?' . $tagtoremove . '>/', '', $temp);
9279 $temp = preg_replace('/<\/?' . $tagtoremove . '\s+[^>]*>/', '', $temp);
9280 }
9281
9282 if ($cleanalsosomestyles) {
9283 $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
9284 }
9285
9286 return $temp;
9287}
9288
9289
9299function dolCloseUnclosedHtmlTags($text)
9300{
9301 if (!is_string($text) || $text === '') {
9302 return $text;
9303 }
9304
9305 // Tags that never carry a closing tag
9306 $selfclosing = array('br', 'hr', 'img', 'input', 'meta', 'link', 'source', 'col', 'area', 'base', 'embed', 'param', 'track', 'wbr');
9307
9308 $opened = array();
9309 if (preg_match_all('/<\s*(\/?)([a-zA-Z][a-zA-Z0-9]*)[^>]*?(\/?)\s*>/', $text, $matches, PREG_SET_ORDER)) {
9310 foreach ($matches as $match) {
9311 $tag = strtolower($match[2]);
9312 if (in_array($tag, $selfclosing) || !empty($match[3])) {
9313 continue;
9314 }
9315 if (empty($match[1])) {
9316 $opened[] = $tag;
9317 } else {
9318 // Close the most recent matching opened tag, ignore a stray closing tag
9319 $idx = array_search($tag, array_reverse($opened, true), true);
9320 if ($idx !== false) {
9321 unset($opened[$idx]);
9322 }
9323 }
9324 }
9325 }
9326
9327 foreach (array_reverse($opened) as $tag) {
9328 $text .= '</'.$tag.'>';
9329 }
9330
9331 return $text;
9332}
9333
9343function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
9344{
9345 if ($nboflines == 1) {
9346 if (dol_textishtml($text)) {
9347 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
9348 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
9349 } else {
9350 if (isset($text)) {
9351 $firstline = preg_replace('/[\n\r].*/', '', $text);
9352 } else {
9353 $firstline = '';
9354 }
9355 }
9356 return $firstline . (isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
9357 } else {
9358 $ishtml = 0;
9359 if (dol_textishtml($text)) {
9360 $text = preg_replace('/\n/', '', $text);
9361 $ishtml = 1;
9362 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9363 } else {
9364 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9365 }
9366
9367 $text = strtr($text, $repTable);
9368 if ($charset == 'UTF-8') {
9369 $pattern = '/(<br[^>]*>)/Uu';
9370 } else {
9371 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9372 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9373 }
9374 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9375
9376 $firstline = '';
9377 $i = 0;
9378 $countline = 0;
9379 $lastaddediscontent = 1;
9380 while ($countline < $nboflines && isset($a[$i])) {
9381 if (preg_match('/<br[^>]*>/', $a[$i])) {
9382 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
9383 $firstline .= ($ishtml ? "<br>\n" : "\n");
9384 // Is it a br for a new line of after a printed line ?
9385 if (!$lastaddediscontent) {
9386 $countline++;
9387 }
9388 $lastaddediscontent = 0;
9389 }
9390 } else {
9391 $firstline .= $a[$i];
9392 $lastaddediscontent = 1;
9393 $countline++;
9394 }
9395 $i++;
9396 }
9397
9398 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
9399 //unset($a);
9400 $ret = $firstline . ($adddots ? '...' : '');
9401 //exit;
9402 return $ret;
9403 }
9404}
9405
9406
9418function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
9419{
9420 if (is_null($stringtoencode)) {
9421 return '';
9422 }
9423
9424 if (!$nl2brmode) {
9425 return nl2br($stringtoencode, $forxml);
9426 } else {
9427 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
9428 return $ret;
9429 }
9430}
9431
9441function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
9442{
9443 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
9444 // TODO using sandbox on inline html content is not possible yet with current browsers
9445 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
9446 //$s .= $stringtoencode;
9447 //$s .= '</body></html></iframe>';
9448 return $stringtoencode;
9449 } else {
9450 $out = $stringtoencode;
9451
9452 // First clean HTML content
9453 do {
9454 $oldstringtoclean = $out;
9455
9456 $outishtml = 0;
9457 if (dol_textishtml($out)) {
9458 $outishtml = 1;
9459 }
9460
9461 // HTML sanitizer by DOMDocument
9462 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
9463 try {
9464 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
9465 if (LIBXML_VERSION < 20900) {
9466 // Avoid load of external entities (security problem).
9467 // Required only if LIBXML_VERSION < 20900
9468 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
9469 libxml_disable_entity_loader(true);
9470 }
9471
9472 $dom = new DOMDocument();
9473 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
9474 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
9475 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
9476 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
9477 // I don't know what the xml encoding is the trick for
9478 if ($outishtml) {
9479 //$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>';
9480 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">' . $out . '</div></body></html>';
9481 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
9482 } else {
9483 //$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>';
9484 $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>';
9485 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
9486 }
9487
9488 // Note: <a href="https://__[aaa]__/aaa.html"> is transformed into <a href="https://__[aaa]__/aaa.html">
9489 // We don't want that, so we protect __[xxx]__ by replacing [ and ] before loadHTML and restore them after saveHTML
9490 $out = preg_replace_callback(
9491 '/__\[([0-9a-zA-Z_]+)\]__/',
9496 function ($m) {
9497 return '__BRACKETSTART' . $m[1] . 'BRACKETEND__';
9498 },
9499 $out
9500 );
9501
9502 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
9503
9504 $dom->encoding = 'UTF-8';
9505
9506 $out = trim($dom->saveHTML());
9507
9508 // Restore [ and ] that were protected before loadHTML
9509 $out = preg_replace_callback(
9510 '/__BRACKETSTART([0-9a-zA-Z_]+)BRACKETEND__/',
9515 function ($m) {
9516 return '__[' . $m[1] . ']__';
9517 },
9518 $out
9519 );
9520
9521 // Remove the trick added to solve pb with text in utf8 and text without parent tag
9522 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
9523 $out = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]+' . preg_quote('></head><body><div class="tricktoremove">', '/') . '/', '', $out);
9524 $out = preg_replace('/' . preg_quote('</div></body></html>', '/') . '$/', '', trim($out));
9525 //$out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
9526 //$out = preg_replace('/<\/div>$/', '', $out);
9527
9528 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
9529 $out = str_replace('<br>', '', $out);
9530 }
9531 } catch (Exception $e) {
9532 // If error, invalid HTML string with no way to clean it
9533 //print $e->getMessage();
9534 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9535 }
9536 }
9537
9538 // HTML sanitizer by Tidy
9539 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
9540 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
9541 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
9542 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
9543 try {
9544 //var_dump($out);
9545
9546 // Try cleaning using tidy
9547 if (extension_loaded('tidy') && class_exists("tidy")) {
9548 //print "aaa".$out."\n";
9549
9550 // See options at https://tidy.sourceforge.net/docs/quickref.html
9551 $config = array(
9552 'clean' => false,
9553 // 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;
9554 'quote-marks' => false,
9555 'doctype' => 'strict',
9556 'show-body-only' => true,
9557 "indent-attributes" => false,
9558 "vertical-space" => false,
9559 //'ident' => false, // Not always supported
9560 "wrap" => 0,
9561 'preserve-entities' => true
9562 // HTML5 tags
9563 //'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',
9564 //'new-blocklevel-tags' => 'footer header section menu menuitem'
9565 //'new-empty-tags' => 'command embed keygen source track wbr',
9566 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
9567 );
9568
9569 // Tidy
9570 $tidy = new tidy();
9571 $out = $tidy->repairString($out, $config, 'utf8');
9572
9573 //print "xxx".$out;exit;
9574 }
9575
9576 //var_dump($out);
9577 } catch (Exception $e) {
9578 // If error, invalid HTML string with no way to clean it
9579 //print $e->getMessage();
9580 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9581 }
9582 }
9583
9584 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
9585 // TODO $out = preg_replace('/[\x{2000}-\x{200D}\x{FEFF}]/u', ' ', $out);
9586 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
9587
9588 // Clean some html entities that are useless so text is cleaner
9589 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
9590
9591 // Ckeditor uses the numeric entity for apostrophe, so we force it to
9592 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
9593 $out = preg_replace('/&#39;/i', '&apos;', $out);
9594
9595 // 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).
9596 // 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
9597 // using a non conventionnal way to be encoded, to not have them sanitized just after)
9598 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
9599 $out = preg_replace_callback(
9600 '/&#(x?[0-9][0-9a-f]+;?)/i',
9605 static function ($m) {
9606 return realCharForNumericEntities($m);
9607 },
9608 $out
9609 );
9610 }
9611
9612 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
9613 $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'.
9614
9615 // Keep only some html tags and remove also some 'javascript:' strings
9616 if ($check == 'restricthtmlallowlinkscript') {
9617 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
9618 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
9619 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
9620 } elseif ($check == 'restricthtmlallowiframe') {
9621 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
9622 } else {
9623 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
9624 }
9625
9626 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
9627 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
9629 }
9630
9631 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
9632 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
9633 $out = preg_replace('/&apos;/i', "&#39;", $out);
9634
9635 // Now remove js
9636 // 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
9637 $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)>
9638 $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);
9639 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
9640 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
9641 $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);
9642 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
9643 // More not into the previous list
9644 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
9645 } while ($oldstringtoclean != $out);
9646
9647 // Check the limit of external links that are automatically executed in a Rich text content. We count:
9648 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
9649 // 'url(' to avoid inline style like background: url(http...
9650 // '<link' to avoid <link href="http...">
9651 $reg = array();
9652 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
9653 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
9654 $nblinks = count($reg[0]);
9655 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
9656 $out = 'ErrorTooManyLinksIntoHTMLString';
9657 }
9658
9659 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
9660 if ($nblinks > 0) {
9661 $out = 'ErrorHTMLLinksNotAllowed';
9662 }
9663 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
9664 // Refuse any links except it they are to the wrapper document.php or viewimage.php
9665 $nblinks = 0;
9666
9667 // Loop on each url in src= and url(
9668 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
9669
9671
9672 $matches = array();
9673 if (preg_match_all($pattern, $out, $matches)) {
9674 // URLs are into $matches[1] or $matches[2]
9675 $urls = array();
9676 foreach ($matches[1] as $tmpval) {
9677 if (!empty($tmpval)) {
9678 $urls[] = $tmpval;
9679 }
9680 }
9681 foreach ($matches[2] as $tmpval) {
9682 if (!empty($tmpval)) {
9683 $urls[] = $tmpval;
9684 }
9685 }
9686
9687 // Show URLs
9688 $firstexturl = '';
9689 $secondexturl = '';
9690 foreach ($urls as $url) {
9691 $urlok = 0;
9692 $parsedurl = parse_url($url);
9693 if (!empty($parsedurl)) {
9694 if (preg_match('/'.preg_quote($dolibarr_main_url_root, '/').'/', $url)
9695 //&& preg_match('/(document|viewimage)\.php$/', $parsedurl['path']) && preg_match('/modulepart=(media|mycompany)/', $parsedurl['query'])
9696 ) {
9697 $urlok = 1;
9698 }
9699 }
9700 if (!$urlok) {
9701 $nblinks++;
9702 if (empty($firstexturl)) {
9703 $firstexturl = $url;
9704 } elseif (empty($secondexturl)) {
9705 $secondexturl = $url;
9706 }
9707 //echo "Found url = ".$url . "\n";
9708 }
9709 }
9710 if ($nblinks > 0) {
9711 $out = 'ErrorHTMLExternalLinksNotAllowed (Example: '.$firstexturl.($secondexturl ? ' '.$secondexturl : '').')';
9712 }
9713 }
9714 }
9715
9716 return $out;
9717 }
9718}
9719
9740function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9741{
9742 if (is_null($stringtoencode)) {
9743 return '';
9744 }
9745
9746 $newstring = $stringtoencode;
9747 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9748 $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.
9749 if ($removelasteolbr) {
9750 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9751 }
9752 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9753 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9754 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9755 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9756 } else {
9757 if ($removelasteolbr) {
9758 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9759 }
9760 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9761 }
9762 // Other substitutions that htmlentities does not do
9763 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9764 return $newstring;
9765}
9766
9774function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9775{
9776 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9777 $ret = preg_replace('/' . "\r\n" . '<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9778 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\r\n" . '/i', "\r\n", $ret);
9779 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\n" . '/i', "\n", $ret);
9780 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9781 return $ret;
9782}
9783
9790function dol_htmlcleanlastbr($stringtodecode)
9791{
9792 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9793 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|' . "\n" . '|' . "\r" . ')+$/i', "", $ret);
9794 return $ret;
9795}
9796
9806function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9807{
9808 $newstring = $a;
9809 if ($keepsomeentities) {
9810 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9811 }
9812 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9813 if ($keepsomeentities) {
9814 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9815 }
9816 return $newstring;
9817}
9818
9830function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9831{
9832 return htmlentities($string, $flags, $encoding, $double_encode);
9833}
9834
9846function dol_string_is_good_iso($s, $clean = 0)
9847{
9848 $len = dol_strlen($s);
9849 $out = '';
9850 $ok = 1;
9851 for ($scursor = 0; $scursor < $len; $scursor++) {
9852 $ordchar = ord($s[$scursor]);
9853 //print $scursor.'-'.$ordchar.'<br>';
9854 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9855 $ok = 0;
9856 break;
9857 } elseif ($ordchar > 126 && $ordchar < 160) {
9858 $ok = 0;
9859 break;
9860 } elseif ($clean) {
9861 $out .= $s[$scursor];
9862 }
9863 }
9864 if ($clean) {
9865 return $out;
9866 }
9867 return $ok;
9868}
9869
9878function dol_nboflines($s, $maxchar = 0)
9879{
9880 if ($s == '') {
9881 return 0;
9882 }
9883 $arraystring = explode("\n", $s);
9884 $nb = count($arraystring);
9885
9886 return $nb;
9887}
9888
9889
9899function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9900{
9901 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9902 if (dol_textishtml($text)) {
9903 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9904 }
9905
9906 $text = strtr($text, $repTable);
9907 if ($charset == 'UTF-8') {
9908 $pattern = '/(<br[^>]*>)/Uu';
9909 } else {
9910 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9911 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9912 }
9913 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9914
9915 $nblines = (int) floor((count($a) + 1) / 2);
9916 // count possible auto line breaks
9917 if ($maxlinesize) {
9918 foreach ($a as $line) {
9919 if (dol_strlen($line) > $maxlinesize) {
9920 //$line_dec = html_entity_decode(strip_tags($line));
9921 $line_dec = html_entity_decode($line);
9922 if (dol_strlen($line_dec) > $maxlinesize) {
9923 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9924 $nblines += substr_count($line_dec, '\n');
9925 }
9926 }
9927 }
9928 }
9929
9930 unset($a);
9931 return $nblines;
9932}
9933
9942function dol_textishtml($msg, $option = 0)
9943{
9944 if (is_null($msg)) {
9945 return false;
9946 }
9947
9948 if ($option == 1) {
9949 if (preg_match('/<(html|link|script)/i', $msg)) {
9950 return true;
9951 } elseif (preg_match('/<body/i', $msg)) {
9952 return true;
9953 } elseif (preg_match('/<\/textarea/i', $msg)) {
9954 return true;
9955 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9956 return true;
9957 } elseif (preg_match('/<br/i', $msg)) {
9958 return true;
9959 }
9960 return false;
9961 } else {
9962 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9963 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9964 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9965 return true;
9966 } elseif (preg_match('/<\/textarea/i', $msg)) {
9967 return true;
9968 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9969 return true;
9970 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9971 return true;
9972 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9973 return true;
9974 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9975 return true;
9976 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9977 return true; // must accept <img src="http://example.com/aaa.png" />
9978 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9979 return true; // must accept <a href="http://example.com/aaa.png" />
9980 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9981 return true;
9982 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9983 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9984 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9985 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9986 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9987 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
9988 return true; // Html entities numbers in hexa
9989 }
9990
9991 return false;
9992 }
9993}
9994
10009function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
10010{
10011 if (!empty($invert)) {
10012 $tmp = $text1;
10013 $text1 = $text2;
10014 $text2 = $tmp;
10015 }
10016
10017 $ret = '';
10018 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
10019 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
10020 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
10021 return $ret;
10022}
10023
10032function dol_concat($text1, $text2)
10033{
10034 return $text1.$text2;
10035}
10036
10050function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
10051{
10052 global $db, $conf, $mysoc, $user, $extrafields;
10053
10054 $substitutionarray = array();
10055
10056 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
10057 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
10058 // this will include signature content first and then replace var found into content of signature
10059 //var_dump($onlykey);
10060 $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()
10061 $usersignature = $user->signature;
10062 $substitutionarray = array_merge($substitutionarray, array(
10063 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
10064 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
10065 ));
10066
10067 if (is_object($user) && ($user instanceof User)) {
10068 $substitutionarray = array_merge($substitutionarray, array(
10069 '__USER_ID__' => (string) $user->id,
10070 '__USER_LOGIN__' => (string) $user->login,
10071 '__USER_EMAIL__' => (string) $user->email,
10072 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
10073 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
10074 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
10075 '__USER_FAX__' => (string) $user->office_fax,
10076 '__USER_LASTNAME__' => (string) $user->lastname,
10077 '__USER_FIRSTNAME__' => (string) $user->firstname,
10078 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
10079 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
10080 '__USER_JOB__' => (string) $user->job,
10081 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
10082 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
10083 ));
10084 }
10085 }
10086 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
10087 $substitutionarray = array_merge($substitutionarray, array(
10088 '__MYCOMPANY_NAME__' => $mysoc->name,
10089 '__MYCOMPANY_EMAIL__' => $mysoc->email,
10090 '__MYCOMPANY_URL__' => $mysoc->url,
10091 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
10092 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
10093 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
10094 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
10095 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
10096 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
10097 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
10098 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
10099 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
10100 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
10101 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
10102 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
10103 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
10104 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
10105 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
10106 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
10107 '__MYCOMPANY_VATNUMBER__' => $mysoc->tva_intra,
10108 '__MYCOMPANY_ZIP__' => $mysoc->zip,
10109 '__MYCOMPANY_TOWN__' => $mysoc->town,
10110 '__MYCOMPANY_STATE__' => $mysoc->state,
10111 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
10112 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
10113 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
10114 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
10115 ));
10116 }
10117
10118 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
10119 if ($onlykey) {
10120 $substitutionarray['__ID__'] = '__ID__';
10121 $substitutionarray['__REF__'] = '__REF__';
10122 $substitutionarray['__NEWREF__'] = '__NEWREF__';
10123 $substitutionarray['__LABEL__'] = '__LABEL__';
10124 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
10125 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
10126 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
10127 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
10128 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
10129
10130 if (isModEnabled("societe")) { // Most objects are concerned
10131 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
10132 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
10133 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
10134 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
10135 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
10136 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
10137 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
10138 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
10139 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
10140 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
10141 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
10142 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
10143 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
10144 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
10145 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
10146 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
10147 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
10148 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
10149 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
10150 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
10151 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
10152 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
10153 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
10154 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
10155 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
10156 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
10157 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
10158 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
10159 }
10160 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
10161 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
10162 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
10163 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
10164 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
10165 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
10166 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
10167 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
10168 }
10169 // add substitution variables for ticket
10170 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
10171 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
10172 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
10173 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
10174 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
10175 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
10176 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
10177 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
10178 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
10179 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
10180 }
10181
10182 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
10183 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
10184 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
10185 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
10186 }
10187 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
10188 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
10189 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
10190 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
10191 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
10192 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
10193 }
10194 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
10195 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
10196 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
10197 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
10198 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
10199 }
10200 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
10201 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
10202 }
10203 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
10204 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
10205 }
10206 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
10207 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
10208 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
10209 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
10210 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
10211 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
10212 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
10213
10214 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
10215 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
10216 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
10217 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
10218 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
10219
10220 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
10221 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
10222 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
10223 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
10224 }
10225 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
10226 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
10227 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
10228 }
10229 } else {
10230 '@phan-var-force Adherent|Delivery $object';
10232 $substitutionarray['__ID__'] = $object->id;
10233 $substitutionarray['__REF__'] = $object->ref;
10234 $substitutionarray['__NEWREF__'] = $object->newref;
10235 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
10236 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10237 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10238 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
10239 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
10240
10241 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
10242 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
10243 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
10244
10245 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
10246 $date_delivery = null;
10247 if (property_exists($object, 'date_delivery')) {
10248 $date_delivery = $object->date_delivery;
10249 } elseif (property_exists($object, 'delivery_date')) {
10250 $date_delivery = $object->delivery_date;
10251 }
10252 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10253 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
10254 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
10255 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
10256 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
10257 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
10258 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
10259 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
10260 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
10261
10262 // For backward compatibility (deprecated)
10263 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10264 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10265
10266 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10267 $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 : '')) : '');
10268 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
10269
10270 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
10271 '@phan-var-force Adherent $object';
10273 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
10274
10275 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
10276 if (method_exists($object, 'getCivilityLabel')) {
10277 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
10278 }
10279 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
10280 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
10281 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
10282 if (method_exists($object, 'getFullName')) {
10283 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
10284 }
10285 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
10286 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
10287 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
10288 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
10289 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
10290 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
10291 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
10292 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
10293 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
10294 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
10295 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
10296 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
10297 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
10298 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
10299 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
10300 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
10301
10302 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
10303 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
10304 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
10305 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
10306 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
10307 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
10308 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
10309 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
10310 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
10311 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
10312 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
10313 }
10314
10315 if (is_object($object) && $object->element == 'societe') {
10317 '@phan-var-force Societe $object';
10318 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
10319 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
10320 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
10321 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
10322 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
10323 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
10324 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
10325 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
10326 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
10327 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
10328 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
10329 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
10330 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
10331 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
10332 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
10333 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ?: '');
10334 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
10335 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
10336 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
10337 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
10338 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
10339 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
10340 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
10341 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
10342 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
10343 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
10344 } elseif (is_object($object) && is_object($object->thirdparty)) {
10345 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
10346 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
10347 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
10348 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
10349 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
10350 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
10351 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
10352 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
10353 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
10354 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
10355 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
10356 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
10357 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
10358 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ?: '');
10359 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
10360 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
10361 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
10362 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
10363 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
10364 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
10365 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
10366 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
10367 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
10368 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
10369 }
10370
10371 if (is_object($object) && $object->element == 'recruitmentcandidature') {
10372 '@phan-var-force RecruitmentCandidature $object';
10374 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
10375 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10376 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10377 }
10378 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
10379 '@phan-var-force ConferenceOrBoothAttendee $object';
10381 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
10382 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10383 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10384 }
10385
10386 if (is_object($object) && $object->element == 'project') {
10387 '@phan-var-force Project $object';
10389 $substitutionarray['__PROJECT_ID__'] = $object->id;
10390 $substitutionarray['__PROJECT_REF__'] = $object->ref;
10391 $substitutionarray['__PROJECT_NAME__'] = $object->title;
10392 } elseif (is_object($object)) {
10393 $project = null;
10394 if (!empty($object->project)) {
10395 $project = $object->project;
10396 }
10397 if (!is_null($project) && is_object($project)) {
10398 $substitutionarray['__PROJECT_ID__'] = $project->id;
10399 $substitutionarray['__PROJECT_REF__'] = $project->ref;
10400 $substitutionarray['__PROJECT_NAME__'] = $project->title;
10401 } else {
10402 // can substitute variables for project : uses lazy load in "make_substitutions" method
10403 $project_id = 0;
10404 if (!empty($object->fk_project) && $object->fk_project > 0) {
10405 $project_id = $object->fk_project;
10406 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
10407 $project_id = $object->fk_project;
10408 }
10409 if ($project_id > 0) {
10410 // path:class:method:id
10411 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10412 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10413 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10414 }
10415 }
10416 }
10417
10418 if (is_object($object) && $object->element == 'facture') {
10419 '@phan-var-force Facture $object';
10421 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
10422 }
10423 if (is_object($object) && $object->element == 'shipping') {
10424 '@phan-var-force Expedition $object';
10426 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
10427 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
10428 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
10429 }
10430 if (is_object($object) && $object->element == 'reception') {
10431 '@phan-var-force Reception $object';
10433 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
10434 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
10435 }
10436
10437 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
10438 '@phan-var-force Contrat $object';
10440 $dateplannedstart = '';
10441 $datenextexpiration = '';
10442 foreach ($object->lines as $line) {
10443 if ($line->date_start > $dateplannedstart) {
10444 $dateplannedstart = $line->date_start;
10445 }
10446 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
10447 $datenextexpiration = $line->date_end;
10448 }
10449 }
10450 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
10451 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
10452 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
10453
10454 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
10455 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
10456 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
10457 }
10458 // add substitution variables for ticket
10459 if (is_object($object) && $object->element == 'ticket') {
10460 '@phan-var-force Ticket $object';
10462 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
10463 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
10464 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
10465 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
10466 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
10467 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
10468 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
10469 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
10470 $userstat = new User($db);
10471 if ($object->fk_user_assign > 0) {
10472 $userstat->fetch($object->fk_user_assign);
10473 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10474 }
10475
10476 if ($object->fk_user_create > 0) {
10477 $userstat->fetch($object->fk_user_create);
10478 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10479 }
10480 }
10481
10482 // Create dynamic tags for __EXTRAFIELD_FIELD__
10483 if ($object->table_element && $object->id > 0) {
10484 if (!is_object($extrafields)) {
10485 $extrafields = new ExtraFields($db);
10486 }
10487 $extrafields->fetch_name_optionals_label($object->table_element, true);
10488
10489 if ($object->fetch_optionals() > 0) {
10490 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
10491 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
10492 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
10493 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_date($object->array_options['options_' . $key], 'day');
10494 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = dol_print_date($object->array_options['options_' . $key], 'day', 'tzserver', $outputlangs);
10495 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = dol_print_date($object->array_options['options_' . $key], 'dayrfc');
10496 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
10497 $datetime = $object->array_options['options_' . $key];
10498 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
10499 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
10500 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
10501 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
10502 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
10503 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_phone($object->array_options['options_' . $key]);
10504 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
10505 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
10506 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATED__'] = price($object->array_options['options_' . $key]); // For compatibility
10507 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATTED__'] = price($object->array_options['options_' . $key]);
10508 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
10509 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = !empty($object->array_options['options_' . $key]) ? $object->array_options['options_' . $key] : '';
10510 }
10511 }
10512 }
10513 }
10514 }
10515
10516 // Complete substitution array with the url to make online payment
10517 if (empty($substitutionarray['__REF__'])) {
10518 $paymenturl = '';
10519 } else {
10520 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
10521 require_once DOL_DOCUMENT_ROOT . '/core/lib/payments.lib.php';
10522 $outputlangs->loadLangs(array('paypal', 'other'));
10523
10524 $amounttouse = 0;
10525 $typeforonlinepayment = 'free';
10526 if (is_object($object) && $object->element == 'commande') {
10527 $typeforonlinepayment = 'order';
10528 }
10529 if (is_object($object) && $object->element == 'facture') {
10530 $typeforonlinepayment = 'invoice';
10531 }
10532 if (is_object($object) && $object->element == 'member') {
10533 $typeforonlinepayment = 'member';
10534 if (!empty($object->last_subscription_amount)) {
10535 $amounttouse = $object->last_subscription_amount;
10536 }
10537 }
10538 if (is_object($object) && $object->element == 'contrat') {
10539 $typeforonlinepayment = 'contract';
10540 }
10541 if (is_object($object) && $object->element == 'fichinter') {
10542 $typeforonlinepayment = 'ficheinter';
10543 }
10544
10545 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], (float) $amounttouse);
10546 $paymenturl = $url;
10547 }
10548
10549 if ($object->id > 0) {
10550 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
10551 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
10552
10553 // Show structured communication
10554 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
10555 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions_be.lib.php';
10556 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
10557 }
10558
10559 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
10560 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10561 } else {
10562 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
10563 }
10564 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
10565 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
10566 } else {
10567 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
10568 }
10569 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
10570 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
10571 } else {
10572 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
10573 }
10574 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
10575 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
10576 } else {
10577 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
10578 }
10579 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
10580 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
10581 } else {
10582 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
10583 }
10584 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
10585 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10586 } else {
10587 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
10588 }
10589
10590 if (is_object($object) && $object->element == 'propal') {
10591 '@phan-var-force Propal $object';
10593 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/comm/propal/card.php?id=" . $object->id;
10594 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10595 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
10596 }
10597 if (is_object($object) && $object->element == 'commande') {
10598 '@phan-var-force Commande $object';
10600 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT . "/commande/card.php?id=" . $object->id;
10601 }
10602 if (is_object($object) && $object->element == 'facture') {
10603 '@phan-var-force Facture $object';
10605 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT . "/compta/facture/card.php?id=" . $object->id;
10606 }
10607 if (is_object($object) && $object->element == 'contrat') {
10608 '@phan-var-force Contrat $object';
10610 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT . "/contrat/card.php?id=" . $object->id;
10611 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10612 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
10613 }
10614 if (is_object($object) && $object->element == 'fichinter') {
10615 '@phan-var-force Fichinter $object';
10617 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT . "/fichinter/card.php?id=" . $object->id;
10618 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10619 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
10620 }
10621 if (is_object($object) && $object->element == 'supplier_proposal') {
10622 '@phan-var-force SupplierProposal $object';
10624 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/supplier_proposal/card.php?id=" . $object->id;
10625 }
10626 if (is_object($object) && $object->element == 'invoice_supplier') {
10627 '@phan-var-force FactureFournisseur $object';
10629 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT . "/fourn/facture/card.php?id=" . $object->id;
10630 }
10631 if (is_object($object) && $object->element == 'payment_supplier') {
10632 '@phan-var-force PaiementFourn $object';
10634 //print_r($object);
10635 $liste_factures = [];
10636 $total = 0;
10637
10638 $sql = 'SELECT f.ref,f.multicurrency_code as f_mccode, pf.*
10639 FROM '.MAIN_DB_PREFIX.'paiementfourn_facturefourn as pf
10640 JOIN '.MAIN_DB_PREFIX.'facture_fourn as f ON pf.fk_facturefourn = f.rowid
10641 WHERE pf.fk_paiementfourn = '.((int) $object->id);
10642
10643 $resql = $db->query($sql);
10644 if ($resql) {
10645 while ($objp = $db->fetch_object($resql)) {
10646 $liste_factures[] = ' - '.$outputlangs->trans('Invoice').' '. $objp->ref.' '.$outputlangs->trans('AmountPayed').' '.price($objp->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $objp->multicurrency_code);
10647 }
10648 }
10649 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_LIST__'] = implode("\n", $liste_factures);
10650 ;
10651 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_TOTAL__'] = price($object->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $object->multicurrency_code ? $object->multicurrency_code : $conf->currency);
10652 }
10653 if (is_object($object) && $object->element == 'shipping') {
10654 '@phan-var-force Expedition $object';
10656 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT . "/expedition/card.php?id=" . $object->id;
10657 }
10658 }
10659
10660 if (is_object($object) && $object->element == 'action') {
10661 '@phan-var-force ActionComm $object';
10663 $substitutionarray['__EVENT_LABEL__'] = $object->label;
10664 $substitutionarray['__EVENT_DESCRIPTION__'] = $object->note;
10665 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action" . $object->type_code);
10666 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
10667 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
10668 $substitutionarray['__EVENT_DATE_TZUSER__'] = dol_print_date($object->datep, 'day', 'tzuserrel', $outputlangs);
10669 $substitutionarray['__EVENT_TIME_TZUSER__'] = dol_print_date($object->datep, 'hour', 'tzuserrel', $outputlangs);
10670 }
10671 }
10672 }
10673
10674 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
10675 '@phan-var-force Facture|FactureRec $object';
10677 include_once DOL_DOCUMENT_ROOT . '/core/lib/functionsnumtoword.lib.php';
10678
10679 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
10680 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
10681 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
10682 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
10683
10684 $already_payed_all = 0;
10685 if (is_object($object) && ($object instanceof Facture)) {
10686 $already_payed_all = $object->totalpaid + $object->totaldeposits + $object->totalcreditnotes;
10687 }
10688
10689 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
10690 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
10691 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
10692
10693 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
10694 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
10695 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
10696
10697 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
10698
10699 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10700 $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)) : '';
10701 $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)) : '';
10702
10703 $mysocuselocaltax1 = false;
10704 $mysocuselocaltax2 = false;
10705 if ($mysoc instanceof Societe && !empty($mysoc->country_code)) {
10706 $tmparray = $mysoc->useLocalTax(-1);
10707 $mysocuselocaltax1 = $tmparray[1];
10708 $mysocuselocaltax2 = $tmparray[2];
10709 }
10710
10711 // Local taxes
10712 if ($onlykey != 2 || $mysocuselocaltax1) {
10713 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
10714 }
10715 if ($onlykey != 2 || $mysocuselocaltax2) {
10716 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
10717 }
10718
10719 // Amount keys formatted in a currency
10720 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10721 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10722 $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) : '';
10723 $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)) : '';
10724 if ($onlykey != 2 || $mysocuselocaltax1) {
10725 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10726 }
10727 if ($onlykey != 2 || $mysocuselocaltax2) {
10728 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10729 }
10730 // Amount keys formatted in a currency (with the typo error for backward compatibility)
10731 if ($onlykey != 2) {
10732 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
10733 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
10734 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
10735 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
10736 if ($mysocuselocaltax1) {
10737 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
10738 }
10739 if ($mysoc->useLocalTax2) {
10740 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
10741 }
10742 }
10743
10744 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
10745 $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) : '';
10746 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
10747 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
10748 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
10749 // TODO Add other keys for foreign multicurrency
10750
10751 // For backward compatibility
10752 if ($onlykey != 2) {
10753 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
10754 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
10755 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10756 }
10757 }
10758
10759
10760 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10761 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
10762
10763 $now = dol_now();
10764
10765 $tmp = dol_getdate($now, true);
10766 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10767 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10768 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10769 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10770
10771 $daytext = $outputlangs->trans('Day' . $tmp['wday']);
10772
10773 $substitutionarray = array_merge($substitutionarray, array(
10774 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10775 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10776 '__DAY__' => (string) $tmp['mday'],
10777 '__DAY_TEXT__' => $daytext, // Monday
10778 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10779 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10780 '__MONTH__' => (string) $tmp['mon'],
10781 '__MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp['mon'])),
10782 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp['mon'])),
10783 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp['mon'])),
10784 '__YEAR__' => (string) $tmp['year'],
10785 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10786 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10787 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10788 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10789 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp3['month'])),
10790 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp3['month'])),
10791 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp3['month'])),
10792 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10793 '__NEXT_DAY__' => (string) $tmp4['day'],
10794 '__NEXT_MONTH__' => (string) $tmp5['month'],
10795 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp5['month'])),
10796 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp5['month'])),
10797 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp5['month'])),
10798 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10799 ));
10800 }
10801
10802 if (isModEnabled('multicompany')) {
10803 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10804 }
10805 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10806 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10807 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10808 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey') . ' (load also language file before)';
10809 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10810 }
10811
10812 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10813
10814 return $substitutionarray;
10815}
10816
10833function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10834{
10835 global $db, $langs;
10836
10837 if (!is_array($substitutionarray)) {
10838 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10839 }
10840
10841 if (empty($outputlangs)) {
10842 $outputlangs = $langs;
10843 }
10844
10845 // Is initial text HTML or simple text ?
10846 $msgishtml = 0;
10847 if (dol_textishtml($text, 1)) {
10848 $msgishtml = 1;
10849 }
10850
10851 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10852 if (is_object($outputlangs)) {
10853 $reg = array();
10854 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10855 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10856 $tmp = explode('|', $reg[1]);
10857 if (!empty($tmp[1])) {
10858 $outputlangs->load($tmp[1]);
10859 }
10860
10861 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10862
10863 if (empty($converttextinhtmlifnecessary)) {
10864 // convert $newval into HTML is necessary
10865 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10866 } else {
10867 if (! $msgishtml) {
10868 $valueishtml = dol_textishtml($value, 1);
10869 //var_dump("valueishtml=".$valueishtml);
10870
10871 if ($valueishtml) {
10872 $text = dol_htmlentitiesbr($text);
10873 $msgishtml = 1;
10874 }
10875 } else {
10876 $value = dol_nl2br((string) $value);
10877 }
10878
10879 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $value, $text);
10880 }
10881 }
10882 }
10883
10884 // Make substitution for constant keys.
10885 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10886 $reg = array();
10887 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10888 $originalkeyfound = $reg[1];
10889 $keyfound = preg_replace('/\|urlencode$/', '', $originalkeyfound);
10890
10891 if (isASecretKey($keyfound)) {
10892 $value = '*****forbidden*****';
10893 } else {
10894 $value = getDolGlobalString($keyfound);
10895 // Execute some functions on value of substitution key
10896 if (preg_match('/\|urlencode$/', $originalkeyfound)) {
10897 $value = urlencode($value);
10898 }
10899 }
10900
10901 if (empty($converttextinhtmlifnecessary)) {
10902 // convert $newval into HTML is necessary
10903 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10904 } else {
10905 if (! $msgishtml) {
10906 $valueishtml = dol_textishtml($value, 1);
10907
10908 if ($valueishtml) {
10909 $text = dol_htmlentitiesbr($text);
10910 $msgishtml = 1;
10911 }
10912 } else {
10913 $value = dol_nl2br((string) $value);
10914 }
10915
10916 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $value, $text);
10917 }
10918 }
10919
10920 // Make substitution for array $substitutionarray
10921 foreach ($substitutionarray as $key => $value) {
10922 if (!isset($value)) {
10923 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10924 }
10925
10926 if (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN') && ($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__')) {
10927 $value = ''; // Protection
10928 }
10929
10930 if (empty($converttextinhtmlifnecessary)) {
10931 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10932 } else {
10933 if (! $msgishtml) {
10934 $valueishtml = dol_textishtml($value, 1);
10935
10936 if ($valueishtml) {
10937 $text = dol_htmlentitiesbr($text);
10938 $msgishtml = 1;
10939 }
10940 } else {
10941 $value = dol_nl2br((string) $value);
10942 }
10943 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10944 }
10945 }
10946
10947 // TODO Implement the lazyload substitution
10948 /*
10949 add a loop to scan $substitutionarray:
10950 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.
10951 If no, we don't need to make replacement, so we do nothing.
10952 If yes, we can make the substitution:
10953
10954 include_once $path;
10955 $tmpobj = new $class($db);
10956 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10957 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10958 */
10959 $memory_object_list = array();
10960 foreach ($substitutionarray as $key => $value) {
10961 $lazy_load_arr = array();
10962 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10963 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10964 $key_to_substitute = $lazy_load_arr[1];
10965 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10966 $param_arr = explode(':', (string) $value);
10967 // path:class:method:id
10968 if (count($param_arr) == 4) {
10969 $path = $param_arr[0];
10970 $class = $param_arr[1];
10971 $method = $param_arr[2];
10972 $id = (int) $param_arr[3];
10973
10974 // load class file and init object list in memory
10975 if (!isset($memory_object_list[$class])) {
10976 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10977 require_once DOL_DOCUMENT_ROOT . $path;
10978 if (class_exists($class)) {
10979 $memory_object_list[$class] = array(
10980 'list' => array(),
10981 );
10982 }
10983 }
10984 }
10985
10986 // fetch object and set substitution
10987 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
10988 if (method_exists($class, $method)) {
10989 if (!isset($memory_object_list[$class]['list'][$id])) {
10990 $tmpobj = new $class($db);
10991 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10992 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
10993 $memory_object_list[$class]['list'][$id] = $tmpobj;
10994 } else {
10995 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10996 $tmpobj = $memory_object_list[$class]['list'][$id];
10997 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10998 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
10999 }
11000
11001 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
11002 }
11003 }
11004 }
11005 }
11006 }
11007 }
11008 }
11009
11010 return $text;
11011}
11012
11025function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
11026{
11027 global $conf, $user;
11028
11029 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
11030
11031 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
11032
11033 // Check if there is external substitution to do, requested by plugins
11034 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
11035
11036 foreach ($dirsubstitutions as $reldir) {
11037 $dir = dol_buildpath($reldir, 0);
11038
11039 // Check if directory exists
11040 if (!dol_is_dir($dir)) {
11041 continue;
11042 }
11043
11044 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
11045 foreach ($substitfiles as $substitfile) {
11046 $reg = array();
11047 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
11048 $module = $reg[1];
11049
11050 dol_syslog("Library " . $substitfile['name'] . " found into " . $dir);
11051 // Include the user's functions file
11052 require_once $dir . $substitfile['name'];
11053 // Call the user's function, and only if it is defined
11054 $function_name = $module . "_" . $callfunc;
11055 if (function_exists($function_name)) {
11056 $function_name($substitutionarray, $outputlangs, $object, $parameters);
11057 }
11058 }
11059 }
11060 }
11061 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
11062 // to list all tags in odt template
11063 $tags = '';
11064 foreach ($substitutionarray as $key => $value) {
11065 $tags .= '{' . $key . '} => ' . $value . "\n";
11066 }
11067 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
11068 }
11069}
11070
11080function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
11081{
11082 print get_date_range($date_start, $date_end, $format, $outputlangs);
11083}
11084
11095function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
11096{
11097 global $langs;
11098
11099 $out = '';
11100
11101 if (!is_object($outputlangs)) {
11102 $outputlangs = $langs;
11103 }
11104
11105 if ($date_start && $date_end) {
11106 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11107 }
11108 if ($date_start && !$date_end) {
11109 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11110 }
11111 if (!$date_start && $date_end) {
11112 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11113 }
11114
11115 return $out;
11116}
11117
11126function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
11127{
11128 $ret = '';
11129 // If order not defined, we use the setup
11130 if ($nameorder < 0) {
11131 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
11132 }
11133 if ($nameorder == 1) {
11134 $ret .= $firstname;
11135 if ($firstname && $lastname) {
11136 $ret .= ' ';
11137 }
11138 $ret .= $lastname;
11139 } elseif ($nameorder == 2 || $nameorder == 3) {
11140 $ret .= $firstname;
11141 if (empty($ret) && $nameorder == 3) {
11142 $ret .= $lastname;
11143 }
11144 } else { // 0, 4 or 5
11145 $ret .= $lastname;
11146 if (empty($ret) && $nameorder == 5) {
11147 $ret .= $firstname;
11148 }
11149 if ($nameorder == 0) {
11150 if ($firstname && $lastname) {
11151 $ret .= ' ';
11152 }
11153 $ret .= $firstname;
11154 }
11155 }
11156 return $ret;
11157}
11158
11159
11172function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
11173{
11174 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
11175 if (!is_array($mesgs)) {
11176 $mesgs = trim((string) $mesgs);
11177 // If mesgs is a not an empty string
11178 if ($mesgs) {
11179 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
11180 return;
11181 }
11182 if ($attop) {
11183 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11184 } else {
11185 $_SESSION['dol_events'][$style][] = $mesgs;
11186 }
11187 }
11188 } else {
11189 // If mesgs is an array
11190 foreach ($mesgs as $mesg) {
11191 $mesg = trim((string) $mesg);
11192 if ($mesg) {
11193 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
11194 return;
11195 }
11196 if ($attop) {
11197 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11198 } else {
11199 $_SESSION['dol_events'][$style][] = $mesg;
11200 }
11201 }
11202 }
11203 }
11204}
11205
11219function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
11220{
11221 if (empty($mesg) && empty($mesgs)) {
11222 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
11223 } else {
11224 if ($messagekey) {
11225 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
11226 // TODO
11227 $mesg .= '';
11228 }
11229 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE" . $messagekey])) {
11230 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
11231 dol_print_error(null, 'Bad parameter style=' . $style . ' for setEventMessages');
11232 }
11233 if (empty($mesgs)) {
11234 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
11235 } else {
11236 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
11237 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
11238 }
11239 setEventMessage($mesgs, $style, $noduplicate, $attop);
11240 }
11241 }
11242 }
11243}
11244
11254function dol_htmloutput_events($disabledoutputofmessages = 0)
11255{
11256 // Show mesgs
11257 if (isset($_SESSION['dol_events']['mesgs'])) {
11258 if (empty($disabledoutputofmessages)) {
11259 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
11260 }
11261 unset($_SESSION['dol_events']['mesgs']);
11262 }
11263 // Show errors
11264 if (isset($_SESSION['dol_events']['errors'])) {
11265 if (empty($disabledoutputofmessages)) {
11266 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
11267 }
11268 unset($_SESSION['dol_events']['errors']);
11269 }
11270
11271 // Show warnings
11272 if (isset($_SESSION['dol_events']['warnings'])) {
11273 if (empty($disabledoutputofmessages)) {
11274 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
11275 }
11276 unset($_SESSION['dol_events']['warnings']);
11277 }
11278}
11279
11294function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
11295{
11296 global $conf, $langs;
11297
11298 $ret = 0;
11299 $return = '';
11300 $out = '';
11301 $divstart = $divend = '';
11302
11303 // If inline message with no format, we add it.
11304 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
11305 $divstart = '<div class="' . $style . ' clearboth">';
11306 $divend = '</div>';
11307 }
11308
11309 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
11310 $langs->load("errors");
11311 $out .= $divstart;
11312 if (is_array($mesgarray) && count($mesgarray)) {
11313 foreach ($mesgarray as $message) {
11314 $ret++;
11315 $out .= $langs->trans($message);
11316 if ($ret < count($mesgarray)) {
11317 $out .= "<br>\n";
11318 }
11319 }
11320 }
11321 if ($mesgstring) {
11322 $ret++;
11323 $out .= $langs->trans($mesgstring);
11324 }
11325 $out .= $divend;
11326 }
11327
11328 if ($out) {
11329 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
11330 $return = '<script nonce="' . getNonce() . '">
11331 $(document).ready(function() {
11332 /* jnotify(message, preset of message type, keepmessage) */
11333 $.jnotify("' . dol_escape_js($out) . '", "' . ($style == "ok" ? 3000 : $style) . '", ' . ($style == "ok" ? "false" : "true") . ',{ remove: function (){} } );
11334 });
11335 </script>';
11336 } else {
11337 $return = $out;
11338 }
11339 }
11340
11341 return $return;
11342}
11343
11355function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11356{
11357 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11358}
11359
11373function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
11374{
11375 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
11376 return;
11377 }
11378
11379 $iserror = 0;
11380 $iswarning = 0;
11381 if (is_array($mesgarray)) {
11382 foreach ($mesgarray as $val) {
11383 if ($val && preg_match('/class="error"/i', $val)) {
11384 $iserror++;
11385 break;
11386 }
11387 if ($val && preg_match('/class="warning"/i', $val)) {
11388 $iswarning++;
11389 break;
11390 }
11391 }
11392 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
11393 $iserror++;
11394 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
11395 $iswarning++;
11396 }
11397 if ($style == 'error' || $style == 'errors') {
11398 $iserror++;
11399 }
11400 if ($style == 'warning' || $style == 'warnings') {
11401 $iswarning++;
11402 }
11403
11404 if ($iserror || $iswarning) {
11405 // Remove div from texts
11406 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
11407 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
11408 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
11409 // Remove div from texts array
11410 if (is_array($mesgarray)) {
11411 $newmesgarray = array();
11412 foreach ($mesgarray as $val) {
11413 if (is_string($val)) {
11414 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
11415 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
11416 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
11417 $newmesgarray[] = $tmpmesgstring;
11418 } else {
11419 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
11420 }
11421 }
11422 $mesgarray = $newmesgarray;
11423 }
11424 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
11425 } else {
11426 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
11427 }
11428}
11429
11441function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11442{
11443 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11444}
11445
11466function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
11467{
11468 // Clean parameters
11469 $order = strtolower($order);
11470
11471 if (is_array($array)) {
11472 $sizearray = count($array);
11473 if ($sizearray > 0) {
11474 // Build a temp array with sorting key as value
11475 $temp = array();
11476 foreach (array_keys($array) as $key) {
11477 $tmpmultikey = explode(',', $index);
11478 $newindex = $tmpmultikey[0];
11479 if (is_object($array[$key])) {
11480 $temp[$key] = empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex;
11481 // Add other keys
11482 if (!empty($tmpmultikey[1])) {
11483 $newindex = $tmpmultikey[1];
11484 $temp[$key] .= '__' . (empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex);
11485 }
11486 } else {
11487 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11488 $temp[$key] = empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex];
11489 // Add other keys
11490 if (!empty($tmpmultikey[1])) {
11491 $newindex = $tmpmultikey[1];
11492 // @phan-suppress-next-line PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11493 $temp[$key] .= '__' . (empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex]);
11494 }
11495 }
11496 if ($natsort == -1) {
11497 $temp[$key] = '___' . $temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
11498 }
11499 }
11500 if (empty($natsort) || $natsort == -1) {
11501 if ($order == 'asc') {
11502 asort($temp);
11503 } else {
11504 arsort($temp);
11505 }
11506 } else {
11507 if ($case_sensitive) {
11508 natsort($temp);
11509 } else {
11510 natcasesort($temp); // natecasesort is not sensible to case
11511 }
11512 if ($order != 'asc') {
11513 $temp = array_reverse($temp, true);
11514 }
11515 }
11516
11517 $sorted = array();
11518
11519 foreach (array_keys($temp) as $key) {
11520 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
11521 }
11522
11523 return $sorted;
11524 }
11525 }
11526 return $array;
11527}
11528
11529
11537function utf8_check($str)
11538{
11539 $str = (string) $str; // Sometimes string is an int.
11540
11541 // We must use here a binary strlen function (so not dol_strlen)
11542 $strLength = strlen($str);
11543 for ($i = 0; $i < $strLength; $i++) {
11544 if (ord($str[$i]) < 0x80) {
11545 continue; // 0bbbbbbb
11546 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
11547 $n = 1; // 110bbbbb
11548 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
11549 $n = 2; // 1110bbbb
11550 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
11551 $n = 3; // 11110bbb
11552 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
11553 $n = 4; // 111110bb
11554 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
11555 $n = 5; // 1111110b
11556 } else {
11557 return false; // Does not match any model
11558 }
11559 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
11560 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
11561 return false;
11562 }
11563 }
11564 }
11565 return true;
11566}
11567
11575function utf8_valid($str)
11576{
11577 /* 2 other methods to test if string is utf8
11578 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
11579 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
11580 */
11581 return preg_match('//u', $str) ? true : false;
11582}
11583
11584
11591function ascii_check($str)
11592{
11593 if (function_exists('mb_check_encoding')) {
11594 //if (mb_detect_encoding($str, 'ASCII', true) return false;
11595 if (!mb_check_encoding($str, 'ASCII')) {
11596 return false;
11597 }
11598 } else {
11599 if (preg_match('/[^\x00-\x7f]/', $str)) {
11600 return false; // Contains a byte > 7f
11601 }
11602 }
11603
11604 return true;
11605}
11606
11607
11615function dol_osencode($str)
11616{
11617 $tmp = ini_get("unicode.filesystem_encoding");
11618 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
11619 $tmp = 'iso-8859-1'; // By default for windows
11620 }
11621 if (empty($tmp)) {
11622 $tmp = 'utf-8'; // By default for other
11623 }
11624 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
11625 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
11626 }
11627
11628 if ($tmp == 'iso-8859-1') {
11629 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
11630 }
11631 return $str;
11632}
11633
11634
11650function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
11651{
11652 global $conf;
11653
11654 // If key empty
11655 if ($key == '') {
11656 return 0;
11657 }
11658
11659 // Check in cache
11660 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
11661 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
11662 }
11663
11664 dol_syslog('dol_getIdFromCode (value for field ' . $fieldid . ' from key ' . $key . ' not found into cache)', LOG_DEBUG);
11665
11666 $sql = "SELECT " . $fieldid . " as valuetoget";
11667 $sql .= " FROM " . MAIN_DB_PREFIX . $tablename;
11668 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
11669 $sql .= " WHERE " . $fieldkey . " = " . ((int) $key);
11670 } else {
11671 $sql .= " WHERE " . $fieldkey . " = '" . $db->escape($key) . "'";
11672 }
11673 if (!empty($entityfilter)) {
11674 $sql .= " AND entity IN (" . getEntity($tablename) . ")";
11675 }
11676 if ($filters) {
11677 $sql .= $filters;
11678 }
11679
11680 $resql = $db->query($sql);
11681 if ($resql) {
11682 $obj = $db->fetch_object($resql);
11683 $valuetoget = '';
11684 if ($obj) {
11685 $valuetoget = $obj->valuetoget;
11686 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
11687 } else {
11688 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
11689 }
11690 $db->free($resql);
11691
11692 return $valuetoget;
11693 } else {
11694 return -1;
11695 }
11696}
11697
11707function isStringVarMatching($var, $regextext, $matchrule = 1)
11708{
11709 // Tolerate callers (custom modules, older code) that already pass a full regex with delimiters
11710 // like '/^(aaa|bbb)/' instead of the bare body. Without this, the function would build
11711 // '/^/^(aaa|bbb)//' which trips preg_match() with 'Unknown modifier ^'.
11712 $regextext = preg_replace('#^/\^?#', '', (string) $regextext);
11713 $regextext = preg_replace('#\$?/[imsxuADSUXJ]*$#', '', $regextext);
11714
11715 if ($matchrule == 1) {
11716 if ($var == 'mainmenu') {
11717 global $mainmenu;
11718 return (preg_match('/^' . $regextext . '/', $mainmenu));
11719 } elseif ($var == 'leftmenu') {
11720 global $leftmenu;
11721 return (preg_match('/^' . $regextext . '/', $leftmenu));
11722 } else {
11723 return 'This variable is not accessible with dol_eval';
11724 }
11725 } else {
11726 return 'This value '.$matchrule.' for param $matchrule is not yet implemented';
11727 }
11728}
11729
11730
11740function verifCond($strToEvaluate, $onlysimplestring = '1')
11741{
11742 //print $strToEvaluate."<br>\n";
11743 $rights = true;
11744 if (isset($strToEvaluate) && $strToEvaluate !== '') {
11745 //var_dump($strToEvaluate);
11746 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
11747 $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
11748 //var_dump($strToEvaluate, $rep);
11749 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
11750 //var_dump($rights);
11751 }
11752 return $rights;
11753}
11754
11769function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11770{
11771 if ($returnvalue != 1) {
11772 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
11773 }
11774
11775 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
11776 return dol_eval_new($s);
11777 } else {
11778 return dol_eval_standard($s, $hideerrors, $onlysimplestring);
11779 }
11780}
11781
11791function dol_eval_new($s)
11792{
11793 // Only this global variables can be read by eval function and returned to caller
11794 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11795 $db, $langs, $user, $website, $websitepage,
11796 $action, $mainmenu, $leftmenu,
11797 $mysoc,
11798 $objectoffield, // To allow the use of $objectoffield in computed fields
11799
11800 // Old variables used
11801 $object,
11802 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11803
11804 // PHP < 7.4.0
11805 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11806 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11807
11808 // PHP < 8.0.0
11809 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11810 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11811 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11812 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11813 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11814
11815 // PHP < 8.1.0
11816 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11817 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11818 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11819 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11820
11821 // PHP < 8.4.0
11822 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11823 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11824 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11825
11826 $prohibited_token_ids = [
11827 /*
11828 * Prohibited int tokens
11829 */
11830
11831 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11832 'T_ABSTRACT',
11833 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
11834 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
11835 'T_ATTRIBUTE',
11836 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11837 'T_BAD_CHARACTER',
11838 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11839 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11840 'T_CALLABLE',
11841 'T_CATCH',
11842 'T_CLASS',
11843 'T_CLOSE_TAG',
11844 'T_CONST',
11845 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11846 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11847 'T_DECLARE',
11848 'T_DIR',
11849 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11850 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11851 'T_ECHO',
11852 'T_ENDDECLARE',
11853 'T_ENUM',
11854 'T_EVAL',
11855 'T_EXIT',
11856 'T_EXTENDS',
11857 // 'T_FOR', 'T_FOREACH',
11858 'T_FILE',
11859 'T_FINAL',
11860 'T_FINALLY',
11861 'T_FN',
11862 'T_FUNCTION',
11863 'T_FUNC_C',
11864 'T_GLOBAL',
11865 'T_GOTO',
11866 'T_HALT_COMPILER',
11867 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11868 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11869 'T_IMPLEMENTS',
11870 'T_INCLUDE',
11871 'T_INCLUDE_ONCE',
11872 'T_INSTEADOF',
11873 'T_INTERFACE',
11874 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11875 'T_LINE',
11876 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11877 'T_METHOD_C',
11878 // 'T_NEW',
11879 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11880 'T_NAMESPACE',
11881 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11882 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11883 'T_OPEN_TAG',
11884 'T_OPEN_TAG_WITH_ECHO',
11885 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11886 'T_PRINT',
11887 'T_PRIVATE',
11888 'T_PROTECTED',
11889 'T_PUBLIC',
11890 // 'T_PROPERTY_C',
11891 'T_READONLY',
11892 'T_REQUIRE',
11893 'T_REQUIRE_ONCE',
11894 'T_RETURN',
11895 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11896 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11897 'T_STATIC',
11898 'T_THROW',
11899 'T_TRAIT',
11900 'T_TRAIT_C',
11901 'T_TRY',
11902 'T_UNSET',
11903 'T_UNSET_CAST',
11904 'T_USE',
11905 // 'T_VARIABLE',
11906 'T_VAR',
11907 // 'T_WHILE', 'T_WHITESPACE',
11908 // 'T_XOR_EQUAL',
11909 // 'T_YIELD', 'T_YIELD_FROM',
11910
11911 /*
11912 * Prohibited string tokens
11913 */
11914 ';',
11915 '`',
11916 ];
11917
11918 $prohibited_variables = [
11919 '$_COOKIE',
11920 '$_ENV',
11921 '$_FILES',
11922 '$GLOBALS',
11923 '$_GET',
11924 '$_POST',
11925 '$_REQUEST',
11926 '$_SERVER',
11927 '$_SESSION',
11928 ];
11929
11930 $prohibited_functions = [
11931 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11932 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11933 'override_function',
11934 'session_id',
11935 'session_create_id',
11936 'session_regenerate_id',
11937 'call_user_func',
11938 'call_user_func_array', // PREVENT calling forbidden functions
11939 'exec',
11940 'passthru',
11941 'shell_exec',
11942 'system',
11943 'proc_open',
11944 'popen',
11945 'dol_eval',
11946 'dol_eval_new',
11947 'dol_eval_standard',
11948 'dol_contctdesc',
11949 'executeCLI',
11950 'verifCond',
11951 'GETPOST', // Native Dolibarr functions
11952 'create_function',
11953 'assert',
11954 'mb_ereg_replace',
11955 'mb_eregi_replace', // function with eval capabilities
11956 'dol_compress_dir',
11957 'dol_decode',
11958 'dol_delete_file',
11959 'dol_delete_dir',
11960 'dol_delete_dir_recursive',
11961 'dol_copy',
11962 'archiveOrBackupFile', // more dolibarr functions
11963 'fopen',
11964 'file_put_contents',
11965 'fputs',
11966 'fputscsv',
11967 'fwrite',
11968 'fpassthru',
11969 'mkdir',
11970 'rmdir',
11971 'symlink',
11972 'touch',
11973 'unlink',
11974 'umask', // PHP functions related to file operations
11975 'invoke',
11976 'invokeArgs', // Method of ReflectionFunction to execute a function
11977 'filter_input',
11978 'filter_input_array',
11979 'GETPOST', // PREVENT CODE INJECTION
11980 ];
11981
11982 $prohibited_token_arrangements = [
11983 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11984 ' T_VARIABLE ( ',
11985 ' " ( ',
11986 ' \' ( ',
11987 ' T_CONSTANT_ENCAPSED_STRING ( ',
11988 ' ) ( ',
11989 ];
11990
11991 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
11992
11993 $tokens_arrangement = ' ';
11994
11995 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
11996 if (is_array($tokens[$i])) {
11997 $token_id = $tokens[$i][0];
11998 $token_value = $tokens[$i][1];
11999 $token_name = token_name($tokens[$i][0]);
12000 } else {
12001 $token_id = $tokens[$i];
12002 $token_value = $tokens[$i];
12003 $token_name = $tokens[$i];
12004 }
12005
12006 // Ignore whitespaces
12007 if (T_WHITESPACE === $token_id) {
12008 continue;
12009 }
12010
12011 // Keep history to check arrangements
12012 $tokens_arrangement .= "{$token_name} ";
12013
12014 // Prohibited Variables
12015 if (
12016 T_VARIABLE === $token_id
12017 && in_array($token_value, $prohibited_variables, true)
12018 ) {
12019 return "« {$token_value} » is prohibited in « {$s} »";
12020 }
12021
12022 // Prohibited Functions
12023 if (
12024 T_STRING === $token_id
12025 && in_array($token_value, $prohibited_functions, true)
12026 ) {
12027 return "« {$token_value} » is prohibited in « {$s} »";
12028 }
12029 }
12030
12031 // Prohibited Token IDs
12032 $maxi = count($prohibited_token_ids);
12033 for ($i = 0; $i < $maxi; ++$i) {
12034 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
12035 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
12036 }
12037 }
12038
12039 // Prohibited token arrangements
12040 $maxi = count($prohibited_token_arrangements);
12041 for ($i = 0; $i < $maxi; ++$i) {
12042 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
12043 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
12044 }
12045 }
12046
12047 // Return result
12048 try {
12049 return @eval("return {$s};") ?? '';
12050 } catch (Throwable $ex) {
12051 return "Exception during evaluation: " . $s . " - " . $ex->getMessage();
12052 }
12053}
12054
12068function dol_eval_standard($s, $hideerrors = 1, $onlysimplestring = '1')
12069{
12070 // Only this global variables can be read by eval function and returned to caller
12071 // The less we have, the better it is.
12072
12073 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()
12074 global $db, $langs, $user, $website, $websitepage;
12075 global $action, $mainmenu, $leftmenu;
12076 global $mysoc;
12077 global $objectoffield; // To allow the use of $objectoffield in computed fields
12078 global $object;
12079
12080 // Old variables (deprecated)
12081 if (getDolGlobalString('MAIN_ALLOW_OLD_VAR_OBJ_IN_DOL_EVAL')) {
12082 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $objectoffield
12083 }
12084
12085 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
12086 if ($onlysimplestring == '0') { // '0' is deprecated, we process it as the more secured '1'
12087 $onlysimplestring = '1';
12088 }
12089 if (!in_array($onlysimplestring, array('1', '2'))) {
12090 return "Bad call of dol_eval. Parameter onlysimplestring must be '1' or '2'.";
12091 }
12092 if (!is_scalar($s)) {
12093 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
12094 }
12095
12096 try {
12097 global $dolibarr_main_restrict_eval_methods;
12098
12099 // Set $dolibarr_main_restrict_eval_methods_array
12100 if (!isset($dolibarr_main_restrict_eval_methods)) {
12101 $dolibarr_main_restrict_eval_methods = 'getDolGlobalString, getDolGlobalInt, getDolCurrency, getDolEntity, getDolDBType, fetchNoCompute, hasRight, isAdmin, isModEnabled, isStringVarMatching, abs, min, max, round, dol_now, preg_match';
12102 }
12103 //print '$dolibarr_main_restrict_eval_methods = '.$dolibarr_main_restrict_eval_methods."\n";
12104 $dolibarr_main_restrict_eval_methods_array = explode(',', str_replace(" ", "", $dolibarr_main_restrict_eval_methods));
12105
12106 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
12107 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
12108 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$objectoffield->canvas == "patient@cabinetmed"'
12109 // 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"
12110
12111 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
12112 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
12113 if ($onlysimplestring == '2') {
12114 $specialcharsallowed .= '<[]'; // Later we check that < has space before and after
12115 }
12116 global $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12117 if (!empty($dolibarr_main_allow_unsecured_special_chars_in_dol_eval)) {
12118 $specialcharsallowed .= (string) $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12119 }
12120 if (preg_match('/[^a-z0-9\s' . preg_quote($specialcharsallowed, '/') . ']/i', $s)) {
12121 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): ' . $s;
12122 }
12123
12124 // Check if we found a | without a space before and after
12125 /* Disabled to allow preg_match('/(AAA|BBB)/')
12126 $tmps = str_replace(' || ', '__XXX__', $s);
12127 if (strpos($tmps, '|') !== false) {
12128 return 'Bad string syntax to evaluate (The char | can be used only when duplicated || with a space before and after): ' . $s;
12129 }
12130 */
12131
12132 // Check if there is PHP comments (can be used to obfuscate code)
12133 if (strpos($s, '/*') !== false || strpos($s, '//') !== false) {
12134 return 'Bad string syntax to evaluate (The comment string /* and // are not allowed): ' . $s;
12135 }
12136
12137 // Check if we found a ? without a space before and after
12138 $tmps = str_replace(' ? ', '__XXX__', $s);
12139 if (strpos($tmps, '?') !== false) {
12140 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): ' . $s;
12141 }
12142
12143 // Check if there is a < or <= without spaces after
12144 if (preg_match('/<=?[^\s]/', $s)) {
12145 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found a < or <= without space after): ' . $s;
12146 }
12147
12148 // Check if there is dynamic call (first we use black list patterns)
12149 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
12150 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;
12151 }
12152
12153 if (empty($dolibarr_main_restrict_eval_methods)) {
12154 // If $dolibarr_main_restrict_eval_methods was set to '', we must check if we try dynamic call
12155
12156 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
12157 $savescheck = '';
12158 $scheck = $s;
12159 while ($scheck && $savescheck != $scheck) {
12160 $savescheck = $scheck;
12161 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
12162 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
12163 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
12164 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12165 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12166 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
12167 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
12168 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
12169 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
12170 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
12171 }
12172 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
12173
12174 // Now test if it remains 1 open parenthesis.
12175 if (strpos($scheck, '(') !== false) {
12176 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found call of a function or method without using the direct name of the function): ' . $s;
12177 }
12178 }
12179
12180 if (strpos($s, '`') !== false) {
12181 return 'Bad string syntax to evaluate (backtick char is forbidden): ' . $s;
12182 }
12183
12184 // Disallow also concat operator
12185 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) {
12186 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
12187 return 'Bad string syntax to evaluate (dot char is forbidden if not strictly between 2 numbers): ' . $s;
12188 }
12189 }
12190
12191 // We exclude string using a $ character that are not an expected global or temporary vars, so that are not:
12192 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $var....
12193 $savescheck = '';
12194 $scheck = $s;
12195 while ($scheck && $savescheck != $scheck) {
12196 $savescheck = $scheck;
12197 $scheck = preg_replace('/\$conf->[a-z\_]+->enabled/', '__VARCONFENABLED__', $scheck); // Remove this once $user->module->enabled has been replaced everywhere with isModEnabled.
12198 $scheck = preg_replace('/\$user->id/', '__VARUSERID__', $scheck);
12199 $scheck = preg_replace('/\$user->hasRight/', '__VARUSERHASRIGHT__', $scheck);
12200 $scheck = preg_replace('/\$user->rights/', '__VARUSERHASRIGHT__', $scheck); // Remove this once $user->rights->xxx is replaced everywhere with $user->hasRight()
12201 $scheck = preg_replace('/\$user->admin/', '__VARUSERISADMIN__', $scheck); // Remove this once $user->admin is replaced everywhere with $user->isAdmin()
12202 $scheck = preg_replace('/\‍(\$db\‍)/', '__VARDB__', $scheck);
12203 $scheck = preg_replace('/\$langs/', '__VARLANGSTRANS__', $scheck);
12204 $scheck = preg_replace('/\$mysoc/', '__VARMYSOC__', $scheck);
12205 $scheck = preg_replace('/\$action/', '__VARACTION__', $scheck);
12206 $scheck = preg_replace('/\$mainmenu/', '__VARMAINMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12207 $scheck = preg_replace('/\$leftmenu/', '__VARLEFTMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12208 $scheck = preg_replace('/\$websitepage/', '__VARWEBSITEPAGE__', $scheck);
12209 $scheck = preg_replace('/\$website/', '__VARWEBSITE__', $scheck);
12210 $scheck = preg_replace('/\$objectoffield/', '__VAROBJECTOFFIELD__', $scheck);
12211 $scheck = preg_replace('/\$object/', '__VAROBJECT__', $scheck);
12212 $scheck = preg_replace('/\$var/', '__VARVAR__', $scheck);
12213
12214 // deprecated (now we use $objecf->canvas or $objectoffield->canvas)
12215 $scheck = preg_replace('/\$soc->canvas/', '__VARSOCCANVAS__', $scheck);
12216 $scheck = preg_replace('/\$obj->canvas/', '__VAROBJCANVAS__', $scheck);
12217
12218 // Now test if it remains one '$'
12219 if (strpos($scheck, '$') !== false) {
12220 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);
12221 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;
12222 }
12223 }
12224
12225 // We block use of php exec or php file functions
12226 $forbiddenphpstrings = array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction', 'SplFileObject', 'SplTempFileObject');
12227
12228 if (empty($dolibarr_main_restrict_eval_methods)) { // If forced to ''
12229 // 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)
12230 // 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
12231 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
12232 $forbiddenphpfunctions = array();
12233 $forbiddenphpmethods = array();
12234
12235 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
12236 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
12237 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
12238
12239 $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"));
12240 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
12241 $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"));
12242 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
12243 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
12244
12245 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
12246
12247 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
12248 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
12249 $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"));
12250 $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", ));
12251 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("putenv", "dl", "apache_child_terminate", "apache_setenv"));
12252 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("posix_kill", "posix_setuid", "posix_setgid"));
12253 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
12254 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
12255 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
12256 $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
12257 $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"));
12258 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
12259 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) { // We disallow all function that allow to obfuscate the real name of a function
12260 // @phpcs:ignore
12261 $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
12262 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concat", "dol_concatdesc")); // native dolibarr functions
12263 }
12264 // Remove from blacklist the function that are into the whitelist
12265 /*foreach ($forbiddenphpfunctions as $key => $forbiddenphpfunction) {
12266 if (in_array($forbiddenphpfunction, $dolibarr_main_restrict_eval_methods_array)) {
12267 unset($forbiddenphpfunctions[$key]);
12268 }
12269 }*/
12270
12271 $forbiddenphpmethods = array_merge($forbiddenphpmethods, array('invoke', 'invokeArgs')); // Methods of ReflectionFunction to execute a function
12272 // Remove from blacklist the function that are into the whitelist
12273 /*foreach ($forbiddenphpmethods as $key => $forbiddenphpmethod) {
12274 if (in_array($forbiddenphpmethod, $dolibarr_main_restrict_eval_methods_array)) {
12275 unset($forbiddenphpmethods[$key]);
12276 }
12277 }*/
12278
12279 $forbiddenphpregex = 'global\s*\$';
12280 $forbiddenphpregex .= '|';
12281 $forbiddenphpregex .= '\b(' . implode('|', $forbiddenphpfunctions) . ')\b';
12282
12283 $forbiddenphpmethodsregex = '->(' . implode('|', $forbiddenphpmethods) . ')';
12284
12285 // Now scan all forbidden patterns
12286 do {
12287 $oldstringtoclean = $s;
12288 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12289 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12290 $s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12291 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12292 } while ($oldstringtoclean != $s);
12293
12294 if (strpos($s, '__forbiddenstring__') !== false) {
12295 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12296 return 'Bad string syntax to evaluate: ' . $s;
12297 }
12298 }
12299
12300 if (!empty($dolibarr_main_restrict_eval_methods)) {
12301 // Accept only white-listed allowed function and classes
12302 // TODO Get all pattern '/([\s\w]+)\‍(/', then check that $reg[1] is a defined class or a function into a given list
12303 $pattern = '/([\s\w\'\]\"]+)\‍(/';
12304
12305 $matches = array();
12306 preg_match_all($pattern, $s, $matches);
12307
12308 if (count($matches)) {
12309 foreach ($matches[1] as $m) {
12310 $m = trim($m);
12311 if (empty($m)) {
12312 continue;
12313 }
12314 $reg = array();
12315 if (!preg_match('/new ([A-Z][\w]+)/i', $m, $reg)) {
12316 if (!in_array($m, $dolibarr_main_restrict_eval_methods_array)) {
12317 if ($m != "'" && $m != '"') {
12318 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12319 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;
12320 }
12321 }
12322 } else {
12323 if (!class_exists($reg[1])) {
12324 dol_syslog('Bad string syntax to evaluate: Class "'.$reg[1].'" does not exist. ' . $s, LOG_WARNING);
12325 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" does not exist. ' . $s;
12326 }
12327 $parents = class_parents($reg[1]); // Get list of parent classes of class we want to check
12328 if (!in_array('CommonObject', $parents)) { // Only classes that inherit CommonObject are ok. This forbid dangerous classes like ReflectionFunction, SplFileObject, ...
12329 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);
12330 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" is not allowed because only classes extended CommonObject can be used in dynamic evaluation. ' . $s;
12331 }
12332 }
12333 }
12334 }
12335
12336 $forbiddenphpregex = 'global\s*\$';
12337 $forbiddenphpregex .= '|'; // or
12338 $forbiddenphpregex .= '}\s*\[';
12339 $forbiddenphpregex .= '|'; // or
12340 $forbiddenphpregex .= '\‍)\s*\‍(';
12341
12342 // Now scan all forbidden patterns
12343 do {
12344 $oldstringtoclean = $s;
12345 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12346 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12347 //$s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12348 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12349 } while ($oldstringtoclean != $s);
12350
12351 if (strpos($s, '__forbiddenstring__') !== false) {
12352 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12353 return 'Bad string syntax to evaluate: ' . $s;
12354 }
12355 }
12356
12357 //print $s."<br>\n";
12358 ob_start(); // An evaluation has no reason to output data
12359 $isObBufferActive = true;
12360 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
12361 $tmpo = ob_get_clean();
12362 $isObBufferActive = false;
12363 if ($tmpo) {
12364 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
12365 }
12366 return $tmps;
12367 } catch (Error $e) {
12368 if ($isObBufferActive) {
12369 // Clean up buffer which was left behind due to exception.
12370 $tmpo = ob_get_clean();
12371 $isObBufferActive = false;
12372 }
12373 $error = 'dol_eval try/catch error : ';
12374 $error .= $e->getMessage();
12375 dol_syslog($error, LOG_WARNING);
12376 return 'Exception during evaluation: ' . $s;
12377 }
12378}
12379
12387function dol_validElement($element)
12388{
12389 return (trim($element) != '');
12390}
12391
12400function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
12401{
12402 if (empty($codelang)) {
12403 return '';
12404 }
12405
12406 if ($codelang == 'auto') {
12407 return '<span class="fa fa-language"></span>';
12408 }
12409
12410 $langtocountryflag = array(
12411 'ar_AR' => '',
12412 'ca_ES' => 'catalonia',
12413 'da_DA' => 'dk',
12414 'fr_CA' => 'mq',
12415 'sv_SV' => 'se',
12416 'sw_SW' => 'unknown',
12417 'AQ' => 'unknown',
12418 'CW' => 'unknown',
12419 'IM' => 'unknown',
12420 'JE' => 'unknown',
12421 'MF' => 'unknown',
12422 'BL' => 'unknown',
12423 'SX' => 'unknown'
12424 );
12425
12426 if (isset($langtocountryflag[$codelang])) {
12427 $flagImage = $langtocountryflag[$codelang];
12428 } else {
12429 $tmparray = explode('_', $codelang);
12430 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
12431 }
12432
12433 $morecss = '';
12434 $reg = array();
12435 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
12436 $morecss = $reg[1];
12437 $moreatt = "";
12438 }
12439
12440 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
12441 return '<span class="flag-sprite ' . strtolower($flagImage) . ($morecss ? ' ' . $morecss : '') . '"' . ($moreatt ? ' ' . $moreatt : '') . (!$notitlealt ? ' title="' . $codelang . '"' : '') . '></span>';
12442}
12443
12451function getLanguageCodeFromCountryCode($countrycode)
12452{
12453 global $mysoc;
12454
12455 if (empty($countrycode)) {
12456 return null;
12457 }
12458
12459 if (strtoupper($countrycode) == 'MQ') {
12460 return 'fr_CA';
12461 }
12462 if (strtoupper($countrycode) == 'SE') {
12463 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
12464 }
12465 if (strtoupper($countrycode) == 'CH') {
12466 if ($mysoc->country_code == 'FR') {
12467 return 'fr_CH';
12468 }
12469 if ($mysoc->country_code == 'DE') {
12470 return 'de_CH';
12471 }
12472 if ($mysoc->country_code == 'IT') {
12473 return 'it_CH';
12474 }
12475 }
12476
12477 // Locale list taken from:
12478 // http://stackoverflow.com/questions/3191664/
12479 // list-of-all-locales-and-their-short-codes
12480 $locales = array(
12481 'af-ZA',
12482 'am-ET',
12483 'ar-AE',
12484 'ar-BH',
12485 'ar-DZ',
12486 'ar-EG',
12487 'ar-IQ',
12488 'ar-JO',
12489 'ar-KW',
12490 'ar-LB',
12491 'ar-LY',
12492 'ar-MA',
12493 'ar-OM',
12494 'ar-QA',
12495 'ar-SA',
12496 'ar-SY',
12497 'ar-TN',
12498 'ar-YE',
12499 //'as-IN', // Moved after en-IN
12500 'ba-RU',
12501 'be-BY',
12502 'bg-BG',
12503 'bn-BD',
12504 //'bn-IN', // Moved after en-IN
12505 'bo-CN',
12506 'br-FR',
12507 'ca-ES',
12508 'co-FR',
12509 'cs-CZ',
12510 'cy-GB',
12511 'da-DK',
12512 'de-AT',
12513 'de-CH',
12514 'de-DE',
12515 'de-LI',
12516 'de-LU',
12517 'dv-MV',
12518 'el-GR',
12519 'en-AU',
12520 'en-BZ',
12521 'en-CA',
12522 'en-GB',
12523 'en-IE',
12524 'en-IN',
12525 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
12526 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
12527 'en-JM',
12528 'en-MY',
12529 'en-NZ',
12530 'en-PH',
12531 'en-SG',
12532 'en-TT',
12533 'en-US',
12534 'en-ZA',
12535 'en-ZW',
12536 'es-AR',
12537 'es-BO',
12538 'es-CL',
12539 'es-CO',
12540 'es-CR',
12541 'es-DO',
12542 'es-EC',
12543 'es-ES',
12544 'es-GT',
12545 'es-HN',
12546 'es-MX',
12547 'es-NI',
12548 'es-PA',
12549 'es-PE',
12550 'es-PR',
12551 'es-PY',
12552 'es-SV',
12553 'es-US',
12554 'es-UY',
12555 'es-VE',
12556 'et-EE',
12557 'eu-ES',
12558 'fa-IR',
12559 'fi-FI',
12560 'fo-FO',
12561 'fr-BE',
12562 'fr-CA',
12563 'fr-CH',
12564 'fr-FR',
12565 'fr-LU',
12566 'fr-MC',
12567 'fy-NL',
12568 'ga-IE',
12569 'gd-GB',
12570 'gl-ES',
12571 'gu-IN',
12572 'he-IL',
12573 'hi-IN',
12574 'hr-BA',
12575 'hr-HR',
12576 'hu-HU',
12577 'hy-AM',
12578 'id-ID',
12579 'ig-NG',
12580 'ii-CN',
12581 'is-IS',
12582 'it-CH',
12583 'it-IT',
12584 'ja-JP',
12585 'ka-GE',
12586 'kk-KZ',
12587 'kl-GL',
12588 'km-KH',
12589 'kn-IN',
12590 'ko-KR',
12591 'ky-KG',
12592 'lb-LU',
12593 'lo-LA',
12594 'lt-LT',
12595 'lv-LV',
12596 'mi-NZ',
12597 'mk-MK',
12598 'ml-IN',
12599 'mn-MN',
12600 'mr-IN',
12601 'ms-BN',
12602 'ms-MY',
12603 'mt-MT',
12604 'nb-NO',
12605 'ne-NP',
12606 'nl-BE',
12607 'nl-NL',
12608 'nn-NO',
12609 'oc-FR',
12610 'or-IN',
12611 'pa-IN',
12612 'pl-PL',
12613 'ps-AF',
12614 'pt-BR',
12615 'pt-PT',
12616 'rm-CH',
12617 'ro-MD',
12618 'ro-RO',
12619 'ru-RU',
12620 'rw-RW',
12621 'sa-IN',
12622 'se-FI',
12623 'se-NO',
12624 'se-SE',
12625 'si-LK',
12626 'sk-SK',
12627 'sl-SI',
12628 'sq-AL',
12629 'sv-FI',
12630 'sv-SE',
12631 'sw-KE',
12632 'ta-IN',
12633 'te-IN',
12634 'th-TH',
12635 'tk-TM',
12636 'tn-ZA',
12637 'tr-TR',
12638 'tt-RU',
12639 'ug-CN',
12640 'uk-UA',
12641 'ur-PK',
12642 'vi-VN',
12643 'wo-SN',
12644 'xh-ZA',
12645 'yo-NG',
12646 'zh-CN',
12647 'zh-HK',
12648 'zh-MO',
12649 'zh-SG',
12650 'zh-TW',
12651 'zu-ZA',
12652 );
12653
12654 $buildprimarykeytotest = strtolower($countrycode) . '-' . strtoupper($countrycode);
12655 if (in_array($buildprimarykeytotest, $locales)) {
12656 return strtolower($countrycode) . '_' . strtoupper($countrycode);
12657 }
12658
12659 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
12660 foreach ($locales as $locale) {
12661 $locale_language = locale_get_primary_language($locale);
12662 $locale_region = locale_get_region($locale);
12663 if (strtoupper($countrycode) == $locale_region) {
12664 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
12665 return strtolower($locale_language) . '_' . strtoupper($locale_region);
12666 }
12667 }
12668 } else {
12669 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
12670 }
12671
12672 return null;
12673}
12674
12705function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
12706{
12707 global $hookmanager, $db;
12708
12709 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
12710 foreach ($conf->modules_parts['tabs'][$type] as $value) {
12711 $values = explode(':', $value);
12712
12713 $reg = array();
12714 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
12715 if (count($values) !== 6) {
12716 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);
12717 continue;
12718 }
12719
12720 // new declaration with permissions:
12721 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12722 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12723 if ($values[0] != $type) {
12724 continue;
12725 }
12726
12727 $newtab = array();
12728 $postab = $h;
12729 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
12730 $str = $values[1];
12731 $posstart = strpos($str, '(');
12732 if ($posstart > 0) {
12733 $posend = strpos($str, ')');
12734 if ($posstart > 0) {
12735 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
12736 if (is_numeric($res1)) {
12737 $postab = (int) $res1;
12738 $values[1] = '+' . substr($str, $posend + 1);
12739 }
12740 }
12741 }
12742
12743 global $objectoffield; // So we can use $objectoffield int verifCond
12744 $objectoffield = $object;
12745
12746 if (!verifCond($values[4], '2')) {
12747 continue;
12748 }
12749
12750 if ($values[3]) {
12751 if ($filterorigmodule) { // If a filter of module origin has been requested
12752 if (strpos($values[3], '@')) { // This is an external module
12753 if ($filterorigmodule != 'external') {
12754 continue;
12755 }
12756 } else { // This looks a core module
12757 if ($filterorigmodule != 'core') {
12758 continue;
12759 }
12760 }
12761 }
12762 $langs->load($values[3]);
12763 }
12764
12765 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
12766 // If label is "SUBSTITUION_..."
12767 $substitutionarray = array();
12768 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
12769 $label = make_substitutions($reg[1], $substitutionarray);
12770 } else {
12771 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
12772 $labeltemp = explode(',', $values[2]);
12773 $label = $langs->trans($labeltemp[0]);
12774
12775 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
12776 dol_include_once($labeltemp[2]);
12777 $classtoload = $labeltemp[1];
12778 if (class_exists($classtoload)) {
12779 $obj = new $classtoload($db);
12780 $function = $labeltemp[3];
12781 if ($obj && $function && method_exists($obj, $function)) {
12782 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12783 $nbrec = $obj->$function($object->id, $obj);
12784 if (!empty($nbrec)) {
12785 $label .= '<span class="badge marginleftonlyshort">' . $nbrec . '</span>';
12786 }
12787 }
12788 }
12789 }
12790 }
12791 $url = preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]);
12792 $link = parse_url($url);
12793 $query = [];
12794 if (isset($link['query'])) {
12795 parse_str($link['query'], $query);
12796 }
12797 $newtab[0] = dolBuildUrl(dol_buildpath($link['path'], 1), $query);
12798 $newtab[1] = $label;
12799 $newtab[2] = str_replace('+', '', $values[1]);
12800 $h++;
12801
12802 // set tab at its position
12803 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
12804 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
12805 if ($values[0] != $type) {
12806 continue;
12807 }
12808 $tabname = str_replace('-', '', $values[1]);
12809 foreach ($head as $key => $val) {
12810 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
12811 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
12812 if ($head[$key][2] == $tabname && $condition) {
12813 unset($head[$key]);
12814 break;
12815 }
12816 }
12817 }
12818 }
12819 }
12820
12821 // No need to make a return $head. Var is modified as a reference
12822 if (!empty($hookmanager)) {
12823 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
12824 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
12825 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
12826 if ($reshook > 0) { // Hook ask to replace completely the array
12827 $head = $hookmanager->resArray;
12828 } else { // Hook
12829 $head = array_merge($head, $hookmanager->resArray);
12830 }
12831 $h = count($head);
12832 }
12833}
12834
12846function printCommonFooter($zone = 'private')
12847{
12848 global $conf, $hookmanager, $user, $langs;
12849 global $action;
12850 global $micro_start_time;
12851
12852 if ($zone == 'private') {
12853 print "\n" . '<!-- Common footer for private page -->' . "\n";
12854 } else {
12855 print "\n" . '<!-- Common footer for public page -->' . "\n";
12856 }
12857
12858 // A div to store page_y POST parameter so we can read it using javascript
12859 print "\n<!-- A div to store page_y POST parameter -->\n";
12860 print '<div id="page_y" style="display: none;">' . (GETPOST('page_y') ? GETPOST('page_y') : '') . '</div>' . "\n";
12861
12862 $parameters = array('zone' => $zone);
12863 $tmpobject = null;
12864 // @phan-suppress-next-line PhanPluginConstantVariableNull
12865 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters, $tmpobject, $action); // Note that $action and $object may have been modified by some hooks
12866 if (empty($reshook)) {
12867 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
12868 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
12869 }
12870
12871 print "\n";
12872 if (!empty($conf->use_javascript_ajax)) {
12873 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
12874 print '<script>' . "\n";
12875 print 'jQuery(document).ready(function() {' . "\n";
12876
12877 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
12878 print "\n";
12879 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */' . "\n";
12880 print 'jQuery("li.menuhider").click(function(event) {';
12881 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }' . "\n";
12882 print ' console.log("We click on .menuhider");' . "\n";
12883 print ' $("body").toggleClass("sidebar-collapse")' . "\n";
12884 print '});' . "\n";
12885 }
12886
12887 // Management of focus and mandatory for fields
12888 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"])))) {
12889 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */' . "\n";
12890 $relativepathstring = $_SERVER["PHP_SELF"];
12891 // Clean $relativepathstring
12892 if (constant('DOL_URL_ROOT')) {
12893 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
12894 }
12895 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
12896 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
12897 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
12898
12899 if (!empty($user->default_values[$relativepathstring]['focus'])) {
12900 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
12901 $qualified = 0;
12902 if ($defkey != '_noquery_') {
12903 $tmpqueryarraytohave = explode('&', $defkey);
12904 $foundintru = 0;
12905 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12906 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12907 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12908 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12909 $foundintru = 1;
12910 }
12911 }
12912 if (!$foundintru) {
12913 $qualified = 1;
12914 }
12915 //var_dump($defkey.'-'.$qualified);
12916 } else {
12917 $qualified = 1;
12918 }
12919
12920 if ($qualified) {
12921 print 'console.log("set the focus by executing jQuery(...).focus();")' . "\n";
12922 foreach ($defval as $paramkey => $paramval) {
12923 // Set focus on field
12924 print 'jQuery("input[name=\'' . $paramkey . '\']").focus();' . "\n";
12925 print 'jQuery("textarea[name=\'' . $paramkey . '\']").focus();' . "\n"; // TODO KO with ckeditor
12926 print 'jQuery("select[name=\'' . $paramkey . '\']").focus();' . "\n"; // Not really useful, but we keep it in case of.
12927 }
12928 }
12929 }
12930 }
12931 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12932 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12933 $qualified = 0;
12934 if ($defkey != '_noquery_') {
12935 $tmpqueryarraytohave = explode('&', $defkey);
12936 $foundintru = 0;
12937 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12938 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12939 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12940 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12941 $foundintru = 1;
12942 }
12943 }
12944 if (!$foundintru) {
12945 $qualified = 1;
12946 }
12947 //var_dump($defkey.'-'.$qualified);
12948 } else {
12949 $qualified = 1;
12950 }
12951
12952 if ($qualified) {
12953 print 'console.log("set the js code to manage fields that are set as mandatory");' . "\n";
12954
12955 foreach ($defval as $paramkey => $paramval) {
12956 // Solution 1: Add handler on submit to check if mandatory fields are empty
12957 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12958 print "form.on('submit', function(event) {
12959 var submitter = \$(this).find(':submit:focus').get(0);
12960 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12961
12962 if (buttonName == 'cancel') {
12963 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12964 return true;
12965 }
12966
12967 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');
12968
12969 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12970 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12971
12972 if (tmptypefield == 'textarea') {
12973 // We must instead check the content of ckeditor
12974 var tmpeditor = (typeof CKEDITOR !== 'undefined') ? CKEDITOR.instances['".dol_escape_js($paramkey)."'] : null;
12975 if (tmpeditor) {
12976 tmpvalue = tmpeditor.getData();
12977 console.log('For textarea tmpvalue is '+tmpvalue);
12978 }
12979 }
12980
12981 let tmpvalueisempty = false;
12982 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1 || tmpvalue === '-1') {
12983 tmpvalueisempty = true;
12984 }
12985 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
12986 tmpvalueisempty = true;
12987 }
12988 if (tmpvalueisempty && buttonName !== 'cancel') {
12989 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
12990 event.preventDefault(); // Stop submission of form to allow custom code to decide.
12991 event.stopPropagation(); // Stop other handlers.
12992
12993 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
12994
12995 return false;
12996 }
12997 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
12998 return true;
12999 });
13000 \n";
13001
13002 // Solution 2: Add property 'required' on input
13003 // so browser will check value and try to focus on it when submitting the form.
13004 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
13005 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
13006 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
13007 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
13008 //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";
13009 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
13010 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
13011 // Add 'field required' class on closest td for all input elements : input, textarea and select
13012 //print '}, 500);'; // 500 milliseconds delay
13013
13014 // Now set the class "fieldrequired"
13015 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
13016 }
13017
13018 // If we submit using the cancel button, we remove the required attributes
13019 print 'jQuery("input[name=\'cancel\']").click(function() {
13020 console.log("We click on cancel button so removed all required attribute");
13021 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
13022 });' . "\n";
13023 }
13024 }
13025 }
13026 }
13027
13028 print '});' . "\n";
13029
13030 // End of tuning
13031 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
13032 print "\n";
13033 print "/* JS CODE TO ENABLE to add memory info */\n";
13034 print 'window.console && console.log("';
13035 if (getDolGlobalString('MEMCACHED_SERVER')) {
13036 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER') . ' - ';
13037 }
13038 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
13039 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
13040 $micro_end_time = microtime(true);
13041 print ' - Build time: ' . ceil(1000 * ($micro_end_time - $micro_start_time)) . ' ms';
13042 }
13043
13044 if (function_exists("memory_get_usage")) {
13045 print ' - Mem: ' . memory_get_usage(); // Do not use true here, it seems it takes the peak amount
13046 }
13047 if (function_exists("memory_get_peak_usage")) {
13048 print ' - Real mem peak: ' . memory_get_peak_usage(true);
13049 }
13050 if (function_exists("zend_loader_file_encoded")) {
13051 print ' - Zend encoded file: ' . (zend_loader_file_encoded() ? 'yes' : 'no');
13052 }
13053 print '");' . "\n";
13054 }
13055
13056 print "\n" . '</script>' . "\n";
13057
13058 // Google Analytics
13059 // TODO Remove this, can be replaced with the hook printCommonFooter
13060 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
13061 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
13062 foreach ($tmptagarray as $tmptag) {
13063 print "\n";
13064 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
13065 print '
13066 <!-- Global site tag (gtag.js) - Google Analytics -->
13067 <script nonce="' . getNonce() . '" async src="https://www.googletagmanager.com/gtag/js?id=' . trim($tmptag) . '"></script>
13068 <script>
13069 window.dataLayer = window.dataLayer || [];
13070 function gtag(){dataLayer.push(arguments);}
13071 gtag(\'js\', new Date());
13072
13073 gtag(\'config\', \'' . trim($tmptag) . '\');
13074 </script>';
13075 print "\n";
13076 }
13077 }
13078 }
13079
13080 // Add Xdebug coverage of code
13081 if (defined('XDEBUGCOVERAGE')) {
13082 print_r(xdebug_get_code_coverage());
13083 }
13084
13085 // Output string from hooks
13086 if (!empty($hookmanager->resPrint)) {
13087 print $hookmanager->resPrint;
13088 }
13089
13090 // Add DebugBar data
13091 if ($user->hasRight('debugbar', 'read')) {
13092 global $debugbar;
13093 if ($debugbar instanceof DebugBar\DebugBar) {
13094 if (isset($debugbar['time'])) {
13095 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
13096 $debugbar['time']->stopMeasure('pageaftermaster');
13097 }
13098 print '<!-- Output debugbar data -->' . "\n";
13099 $renderer = $debugbar->getJavascriptRenderer();
13100 print $renderer->render();
13101 }
13102 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
13103 print "\n";
13104 print "<!-- Start of log output\n";
13105 //print '<div class="hidden">'."\n";
13106 foreach ($conf->logbuffer as $logline) {
13107 print $logline . "<br>\n";
13108 }
13109 //print '</div>'."\n";
13110 print "End of log output -->\n";
13111 }
13112 }
13113}
13114
13124function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
13125{
13126 if (is_null($string)) {
13127 return array();
13128 }
13129
13130 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
13131 // This is a regex string
13132 $newdelimiter = $delimiter;
13133 } else {
13134 // This is a simple string
13135 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
13136 $newdelimiter = preg_quote($delimiter, '/');
13137 }
13138
13139 if ($a = preg_split('/' . $newdelimiter . '/', $string)) {
13140 $ka = array();
13141 foreach ($a as $s) { // each part
13142 if ($s) {
13143 if ($pos = strpos($s, $kv)) { // key/value delimiter
13144 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
13145 } else { // key delimiter not found
13146 $ka[] = trim($s);
13147 }
13148 }
13149 }
13150 return $ka;
13151 }
13152
13153 return array();
13154}
13155
13163function dolExplodeKeepIfQuotes($input)
13164{
13165 // Use regexp to capture words and section in quotes
13166 $matches = array();
13167 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
13168
13169 // Merge result and delete empty values
13170
13171 $result = array_map(
13178 static function ($a, $b, $c) {
13179 if ($a !== '') {
13180 return $a;
13181 }
13182 if ($b !== '') {
13183 return $b;
13184 }
13185 if ($c !== '') {
13186 return $c;
13187 }
13188 return '';
13189 },
13190 $matches[1],
13191 $matches[2],
13192 $matches[3]
13193 );
13194 return array_values(array_filter(
13195 $result,
13202 static function ($val) {
13203 return $val !== '';
13204 }
13205 ));
13206}
13207
13208
13215function dol_set_focus($selector)
13216{
13217 print "\n" . '<!-- Set focus onto a specific field -->' . "\n";
13218 print '<script nonce="' . getNonce() . '">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("' . dol_escape_js($selector) . '").focus(); });</script>' . "\n";
13219}
13220
13221
13229function dol_getmypid()
13230{
13231 if (!function_exists('getmypid')) {
13232 return mt_rand(99900000, 99965535);
13233 } else {
13234 return getmypid(); // May be a number on 64 bits (depending on OS)
13235 }
13236}
13237
13260function natural_search($fields, $value, $mode = 0, $nofirstand = 0, $sqltoadd = '')
13261{
13262 global $db, $langs;
13263
13264 $value = trim($value);
13265
13266 if ($mode == 0) {
13267 $value = preg_replace('/\*/', '%', $value); // Replace * with %
13268 }
13269 if ($mode == 1) {
13270 $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
13271 }
13272
13273 $value = preg_replace('/\s*\|\s*/', '|', $value);
13274
13275 // Split criteria on ' ' but not if we are inside quotes.
13276 // For mode 3, the split is done later on the , only and not on the ' '.
13277 if ($mode != -3 && $mode != 3) {
13278 $crits = dolExplodeKeepIfQuotes($value);
13279 } else {
13280 $crits = array($value);
13281 }
13282
13283 $res = '';
13284 if (!is_array($fields)) {
13285 $fields = array($fields);
13286 }
13287 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
13288 foreach ($crits as $crit) { // Loop on each AND criteria
13289 $crit = trim($crit);
13290 $i2 = 0; // count the nb of valid criteria added for this this first criteria
13291 $newres = '';
13292
13293 foreach ($fields as $field) {
13294 if ($mode == 1) {
13295 $tmpcrits = explode('|', $crit);
13296 $i3 = 0; // count the nb of valid criteria added for this current field
13297 foreach ($tmpcrits as $tmpcrit) {
13298 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13299 continue;
13300 }
13301 $tmpcrit = trim($tmpcrit);
13302
13303 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13304
13305 $operator = '=';
13306 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
13307
13308 $reg = array();
13309 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
13310 if (!empty($reg[1])) {
13311 $operator = $reg[1];
13312 }
13313 if ($newcrit != '') {
13314 $numnewcrit = price2num($newcrit);
13315 if (is_numeric($numnewcrit)) {
13316 $newres .= $db->sanitize($field) . ' ' . $operator . ' ' . ((float) $numnewcrit); // should be a numeric
13317 } else {
13318 $newres .= '1 = 2'; // force false, we received a corrupted data
13319 }
13320 $i3++; // a criteria was added to string
13321 }
13322 }
13323 $i2++; // a criteria for 1 more field was added to string
13324 } elseif ($mode == 2 || $mode == -2) {
13325 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
13326 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -2 ? 'NOT ' : '');
13327 $newres .= $crit ? "IN (" . $db->sanitize($db->escape($crit)) . ")" : "IN (0)";
13328 if ($mode == -2) {
13329 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13330 }
13331 $i2++; // a criteria for 1 more field was added to string
13332 } elseif ($mode == 3 || $mode == -3) {
13333 $tmparray = explode(',', $crit);
13334 if (count($tmparray)) {
13335 $listofcodes = '';
13336 foreach ($tmparray as $val) {
13337 $val = trim($val);
13338 if ($val) { // TODO Test with if ($val !== '') {
13339 $listofcodes .= ($listofcodes ? ',' : '');
13340 $listofcodes .= "'" . $db->escape($val) . "'";
13341 }
13342 }
13343 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -3 ? 'NOT ' : '') . "IN (" . $db->sanitize($listofcodes, 1, 0, 1) . ")";
13344 $i2++; // a criteria for 1 more field was added to string
13345 }
13346 if ($mode == -3) {
13347 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13348 }
13349 } elseif ($mode == 4) {
13350 $tmparray = explode(',', $crit);
13351 if (count($tmparray)) {
13352 $listofcodes = '';
13353 foreach ($tmparray as $val) {
13354 $val = trim($val);
13355 if ($val) {
13356 $newres .= ($i2 > 0 ? " OR (" : "(") . $db->sanitize($field) . " LIKE '" . $db->escape($val) . ",%'";
13357 $newres .= ' OR ' . $db->sanitize($field) . " = '" . $db->escape($val) . "'";
13358 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . "'";
13359 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . ",%'";
13360 $newres .= ')';
13361 $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)
13362 }
13363 }
13364 }
13365 } else { // $mode=0
13366 $tmpcrits = explode('|', $crit);
13367 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
13368 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
13369 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13370 continue;
13371 }
13372 $tmpcrit = trim($tmpcrit);
13373
13374 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
13375 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
13376 } else {
13377 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13378 }
13379
13380 $isSellist = false;
13381 $table = $label = $key = null;
13382
13383 if (strpos($field, 'ef.') === 0) {
13384 $extrafieldName = substr($field, 3);
13385 $extrafields = new ExtraFields($db);
13386 $extrafields->fetch_name_optionals_label('product');
13387
13388 if (isset($extrafields->attributes['product']['type'][$extrafieldName]) && $extrafields->attributes['product']['type'][$extrafieldName] === 'sellist') {
13389 $isSellist = true;
13390 $paramArray = $extrafields->attributes['product']['param'][$extrafieldName]['options'] ?? [];
13391 $param = array_key_first($paramArray);
13392 list($table, $label, $key) = explode(':', $param);
13393 }
13394 }
13395
13396 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
13397 $newres .= $db->sanitize($field) . " = " . (is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
13398 } else {
13399 $tmpcrit2 = $tmpcrit;
13400 $tmpbefore = '%';
13401 $tmpafter = '%';
13402 $tmps = '';
13403
13404 if ($isSellist) {
13405 $newres .= $field . " IN (SELECT t." . $key . " FROM " . $db->prefix() . $table . " AS t WHERE t." . $label . " LIKE '%" . $db->escape($tmpcrit2) . "%')";
13406 } else {
13407 if (preg_match('/^!/', $tmpcrit)) {
13408 $tmps .= $db->sanitize($field) . " NOT LIKE "; // ! as exclude character
13409 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
13410 } else {
13411 $tmps .= $db->sanitize($field) . " LIKE ";
13412 }
13413 $tmps .= "'";
13414
13415 if (preg_match('/^[\^\$]/', $tmpcrit)) {
13416 $tmpbefore = '';
13417 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
13418 }
13419 if (preg_match('/[\^\$]$/', $tmpcrit)) {
13420 $tmpafter = '';
13421 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
13422 }
13423
13424 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13425 $tmps = "(" . $tmps;
13426 }
13427 $newres .= $tmps;
13428 $newres .= $tmpbefore;
13429 $newres .= $db->escape($tmpcrit2);
13430 $newres .= $tmpafter;
13431 $newres .= "'";
13432 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13433 $newres .= " OR " . $field . " IS NULL)";
13434 }
13435 }
13436 }
13437
13438 $i3++;
13439 }
13440
13441 $i2++; // a criteria for 1 more field was added to string
13442 }
13443 }
13444
13445 if ($sqltoadd) {
13446 $newres .= ($newres ? '' : ' OR ').str_replace('__KEYTOSEARCH__', $crit, $sqltoadd);
13447 }
13448
13449 if ($newres) {
13450 $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') . $newres . ($i2 > 1 ? ')' : '');
13451 }
13452 $i1++;
13453 }
13454 $res = ($nofirstand ? "" : " AND ") . "(" . $res . ")";
13455
13456 return $res;
13457}
13458
13465function showDirectDownloadLink($object)
13466{
13467 global $langs;
13468
13469 $out = '';
13470 $url = $object->getLastMainDocLink($object->element);
13471
13472 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe') . ' <span class="opacitymedium">' . $langs->trans("DirectDownloadLink") . '</span><br>';
13473 if ($url) {
13474 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="' . $url . '"></div>';
13475 $out .= ajax_autoselect("directdownloadlink", '');
13476 } else {
13477 $out .= '<div class="urllink">' . $langs->trans("FileNotShared") . '</div>';
13478 }
13479
13480 return $out;
13481}
13482
13491function getImageFileNameForSize($file, $extName, $extImgTarget = '')
13492{
13493 $dirName = dirname($file);
13494 if ($dirName == '.') {
13495 $dirName = '';
13496 }
13497
13498 if (!in_array($extName, array('', '_small', '_mini'))) {
13499 return 'Bad parameter extName';
13500 }
13501
13502 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
13503 $fileName = basename($fileName);
13504
13505 if (empty($extImgTarget)) {
13506 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
13507 }
13508 if (empty($extImgTarget)) {
13509 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
13510 }
13511 if (empty($extImgTarget)) {
13512 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
13513 }
13514 if (empty($extImgTarget)) {
13515 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
13516 }
13517 if (empty($extImgTarget)) {
13518 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
13519 }
13520 if (empty($extImgTarget)) {
13521 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
13522 }
13523
13524 if (!$extImgTarget) {
13525 return $file;
13526 }
13527
13528 $subdir = '';
13529 if ($extName) {
13530 $subdir = 'thumbs/';
13531 }
13532
13533 return ($dirName ? $dirName . '/' : '') . $subdir . $fileName . $extName . $extImgTarget; // New filename for thumb
13534}
13535
13536
13546function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
13547{
13548 global $conf, $langs;
13549
13550 if (empty($conf->use_javascript_ajax)) {
13551 return '';
13552 }
13553
13554 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
13555
13556 if ($alldata == 1) {
13557 if ($isAllowedForPreview) {
13558 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));
13559 } else {
13560 return array();
13561 }
13562 }
13563
13564 // old behavior, return a string
13565 if ($isAllowedForPreview) {
13566 $tmpurl = DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : '');
13567 $title = $langs->transnoentities("Preview");
13568 //$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().
13569 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
13570
13571 // 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,
13572 // 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.
13573 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
13574 return 'javascript:' . dol_escape_uri('document_preview(\'' . dol_escape_js($tmpurl) . '\', \'' . dol_escape_js(dol_mimetype($relativepath)) . '\', \'' . dol_escape_js($title) . '\')');
13575 } else {
13576 return '';
13577 }
13578}
13579
13586function getLabelSpecialCode($idcode)
13587{
13588 global $langs;
13589
13590 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
13591 if ($idcode > 10) {
13592 return 'Module ID ' . $idcode;
13593 }
13594 if (!empty($arrayspecialines[$idcode])) {
13595 return $langs->trans($arrayspecialines[$idcode]);
13596 }
13597 return '';
13598}
13599
13608function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
13609{
13610 global $langs;
13611 $out = '<script nonce="' . getNonce() . '">
13612 jQuery(document).ready(function () {
13613 jQuery("' . ((strpos($htmlname, '.') === 0 ? '' : '#') . $htmlname) . '").click(function() { jQuery(this).select(); } );
13614 });
13615 </script>';
13616 if ($addlink) {
13617 if ($textonlink === 'image') {
13618 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . img_picto('', 'globe') . '</a>';
13619 } else {
13620 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . $langs->trans("Link") . '</a>';
13621 }
13622 }
13623 return $out;
13624}
13625
13633function dolIsAllowedForPreview($file)
13634{
13635 // Check .noexe extension in filename
13636 if (preg_match('/\.noexe$/i', $file)) {
13637 return 0;
13638 }
13639
13640 // Check mime types
13641 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp', 'webm', 'mp4');
13642 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
13643 $mime_preview[] = 'svg+xml';
13644 }
13645 if (getDolGlobalString('MAIN_ALLOW_XML_FILES_AS_PREVIEW')) {
13646 $mime_preview[] = 'xml';
13647 }
13648
13649 //$mime_preview[]='vnd.oasis.opendocument.presentation';
13650 //$mime_preview[]='archive';
13651 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
13652 if ($num_mime !== false) {
13653 return 1;
13654 }
13655
13656 // By default, not allowed for preview
13657 return 0;
13658}
13659
13660
13670function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
13671{
13672 $mime = $default;
13673 $imgmime = 'other.png';
13674 $famime = 'file-o';
13675 $srclang = '';
13676
13677 $tmpfile = preg_replace('/\.noexe$/', '', $file);
13678
13679 // Plain text files
13680 if (preg_match('/\.txt$/i', $tmpfile)) {
13681 $mime = 'text/plain';
13682 $imgmime = 'text.png';
13683 $famime = 'file-alt';
13684 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
13685 $mime = 'text/richtext';
13686 $imgmime = 'text.png';
13687 $famime = 'file-alt';
13688 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
13689 $mime = 'text/csv';
13690 $imgmime = 'text.png';
13691 $famime = 'file-csv';
13692 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
13693 $mime = 'text/tab-separated-values';
13694 $imgmime = 'text.png';
13695 $famime = 'file-alt';
13696 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
13697 $mime = 'text/plain';
13698 $imgmime = 'text.png';
13699 $famime = 'file-alt';
13700 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
13701 $mime = 'text/plain';
13702 $imgmime = 'text.png';
13703 $srclang = 'ini';
13704 $famime = 'file-alt';
13705 } elseif (preg_match('/\.md$/i', $tmpfile)) {
13706 $mime = 'text/plain';
13707 $imgmime = 'text.png';
13708 $srclang = 'md';
13709 $famime = 'file-alt';
13710 } elseif (preg_match('/\.css$/i', $tmpfile)) {
13711 $mime = 'text/css';
13712 $imgmime = 'css.png';
13713 $srclang = 'css';
13714 $famime = 'file-alt';
13715 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
13716 $mime = 'text/plain';
13717 $imgmime = 'text.png';
13718 $srclang = 'lang';
13719 $famime = 'file-alt';
13720 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
13721 $mime = 'text/plain';
13722 $imgmime = 'text.png';
13723 $famime = 'file-alt';
13724 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
13725 $mime = 'text/html';
13726 $imgmime = 'html.png';
13727 $srclang = 'html';
13728 $famime = 'file-alt';
13729 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
13730 $mime = 'text/xml';
13731 $imgmime = 'other.png';
13732 $srclang = 'xml';
13733 $famime = 'file-alt';
13734 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
13735 $mime = 'text/xml';
13736 $imgmime = 'other.png';
13737 $srclang = 'xaml';
13738 $famime = 'file-alt';
13739 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
13740 $mime = 'text/plain';
13741 $imgmime = 'text.png';
13742 $srclang = 'bas';
13743 $famime = 'file-code';
13744 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
13745 $mime = 'text/plain';
13746 $imgmime = 'text.png';
13747 $srclang = 'c';
13748 $famime = 'file-code';
13749 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
13750 $mime = 'text/plain';
13751 $imgmime = 'text.png';
13752 $srclang = 'cpp';
13753 $famime = 'file-code';
13754 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
13755 $mime = 'text/plain';
13756 $imgmime = 'text.png';
13757 $srclang = 'cs';
13758 $famime = 'file-code';
13759 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
13760 $mime = 'text/plain';
13761 $imgmime = 'text.png';
13762 $srclang = 'h';
13763 $famime = 'file-code';
13764 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
13765 $mime = 'text/plain';
13766 $imgmime = 'text.png';
13767 $srclang = 'java';
13768 $famime = 'file-code';
13769 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
13770 $mime = 'text/plain';
13771 $imgmime = 'php.png';
13772 $srclang = 'php';
13773 $famime = 'file-code';
13774 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
13775 $mime = 'text/plain';
13776 $imgmime = 'php.png';
13777 $srclang = 'php';
13778 $famime = 'file-code';
13779 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
13780 $mime = 'text/plain';
13781 $imgmime = 'pl.png';
13782 $srclang = 'perl';
13783 $famime = 'file-code';
13784 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
13785 $mime = 'text/plain';
13786 $imgmime = 'text.png';
13787 $srclang = 'sql';
13788 $famime = 'file-code';
13789 } elseif (preg_match('/\.js$/i', $tmpfile)) {
13790 $mime = 'text/x-javascript';
13791 $imgmime = 'jscript.png';
13792 $srclang = 'js';
13793 $famime = 'file-code';
13794 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
13795 $mime = 'application/vnd.oasis.opendocument.presentation';
13796 $imgmime = 'ooffice.png';
13797 $famime = 'file-powerpoint';
13798 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
13799 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
13800 $imgmime = 'ooffice.png';
13801 $famime = 'file-excel';
13802 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
13803 $mime = 'application/vnd.oasis.opendocument.text';
13804 $imgmime = 'ooffice.png';
13805 $famime = 'file-word';
13806 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
13807 $mime = 'application/msaccess';
13808 $imgmime = 'mdb.png';
13809 $famime = 'file';
13810 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
13811 $mime = 'application/msword';
13812 $imgmime = 'doc.png';
13813 $famime = 'file-word';
13814 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
13815 $mime = 'application/msword';
13816 $imgmime = 'doc.png';
13817 $famime = 'file-word';
13818 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
13819 $mime = 'application/vnd.ms-excel';
13820 $imgmime = 'xls.png';
13821 $famime = 'file-excel';
13822 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
13823 $mime = 'application/vnd.ms-excel';
13824 $imgmime = 'xls.png';
13825 $famime = 'file-excel';
13826 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
13827 $mime = 'application/vnd.ms-excel';
13828 $imgmime = 'xls.png';
13829 $famime = 'file-excel';
13830 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
13831 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
13832 $imgmime = 'xls.png';
13833 $famime = 'file-excel';
13834 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
13835 $mime = 'application/vnd.ms-powerpoint';
13836 $imgmime = 'ppt.png';
13837 $famime = 'file-powerpoint';
13838 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
13839 $mime = 'application/x-mspowerpoint';
13840 $imgmime = 'ppt.png';
13841 $famime = 'file-powerpoint';
13842 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
13843 $mime = 'application/pdf';
13844 $imgmime = 'pdf.png';
13845 $famime = 'file-pdf';
13846 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
13847 $mime = 'text/x-bat';
13848 $imgmime = 'script.png';
13849 $srclang = 'dos';
13850 $famime = 'file-code';
13851 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
13852 $mime = 'text/x-sh';
13853 $imgmime = 'script.png';
13854 $srclang = 'bash';
13855 $famime = 'file-code';
13856 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
13857 $mime = 'text/x-ksh';
13858 $imgmime = 'script.png';
13859 $srclang = 'bash';
13860 $famime = 'file-code';
13861 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
13862 $mime = 'text/x-bash';
13863 $imgmime = 'script.png';
13864 $srclang = 'bash';
13865 $famime = 'file-code';
13866 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
13867 $mime = 'image/x-icon';
13868 $imgmime = 'image.png';
13869 $famime = 'file-image';
13870 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
13871 $mime = 'image/jpeg';
13872 $imgmime = 'image.png';
13873 $famime = 'file-image';
13874 } elseif (preg_match('/\.png$/i', $tmpfile)) {
13875 $mime = 'image/png';
13876 $imgmime = 'image.png';
13877 $famime = 'file-image';
13878 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
13879 $mime = 'image/gif';
13880 $imgmime = 'image.png';
13881 $famime = 'file-image';
13882 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
13883 $mime = 'image/bmp';
13884 $imgmime = 'image.png';
13885 $famime = 'file-image';
13886 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
13887 $mime = 'image/tiff';
13888 $imgmime = 'image.png';
13889 $famime = 'file-image';
13890 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
13891 $mime = 'image/svg+xml';
13892 $imgmime = 'image.png';
13893 $famime = 'file-image';
13894 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
13895 $mime = 'image/webp';
13896 $imgmime = 'image.png';
13897 $famime = 'file-image';
13898 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
13899 $mime = 'text/calendar';
13900 $imgmime = 'other.png';
13901 $famime = 'file-alt';
13902 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
13903 $mime = 'text/calendar';
13904 $imgmime = 'other.png';
13905 $famime = 'file-alt';
13906 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
13907 $mime = 'application/x-bittorrent';
13908 $imgmime = 'other.png';
13909 $famime = 'file-o';
13910 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
13911 $mime = 'audio';
13912 $imgmime = 'audio.png';
13913 $famime = 'file-audio';
13914 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
13915 $mime = 'video/mp4';
13916 $imgmime = 'video.png';
13917 $famime = 'file-video';
13918 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
13919 $mime = 'video/ogg';
13920 $imgmime = 'video.png';
13921 $famime = 'file-video';
13922 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
13923 $mime = 'video/webm';
13924 $imgmime = 'video.png';
13925 $famime = 'file-video';
13926 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
13927 $mime = 'video/x-msvideo';
13928 $imgmime = 'video.png';
13929 $famime = 'file-video';
13930 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
13931 $mime = 'video/divx';
13932 $imgmime = 'video.png';
13933 $famime = 'file-video';
13934 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
13935 $mime = 'video/xvid';
13936 $imgmime = 'video.png';
13937 $famime = 'file-video';
13938 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
13939 $mime = 'video';
13940 $imgmime = 'video.png';
13941 $famime = 'file-video';
13942 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
13943 // application/xxx where zzz is zip, ...
13944 $mime = 'archive';
13945 $imgmime = 'archive.png';
13946 $famime = 'file-archive';
13947 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
13948 $mime = 'application/octet-stream';
13949 $imgmime = 'other.png';
13950 $famime = 'file-o';
13951 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
13952 $mime = 'library';
13953 $imgmime = 'library.png';
13954 $famime = 'file-o';
13955 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13956 $mime = 'error';
13957 $imgmime = 'error.png';
13958 $famime = 'file-alt';
13959 }
13960
13961 if ($famime == 'file-o') {
13962 // file-o seems to not work in fontawesome 5
13963 $famime = 'file';
13964 }
13965
13966 // Return mimetype string
13967 switch ((int) $mode) {
13968 case 1:
13969 $tmp = explode('/', $mime);
13970 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13971 case 2:
13972 return $imgmime;
13973 case 3:
13974 return $srclang;
13975 case 4:
13976 return $famime;
13977 }
13978 return $mime;
13979}
13980
13992function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
13993{
13994 global $conf, $db;
13995
13996 $tablename = preg_replace('/^' . preg_quote(MAIN_DB_PREFIX, '/') . '/', '', $tablename); // Clean name of table for backward compatibility.
13997
13998 $dictvalues = (isset($conf->cache['dictvalues_' . $tablename]) ? $conf->cache['dictvalues_' . $tablename] : null);
13999
14000 if (is_null($dictvalues)) {
14001 $dictvalues = array();
14002
14003 $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
14004 if ($checkentity) {
14005 $sql .= ' AND entity IN (0,' . getEntity($tablename) . ')';
14006 }
14007
14008 $resql = $db->query($sql);
14009 if ($resql) {
14010 while ($obj = $db->fetch_object($resql)) {
14011 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
14012 }
14013 } else {
14014 dol_print_error($db);
14015 }
14016
14017 $conf->cache['dictvalues_' . $tablename] = $dictvalues;
14018 }
14019
14020 if (!empty($dictvalues[$id])) {
14021 // Found
14022 $tmp = $dictvalues[$id];
14023 return (property_exists($tmp, $field) ? $tmp->$field : '');
14024 } else {
14025 // Not found
14026 return '';
14027 }
14028}
14029
14036function colorIsLight($stringcolor)
14037{
14038 $stringcolor = str_replace('#', '', $stringcolor);
14039 $res = -1;
14040 if (!empty($stringcolor)) {
14041 $res = 0;
14042 $tmp = explode(',', $stringcolor);
14043 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
14044 $r = $tmp[0];
14045 $g = $tmp[1];
14046 $b = $tmp[2];
14047 } else {
14048 $hexr = $stringcolor[0] . $stringcolor[1];
14049 $hexg = $stringcolor[2] . $stringcolor[3];
14050 $hexb = $stringcolor[4] . $stringcolor[5];
14051 $r = hexdec($hexr);
14052 $g = hexdec($hexg);
14053 $b = hexdec($hexb);
14054 }
14055 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
14056 if ($bright > 0.6) {
14057 $res = 1;
14058 }
14059 }
14060 return $res;
14061}
14062
14071function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
14072{
14073 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
14074 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
14075 if (empty($menuentry['enabled'])) {
14076 return 0; // Entry disabled by condition
14077 }
14078 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
14079 $tmploops = explode('|', $menuentry['module']);
14080 $found = 0;
14081 foreach ($tmploops as $tmploop) {
14082 if (in_array($tmploop, $listofmodulesforexternal)) {
14083 $found++;
14084 break;
14085 }
14086 }
14087 if (!$found) {
14088 return 0; // Entry is for menus all excluded to external users
14089 }
14090 }
14091 if (!$menuentry['perms'] && $type_user) {
14092 return 0; // No permissions and user is external
14093 }
14094 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
14095 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
14096 }
14097 if (!$menuentry['perms']) {
14098 return 2; // No permissions and user is external
14099 }
14100 return 1;
14101}
14102
14110function roundUpToNextMultiple($n, $x = 5)
14111{
14112 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
14113 return (int) $result;
14114}
14115
14127function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
14128{
14129 $csstouse = 'badge';
14130 $csstouse .= (!empty($mode) ? ' badge-' . $mode : '');
14131 $csstouse .= (!empty($type) ? ' badge-' . $type : '');
14132 $csstouse .= (empty($params['css']) ? '' : ' ' . $params['css']);
14133
14134 $attr = array(
14135 'class' => $csstouse
14136 );
14137
14138 if (empty($html)) {
14139 $html = $label;
14140 }
14141
14142 if (!empty($url)) {
14143 $attr['href'] = $url;
14144 }
14145
14146 if ($mode === 'dot') {
14147 $attr['class'] .= ' classfortooltip';
14148 $attr['title'] = $html;
14149 $attr['aria-label'] = $label;
14150 $html = '';
14151 }
14152
14153 // Override attr
14154 if (!empty($params['attr']) && is_array($params['attr'])) {
14155 foreach ($params['attr'] as $key => $value) {
14156 if ($key == 'class') {
14157 $attr['class'] .= ' ' . $value;
14158 } elseif ($key == 'classOverride') {
14159 $attr['class'] = $value;
14160 } else {
14161 $attr[$key] = $value;
14162 }
14163 }
14164 }
14165
14166 // TODO: add hook
14167
14168 // escape all attribute
14169 $attr = array_map('dolPrintHTMLForAttribute', $attr);
14170
14171 $TCompiledAttr = array();
14172 foreach ($attr as $key => $value) {
14173 $TCompiledAttr[] = $key . '="' . $value . '"';
14174 }
14175
14176 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
14177
14178 $tag = !empty($url) ? 'a' : 'span';
14179
14180 return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
14181}
14182
14183
14196function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
14197{
14198 global $conf;
14199
14200 $return = '';
14201 $dolGetBadgeParams = array();
14202
14203 if (!empty($params['badgeParams'])) {
14204 $dolGetBadgeParams = $params['badgeParams'];
14205 }
14206
14207 // TODO : add a hook
14208 if ($displayMode == 0) {
14209 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
14210 } elseif ($displayMode == 1) {
14211 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14212 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
14213 // Use status with images (for backward compatibility)
14214 $return = '';
14215 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : $statusLabel) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
14216 $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>' : '');
14217
14218 // For small screen, we always use the short label instead of long label.
14219 if (!empty($conf->dol_optimize_smallscreen)) {
14220 if ($displayMode == 0) {
14221 $displayMode = 1;
14222 } elseif ($displayMode == 4) {
14223 $displayMode = 2;
14224 } elseif ($displayMode == 6) {
14225 $displayMode = 5;
14226 }
14227 }
14228
14229 // For backward compatibility. Image's filename are still in French, so we use this array to convert
14230 $statusImg = array(
14231 'status0' => 'statut0',
14232 'status1' => 'statut1',
14233 'status2' => 'statut2',
14234 'status3' => 'statut3',
14235 'status4' => 'statut4',
14236 'status5' => 'statut5',
14237 'status6' => 'statut6',
14238 'status7' => 'statut7',
14239 'status8' => 'statut8',
14240 'status9' => 'statut9'
14241 );
14242
14243 if (!empty($statusImg[$statusType])) {
14244 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
14245 } else {
14246 $htmlImg = img_picto($statusLabel, $statusType);
14247 }
14248
14249 if ($displayMode === 2) {
14250 $return = $htmlImg . ' ' . $htmlLabelShort;
14251 } elseif ($displayMode === 3) {
14252 $return = $htmlImg;
14253 } elseif ($displayMode === 4) {
14254 $return = $htmlImg . ' ' . $htmlLabel;
14255 } elseif ($displayMode === 5) {
14256 $return = $htmlLabelShort . ' ' . $htmlImg;
14257 } else { // $displayMode >= 6
14258 $return = $htmlLabel . ' ' . $htmlImg;
14259 }
14260 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
14261 // Use new badge
14262 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14263
14264 $dolGetBadgeParams['attr']['class'] = 'badge-status';
14265 if (empty($dolGetBadgeParams['attr']['title'])) {
14266 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
14267 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
14268 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
14269 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
14270 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
14271 }
14272
14273 if ($displayMode == 3) {
14274 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
14275 } elseif ($displayMode === 5) {
14276 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
14277 } else {
14278 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
14279 }
14280 }
14281
14282 return $return;
14283}
14284
14285
14321function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
14322{
14323 global $hookmanager, $action, $object, $langs;
14324
14325 // If $url is an array, we must build a dropdown button or recursively iterate over each value
14326 if (is_array($url)) {
14327 // Loop on $url array to remove entries of disabled modules
14328 foreach ($url as $key => $subbutton) {
14329 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
14330 unset($url[$key]);
14331 }
14332 }
14333
14334 $out = '';
14335
14336 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
14337 foreach ($url as $button) {
14338 if (!empty($button['lang'])) {
14339 $langs->load($button['lang']);
14340 }
14341 $label = $langs->trans($button['label']);
14342 $text = $button['text'] ?? '';
14343 $actionType = $button['actionType'] ?? '';
14344 $tmpUrl = DOL_URL_ROOT . $button['url'] . (empty($params['backtopage']) ? '' : '&amp;backtopage=' . urlencode($params['backtopage']));
14345 $id = $button['id'] ?? '';
14346 $userRight = $button['perm'] ?? 1;
14347 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
14348
14349 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
14350 }
14351 return $out;
14352 }
14353
14354 if (count($url) > 1) {
14355 $out .= '<div class="dropdown inline-block dropdown-holder">';
14356 $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>';
14357 $out .= '<div class="dropdown-content">';
14358 foreach ($url as $subbutton) {
14359 if (!empty($subbutton['lang'])) {
14360 $langs->load($subbutton['lang']);
14361 }
14362
14363 if (!empty($subbutton['urlraw'])) {
14364 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14365 } else {
14366 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14367 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14368 }
14369
14370 $subbuttonparam = array();
14371 if (!empty($subbutton['attr'])) {
14372 $subbuttonparam['attr'] = $subbutton['attr'];
14373 }
14374 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
14375
14376 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
14377 }
14378 $out .= "</div>";
14379 $out .= "</div>";
14380 } else {
14381 foreach ($url as $subbutton) { // Should loop on 1 record only
14382 if (!empty($subbutton['lang'])) {
14383 $langs->load($subbutton['lang']);
14384 }
14385
14386 if (!empty($subbutton['urlraw'])) {
14387 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14388 } else {
14389 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14390 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14391 }
14392
14393 $label = $langs->trans($subbutton['label']);
14394 $text = $subbutton['text'] ?? '';
14395 if (empty($text)) {
14396 $text = $label;
14397 $label = '';
14398 }
14399
14400 $out .= dolGetButtonAction($label, $text, 'default', $tmpurl, '', $subbutton['perm'], $params);
14401 }
14402 }
14403
14404 return $out;
14405 }
14406
14407 // Here, $url is a simple link
14408 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
14409 $class = "dropdown-item";
14410 } else {
14411 $class = 'butAction';
14412 if ($actionType == 'edit') {
14413 $class = 'butAction butActionEdit';
14414 } elseif ($actionType == 'email') {
14415 $class = 'butAction butActionEmail';
14416 } elseif ($actionType == 'clone') {
14417 $class = 'butAction butActionClone';
14418 } elseif ($actionType == 'danger' || $actionType == 'delete') {
14419 $class = 'butAction butActionDelete';
14420 if (!empty($url) && strpos($url, 'token=') === false) {
14421 $url .= '&token=' . newToken();
14422 }
14423 }
14424 }
14425 $attr = array(
14426 'class' => $class,
14427 'href' => empty($url) ? '' : $url,
14428 'title' => $label
14429 );
14430
14431 if (empty($text)) {
14432 $text = $label;
14433 $attr['title'] = ''; // if html not set, using label on title is redundant
14434 } else {
14435 $attr['title'] = $label;
14436 $attr['aria-label'] = $label;
14437 }
14438
14439 if (empty($userRight) || $userRight < 0) {
14440 $attr['class'] = 'butActionRefused';
14441 $attr['href'] = '';
14442 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
14443 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '');
14444 $attr['title'] .= ((empty($userRight) && empty($label)) ? $langs->trans('NotEnoughPermissions') : '');
14445 }
14446
14447 if (!empty($id)) {
14448 $attr['id'] = $id;
14449 }
14450
14451 // Override attr
14452 if (!empty($params['attr']) && is_array($params['attr'])) {
14453 foreach ($params['attr'] as $key => $value) {
14454 if ($key == 'class') {
14455 $attr['class'] .= ' ' . $value;
14456 } elseif ($key == 'classOverride') {
14457 $attr['class'] = $value;
14458 } else {
14459 $attr[$key] = $value;
14460 }
14461 }
14462 }
14463
14464 // automatic add tooltip when title is detected
14465 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
14466 $attr['class'] .= ' classfortooltip';
14467 }
14468
14469 // Js Confirm button
14470 if ($userRight && !empty($params['confirm'])) {
14471 if (!is_array($params['confirm'])) {
14472 $params['confirm'] = array();
14473 }
14474
14475 if (empty($params['confirm']['url'])) {
14476 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
14477 }
14478
14479 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
14480 $attr['data-confirm-url'] = $params['confirm']['url'];
14481 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
14482 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
14483 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
14484 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
14485 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
14486 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
14487
14488 $attr['class'] .= ' butActionConfirm';
14489 }
14490
14491 if (isset($attr['href']) && empty($attr['href'])) {
14492 unset($attr['href']);
14493 }
14494
14495 // TODO replace $TCompiledAttr generation by commonHtmlAttributeBuilder given below
14496 $TCompiledAttr = array();
14497 foreach ($attr as $key => $value) {
14498 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
14499 // Not recommended
14500 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
14501 } elseif ($key == 'href') {
14502 $value = dolPrintHTMLForAttributeUrl($value);
14503 } else {
14504 $value = dolPrintHTMLForAttribute($value);
14505 }
14506
14507 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14508 }
14509 // TODO replace $TCompiledAttr generation by uncomment line below and remove old code
14510 // $TCompiledAttr = commonHtmlAttributeBuilder($attr,$params['use_unsecured_unescapedattr'] ?? []);
14511 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
14512
14513 $tag = !empty($attr['href']) ? 'a' : 'span';
14514
14515 $parameters = array(
14516 'TCompiledAttr' => $TCompiledAttr, // array
14517 'compiledAttributes' => $compiledAttributes, // string
14518 'attr' => $attr,
14519 'tag' => $tag,
14520 'label' => $label,
14521 'html' => $text,
14522 'actionType' => $actionType,
14523 'url' => $url,
14524 'id' => $id,
14525 'userRight' => $userRight,
14526 'params' => $params
14527 );
14528
14529 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14530 if ($reshook < 0) {
14531 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
14532 }
14533
14534 if (empty($reshook)) {
14535 if (dol_textishtml($text)) { // If content already HTML encoded
14536 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
14537 } else {
14538 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
14539 }
14540 } else {
14541 return $hookmanager->resPrint;
14542 }
14543}
14544
14580function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
14581{
14582 $TCompiledAttr = array();
14583 if (empty($attr)) {
14584 return [];
14585 }
14586
14587 foreach ($attr as $key => $value) {
14588 // special boolean attributes case
14589 if (in_array($key, getListOfHtmlBooleanAttributes())) {
14590 if ($value) {
14591 $TCompiledAttr[$key] = $key;
14592 }
14593 continue;
14594 }
14595
14596 if (!empty($unescapedAttr) && in_array($key, $unescapedAttr)) {
14597 // Not recommended
14598 $value = dol_htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE);
14599 } elseif ($key == 'href') {
14600 $value = dolPrintHTMLForAttributeUrl((string) $value);
14601 } else {
14602 $value = dolPrintHTMLForAttribute((string) $value);
14603 }
14604
14605 $TCompiledAttr[$key] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14606 }
14607
14608 return $TCompiledAttr;
14609}
14610
14625function getListOfHtmlBooleanAttributes(): array
14626{
14627 return [
14628 // Input / Form
14629 'checked',
14630 'disabled',
14631 'readonly',
14632 'required',
14633 'autofocus',
14634 'multiple',
14635
14636 // Option
14637 'selected',
14638
14639 // Form / General
14640 'novalidate',
14641 'formnovalidate',
14642
14643 // Media
14644 'autoplay',
14645 'controls',
14646 'loop',
14647 'muted',
14648 'playsinline',
14649
14650 // Other elements
14651 'hidden',
14652 'open',
14653 'ismap',
14654 'reversed',
14655 'allowfullscreen',
14656 'itemscope',
14657 'nomodule',
14658 'defer',
14659 'async',
14660 'default',
14661 'inert',
14662 ];
14663}
14664
14665
14674function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
14675{
14676 if (empty($url)) {
14677 return '';
14678 }
14679
14680 $parsedUrl = parse_url($url);
14681 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
14682 return $url;
14683 }
14684
14685 if (!empty($parsedUrl['query'])) {
14686 // Use parse_str() function to parse the string passed via URL
14687 parse_str($parsedUrl['query'], $urlQuery);
14688 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
14689 $url .= '&amp;backtopage=' . urlencode($params['backtopage']);
14690 }
14691 }
14692
14693 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
14694 $url = DOL_URL_ROOT . $url;
14695 }
14696
14697 return $url;
14698}
14699
14700
14707function dolGetButtonTitleSeparator($moreClass = "")
14708{
14709 return '<span class="button-title-separator ' . $moreClass . '" ></span>';
14710}
14711
14718function getFieldErrorIcon($fieldValidationErrorMsg)
14719{
14720 $out = '';
14721 if (!empty($fieldValidationErrorMsg)) {
14722 $out .= '<span class="field-error-icon classfortooltip" title="' . dol_escape_htmltag($fieldValidationErrorMsg, 1) . '" role="alert" >'; // role alert is used for accessibility
14723 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
14724 $out .= '</span>';
14725 }
14726
14727 return $out;
14728}
14729
14742function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
14743{
14744 global $langs, $user;
14745
14746 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
14747 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
14748 return '';
14749 }
14750 // Fix old picto fa-th-list to use fa-grid-vertical instead
14751 if ($iconClass == 'fa fa-th-list imgforviewmode') {
14752 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
14753 }
14754
14755 $class = 'btnTitle';
14756 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
14757 $class .= ' btnTitlePlus';
14758 }
14759 $useclassfortooltip = 1;
14760
14761 if (!empty($params['morecss'])) {
14762 $class .= ' ' . $params['morecss'];
14763 }
14764
14765 $attr = array(
14766 'class' => $class,
14767 'href' => empty($url) ? '' : $url
14768 );
14769
14770 if (!empty($helpText)) {
14771 $attr['title'] = $helpText;
14772 } elseif ($label) { // empty($attr['title']) &&
14773 $attr['title'] = $label;
14774 $useclassfortooltip = 0;
14775 }
14776
14777 if ($status == 2) {
14778 $attr['class'] .= ' btnTitleSelected';
14779 } elseif ($status <= 0) {
14780 $attr['class'] .= ' refused';
14781
14782 $attr['href'] = '';
14783
14784 if ($status == -1) { // disable
14785 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
14786 } elseif ($status == 0) { // Not enough permissions
14787 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
14788 }
14789 }
14790
14791 if (!empty($attr['title']) && $useclassfortooltip) {
14792 $attr['class'] .= ' classfortooltip';
14793 }
14794
14795 if (!empty($id)) {
14796 $attr['id'] = $id;
14797 }
14798
14799 // Override attr
14800 if (!empty($params['attr']) && is_array($params['attr'])) {
14801 foreach ($params['attr'] as $key => $value) {
14802 if ($key == 'class') {
14803 $attr['class'] .= ' ' . $value;
14804 } elseif ($key == 'classOverride') {
14805 $attr['class'] = $value;
14806 } else {
14807 $attr[$key] = $value;
14808 }
14809 }
14810 }
14811
14812 if (isset($attr['href']) && empty($attr['href'])) {
14813 unset($attr['href']);
14814 }
14815
14816 // TODO : add a hook
14817
14818 // Generate attributes with escapement
14819 $TCompiledAttr = array();
14820 foreach ($attr as $key => $value) {
14821 $TCompiledAttr[] = $key . '="' . dol_escape_htmltag($value) . '"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
14822 }
14823
14824 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
14825
14826 $tag = (empty($attr['href']) ? 'span' : 'a');
14827
14828 $button = '<' . $tag . ' ' . $compiledAttributes . '>';
14829 $button .= '<span class="' . $iconClass . ' valignmiddle btnTitle-icon"></span>';
14830 if (!empty($params['forcenohideoftext'])) {
14831 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label' . (empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '') . '">' . $label . '</span>';
14832 }
14833 $button .= '</' . $tag . '>';
14834
14835 return $button;
14836}
14837
14847function getElementProperties($elementType)
14848{
14849 global $conf, $db, $hookmanager;
14850
14851 $regs = array();
14852
14853 //$element_type='facture';
14854
14855 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
14856
14857 // Parse element/subelement
14858 $module = $elementType;
14859 $element = $elementType;
14860 $subelement = $elementType;
14861 $table_element = $elementType;
14862
14863 // If we ask a resource form external module (instead of default path)
14864 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
14865 $element = $subelement = $regs[1];
14866 $module = $regs[2];
14867 }
14868
14869 // If we ask a resource for a string with an element and a subelement
14870 // Example 'project_task'
14871 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
14872 $module = $element = $regs[1];
14873 $subelement = $regs[2];
14874 }
14875
14876 // Object lines will use parent classpath and module ref
14877 if (substr($elementType, -3) == 'det') {
14878 $module = preg_replace('/det$/', '', $element);
14879 $subelement = preg_replace('/det$/', '', $subelement);
14880 $classpath = $module . '/class';
14881 $classfile = $module;
14882 $classname = preg_replace('/det$/', 'Line', $element);
14883 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'supplier_order', 'commandefournisseur'))) {
14884 $classname = preg_replace('/det$/', 'Ligne', $element);
14885 }
14886 }
14887 // For compatibility and to work with non standard path
14888 if ($elementType == "action" || $elementType == "actioncomm") {
14889 $classpath = 'comm/action/class';
14890 $subelement = 'Actioncomm';
14891 $module = 'agenda';
14892 $table_element = 'actioncomm';
14893 } elseif ($elementType == 'cronjob') {
14894 $classpath = 'cron/class';
14895 $module = 'cron';
14896 $table_element = 'cron';
14897 } elseif ($elementType == 'adherent_type') {
14898 $classpath = 'adherents/class';
14899 $classfile = 'adherent_type';
14900 $module = 'adherent';
14901 $subelement = 'adherent_type';
14902 $classname = 'AdherentType';
14903 $table_element = 'adherent_type';
14904 } elseif ($elementType == 'bank_account') {
14905 $classpath = 'compta/bank/class';
14906 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14907 $classfile = 'account';
14908 $classname = 'Account';
14909 } elseif ($elementType == 'bank_line') {
14910 $classpath = 'compta/bank/class';
14911 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14912 $classfile = 'account';
14913 $classname = 'AccountLine';
14914 } elseif ($elementType == 'category') {
14915 $classpath = 'categories/class';
14916 $module = 'categorie';
14917 $subelement = 'categorie';
14918 $table_element = 'categorie';
14919 } elseif ($elementType == 'contact') {
14920 $classpath = 'contact/class';
14921 $classfile = 'contact';
14922 $module = 'societe';
14923 $subelement = 'contact';
14924 $table_element = 'socpeople';
14925 } elseif ($elementType == 'inventory') {
14926 $module = 'product';
14927 $classpath = 'product/inventory/class';
14928 } elseif ($elementType == 'inventoryline') {
14929 $module = 'product';
14930 $classpath = 'product/inventory/class';
14931 $table_element = 'inventorydet';
14932 $parent_element = 'inventory';
14933 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
14934 $module = 'stock';
14935 $classpath = 'product/stock/class';
14936 $classfile = 'entrepot';
14937 $classname = 'Entrepot';
14938 $table_element = 'entrepot';
14939 } elseif ($elementType == 'project') {
14940 $classpath = 'projet/class';
14941 $module = 'projet';
14942 $table_element = 'projet';
14943 } elseif ($elementType == 'project_task') {
14944 $classpath = 'projet/class';
14945 $module = 'projet';
14946 $subelement = 'task';
14947 $table_element = 'projet_task';
14948 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
14949 $classpath = 'compta/facture/class';
14950 $module = 'facture';
14951 $subelement = 'facture';
14952 $table_element = 'facture';
14953 } elseif ($elementType == 'facturedet') {
14954 $classpath = 'compta/facture/class';
14955 $classfile = 'facture';
14956 $classname = 'FactureLigne';
14957 $module = 'facture';
14958 $table_element = 'facturedet';
14959 $parent_element = 'facture';
14960 } elseif ($elementType == 'facturerec'|| $elementType == 'facture_rec') {
14961 $classpath = 'compta/facture/class';
14962 $classfile = 'facture-rec';
14963 $module = 'facture';
14964 $classname = 'FactureRec';
14965 } elseif ($elementType == 'commande' || $elementType == 'order') {
14966 $classpath = 'commande/class';
14967 $module = 'commande';
14968 $subelement = 'commande';
14969 $table_element = 'commande';
14970 } elseif ($elementType == 'commandedet') {
14971 $classpath = 'commande/class';
14972 $classfile = 'commande';
14973 $classname = 'OrderLine';
14974 $module = 'commande';
14975 $table_element = 'commandedet';
14976 $parent_element = 'commande';
14977 } elseif ($elementType == 'propal') {
14978 $classpath = 'comm/propal/class';
14979 $table_element = 'propal';
14980 } elseif ($elementType == 'propaldet') {
14981 $classpath = 'comm/propal/class';
14982 $classfile = 'propal';
14983 $subelement = 'propaleligne';
14984 $module = 'propal';
14985 $table_element = 'propaldet';
14986 $parent_element = 'propal';
14987 } elseif ($elementType == 'shipping') {
14988 $classpath = 'expedition/class';
14989 $classfile = 'expedition';
14990 $classname = 'Expedition';
14991 $module = 'expedition';
14992 $table_element = 'expedition';
14993 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
14994 $classpath = 'expedition/class';
14995 $classfile = 'expedition';
14996 $classname = 'ExpeditionLigne';
14997 $module = 'expedition';
14998 $table_element = 'expeditiondet';
14999 $parent_element = 'expedition';
15000 } elseif ($elementType == 'delivery_note') {
15001 $classpath = 'delivery/class';
15002 $subelement = 'delivery';
15003 $module = 'expedition';
15004 } elseif ($elementType == 'delivery') {
15005 $classpath = 'delivery/class';
15006 $subelement = 'delivery';
15007 $module = 'expedition';
15008 } elseif ($elementType == 'deliverydet') {
15009 // @todo
15010 } elseif ($elementType == 'supplier_proposal') {
15011 $classpath = 'supplier_proposal/class';
15012 $module = 'supplier_proposal';
15013 $element = 'supplierproposal';
15014 $classfile = 'supplier_proposal';
15015 $subelement = 'supplierproposal';
15016 } elseif ($elementType == 'supplier_proposaldet') {
15017 $classpath = 'supplier_proposal/class';
15018 $module = 'supplier_proposal';
15019 $classfile = 'supplier_proposal';
15020 $classname = 'SupplierProposalLine';
15021 $table_element = 'supplier_proposaldet';
15022 $parent_element = 'supplier_proposal';
15023 } elseif ($elementType == 'contract') {
15024 $classpath = 'contrat/class';
15025 $module = 'contrat';
15026 $subelement = 'contrat';
15027 $table_element = 'contract';
15028 } elseif ($elementType == 'contratdet') {
15029 $classpath = 'contrat/class';
15030 $module = 'contrat';
15031 $table_element = 'contratdet';
15032 $parent_element = 'contrat';
15033 } elseif ($elementType == 'mailing') {
15034 $classpath = 'comm/mailing/class';
15035 $module = 'mailing';
15036 $classfile = 'mailing';
15037 $classname = 'Mailing';
15038 $subelement = '';
15039 } elseif ($elementType == 'member' || $elementType == 'adherent') {
15040 $classpath = 'adherents/class';
15041 $module = 'adherent';
15042 $subelement = 'adherent';
15043 $table_element = 'adherent';
15044 } elseif ($elementType == 'subscription') {
15045 $classpath = 'adherents/class';
15046 $classfile = 'subscription';
15047 $module = 'adherent';
15048 $subelement = 'subscription';
15049 $classname = 'Subscription';
15050 $table_element = 'subscription';
15051 } elseif ($elementType == 'usergroup') {
15052 $classpath = 'user/class';
15053 $module = 'user';
15054 } elseif ($elementType == 'mo' || $elementType == 'mrp') {
15055 $classpath = 'mrp/class';
15056 $classfile = 'mo';
15057 $classname = 'Mo';
15058 $module = 'mrp';
15059 $subelement = '';
15060 $table_element = 'mrp_mo';
15061 } elseif ($elementType == 'mrp_production') {
15062 $classpath = 'mrp/class';
15063 $classfile = 'mo';
15064 $classname = 'MoLine';
15065 $module = 'mrp';
15066 $subelement = '';
15067 $table_element = 'mrp_production';
15068 $parent_element = 'mo';
15069 } elseif ($elementType == 'cabinetmed_cons') {
15070 $classpath = 'cabinetmed/class';
15071 $module = 'cabinetmed';
15072 $subelement = 'cabinetmedcons';
15073 $table_element = 'cabinetmedcons';
15074 } elseif ($elementType == 'fichinter') {
15075 $classpath = 'fichinter/class';
15076 $module = 'ficheinter';
15077 $subelement = 'fichinter';
15078 $table_element = 'fichinter';
15079 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
15080 $classpath = 'resource/class';
15081 $module = 'resource';
15082 $subelement = 'dolresource';
15083 $table_element = 'resource';
15084 } elseif ($elementType == 'opensurvey_sondage') {
15085 $classpath = 'opensurvey/class';
15086 $module = 'opensurvey';
15087 $subelement = 'opensurveysondage';
15088 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' || $elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
15089 $classpath = 'fourn/class';
15090 $module = 'fournisseur';
15091 $classfile = 'fournisseur.commande';
15092 $element = 'order_supplier';
15093 $subelement = '';
15094 $classname = 'CommandeFournisseur';
15095 $table_element = 'commande_fournisseur';
15096 } elseif ($elementType == 'commande_fournisseurdet') {
15097 $classpath = 'fourn/class';
15098 $module = 'fournisseur';
15099 $classfile = 'fournisseur.commande';
15100 $element = 'commande_fournisseurdet';
15101 $subelement = '';
15102 $classname = 'CommandeFournisseurLigne';
15103 $table_element = 'commande_fournisseurdet';
15104 $parent_element = 'commande_fournisseur';
15105 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
15106 $classpath = 'fourn/class';
15107 $module = 'fournisseur';
15108 $classfile = 'fournisseur.facture';
15109 $element = 'invoice_supplier';
15110 $subelement = '';
15111 $classname = 'FactureFournisseur';
15112 $table_element = 'facture_fourn';
15113 } elseif ($elementType == 'facture_fourn_det') {
15114 $classpath = 'fourn/class';
15115 $module = 'fournisseur';
15116 $classfile = 'fournisseur.facture';
15117 $element = 'facture_fourn_det';
15118 $subelement = '';
15119 $classname = 'SupplierInvoiceLine';
15120 $table_element = 'facture_fourn_det';
15121 $parent_element = 'invoice_supplier';
15122 } elseif ($elementType == "service") {
15123 $classpath = 'product/class';
15124 $subelement = 'product';
15125 $table_element = 'product';
15126 } elseif ($elementType == 'product_attribute') {
15127 $module = 'variants';
15128 $element = 'product_attribute';
15129 $subelement = 'product_attribute';
15130 $classpath = 'variants/class';
15131 $classfile = 'ProductAttribute';
15132 $classname = 'ProductAttribute';
15133 $table_element = 'product_attribute';
15134 } elseif ($elementType == 'product_attribute_value') {
15135 $module = 'variants';
15136 $element = 'product_attribute_value';
15137 $subelement = 'product_attribute_value';
15138 $classpath = 'variants/class';
15139 $classfile = 'ProductAttributeValue';
15140 $classname = 'ProductAttributeValue';
15141 $table_element = 'product_attribute_value';
15142 $parent_element = 'product_attribute';
15143 } elseif ($elementType == 'salary') {
15144 $classpath = 'salaries/class';
15145 $module = 'salaries';
15146 } elseif ($elementType == 'payment_salary') {
15147 $classpath = 'salaries/class';
15148 $classfile = 'paymentsalary';
15149 $classname = 'PaymentSalary';
15150 $module = 'salaries';
15151 } elseif ($elementType == 'productlot') {
15152 $module = 'productbatch';
15153 $classpath = 'product/stock/class';
15154 $classfile = 'productlot';
15155 $classname = 'Productlot';
15156 $element = 'productlot';
15157 $subelement = '';
15158 $table_element = 'product_lot';
15159 } elseif ($elementType == 'societeaccount') {
15160 $classpath = 'societe/class';
15161 $classfile = 'societeaccount';
15162 $classname = 'SocieteAccount';
15163 $module = 'societe';
15164 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
15165 $classpath = 'website/class';
15166 $classfile = 'websitepage';
15167 $classname = 'Websitepage';
15168 $module = 'website';
15169 $subelement = 'websitepage';
15170 $table_element = 'website_page';
15171 } elseif ($elementType == 'fiscalyear') {
15172 $classpath = 'core/class';
15173 $module = 'accounting';
15174 $subelement = 'fiscalyear';
15175 } elseif ($elementType == 'chargesociales') {
15176 $classpath = 'compta/sociales/class';
15177 $module = 'tax';
15178 $table_element = 'chargesociales';
15179 } elseif ($elementType == 'tva') {
15180 $classpath = 'compta/tva/class';
15181 $module = 'tax';
15182 $subdir = '/vat';
15183 $table_element = 'tva';
15184 } elseif ($elementType == 'emailsenderprofile') {
15185 $module = '';
15186 $classpath = 'core/class';
15187 $classfile = 'emailsenderprofile';
15188 $classname = 'EmailSenderProfile';
15189 $table_element = 'c_email_senderprofile';
15190 $subelement = '';
15191 } elseif ($elementType == 'conferenceorboothattendee') {
15192 $classpath = 'eventorganization/class';
15193 $classfile = 'conferenceorboothattendee';
15194 $classname = 'ConferenceOrBoothAttendee';
15195 $module = 'eventorganization';
15196 } elseif ($elementType == 'conferenceorbooth') {
15197 $classpath = 'eventorganization/class';
15198 $classfile = 'conferenceorbooth';
15199 $classname = 'ConferenceOrBooth';
15200 $module = 'eventorganization';
15201 } elseif ($elementType == 'ccountry') {
15202 $module = '';
15203 $classpath = 'core/class';
15204 $classfile = 'ccountry';
15205 $classname = 'Ccountry';
15206 $table_element = 'c_country';
15207 $subelement = '';
15208 } elseif ($elementType == 'ecmfiles') {
15209 $module = 'ecm';
15210 $classpath = 'ecm/class';
15211 $classfile = 'ecmfiles';
15212 $classname = 'Ecmfiles';
15213 $table_element = 'ecmfiles';
15214 $subelement = '';
15215 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
15216 $module = 'knowledgemanagement';
15217 $classpath = 'knowledgemanagement/class';
15218 $classfile = 'knowledgerecord';
15219 $classname = 'KnowledgeRecord';
15220 $table_element = 'knowledgemanagement_knowledgerecord';
15221 $subelement = '';
15222 } elseif ($elementType == 'customer') {
15223 $module = 'societe';
15224 $classpath = 'societe/class';
15225 $classfile = 'client';
15226 $classname = 'Client';
15227 $table_element = 'societe';
15228 $subelement = '';
15229 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
15230 $module = 'societe';
15231 $classpath = 'fourn/class';
15232 $classfile = 'fournisseur';
15233 $classname = 'Fournisseur';
15234 $table_element = 'societe';
15235 $subelement = '';
15236 } elseif ($elementType == 'recruitmentcandidature') {
15237 $module = 'recruitment';
15238 $classfile = 'recruitmentcandidature';
15239 $classpath = 'recruitment/class';
15240 $classname = 'RecruitmentCandidature';
15241 $subelement = 'recruitmentcandidature';
15242 $subdir = '/recruitmentcandidature';
15243 } elseif ($elementType == 'recruitmentjobposition') {
15244 $module = 'recruitment';
15245 $classfile = 'recruitmentjobposition';
15246 $classpath = 'recruitment/class';
15247 $classname = 'RecruitmentJobPosition';
15248 $subelement = 'recruitmentjobposition';
15249 $subdir = '/recruitmentjobposition';
15250 }
15251
15252
15253 if (empty($classfile)) {
15254 $classfile = strtolower($subelement);
15255 }
15256 if (empty($classname)) {
15257 $classname = ucfirst($subelement);
15258 }
15259 if (empty($classpath)) {
15260 $classpath = $module . '/class';
15261 }
15262
15263 //print 'getElementProperties subdir='.$subdir;
15264
15265 // Set dir_output
15266 if ($module && isset($conf->$module)) { // The generic case
15267 if (!empty($conf->$module->multidir_output[$conf->entity])) {
15268 $dir_output = $conf->$module->multidir_output[$conf->entity];
15269 } elseif (!empty($conf->$module->output[$conf->entity])) {
15270 $dir_output = $conf->$module->output[$conf->entity];
15271 } elseif (!empty($conf->$module->dir_output)) {
15272 $dir_output = $conf->$module->dir_output;
15273 }
15274 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
15275 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
15276 } elseif (!empty($conf->$module->temp[$conf->entity])) {
15277 $dir_temp = $conf->$module->temp[$conf->entity];
15278 } elseif (!empty($conf->$module->dir_temp)) {
15279 $dir_temp = $conf->$module->dir_temp;
15280 }
15281 }
15282
15283 // Overwrite value for special cases
15284 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
15285 $dir_output = $conf->fournisseur->commande->dir_output;
15286 $dir_temp = $conf->fournisseur->commande->dir_temp;
15287 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
15288 $dir_output = $conf->fournisseur->facture->dir_output;
15289 $dir_temp = $conf->fournisseur->facture->dir_temp;
15290 }
15291 $dir_output .= $subdir;
15292 $dir_temp .= $subdir;
15293
15294 $elementProperties = array(
15295 'module' => $module,
15296 'element' => $element,
15297 'table_element' => $table_element,
15298 'subelement' => $subelement,
15299 'classpath' => $classpath,
15300 'classfile' => $classfile,
15301 'classname' => $classname,
15302 'dir_output' => $dir_output,
15303 'dir_temp' => $dir_temp,
15304 'parent_element' => $parent_element,
15305 );
15306
15307
15308 // Add hook
15309 if (!is_object($hookmanager)) {
15310 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
15311 $hookmanager = new HookManager($db);
15312 }
15313 $hookmanager->initHooks(array('elementproperties'));
15314
15315
15316 // Hook params
15317 $parameters = array(
15318 'elementType' => $elementType,
15319 'elementProperties' => $elementProperties
15320 );
15321
15322 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
15323
15324 if ($reshook) {
15325 $elementProperties = $hookmanager->resArray;
15326 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
15327 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
15328 }
15329
15330 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
15331 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
15332 unset($hookmanager->contextarray[$key]);
15333 }
15334
15335 return $elementProperties;
15336}
15337
15350function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
15351{
15352 global $db, $conf;
15353
15354 $ret = 0;
15355
15356 $element_prop = getElementProperties($element_type);
15357
15358 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
15359 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
15360 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
15361 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
15362 // of service and we will return properties of a product.
15363 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
15364 } elseif ($element_prop['module'] == 'societeaccount') {
15365 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
15366 } else {
15367 $ismodenabled = isModEnabled($element_prop['module']);
15368 }
15369 //var_dump('element_type='.$element_type);
15370 //var_dump($element_prop);
15371 //var_dump($element_prop['module'].' '.$ismodenabled);
15372 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
15373 if ($useCache === 1 && $element_id > 0
15374 && !empty($conf->cache['fetchObjectByElement'][$element_type])
15375 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15376 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15377 ) {
15378 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
15379 }
15380
15381 dol_include_once('/' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15382
15383 if (class_exists($element_prop['classname'])) {
15384 $className = $element_prop['classname'];
15385 $objecttmp = new $className($db);
15386 '@phan-var-force CommonObject $objecttmp';
15389 if ($element_id > 0 || !empty($element_ref)) {
15390 $ret = $objecttmp->fetch($element_id, $element_ref);
15391 if ($ret >= 0) {
15392 if (empty($objecttmp->module)) {
15393 $objecttmp->module = $element_prop['module'];
15394 }
15395
15396 if ($useCache > 0) {
15397 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
15398 $conf->cache['fetchObjectByElement'][$element_type] = [];
15399 }
15400
15401 // Manage cache limit
15402 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
15403 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
15404 }
15405
15406 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
15407 }
15408
15409 return $objecttmp;
15410 }
15411 } else {
15412 return $objecttmp; // returned an object without fetch
15413 }
15414 } else {
15415 dol_syslog($element_prop['classname'] . ' doesn\'t exists in /' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15416 return -1;
15417 }
15418 }
15419
15420 return $ret;
15421}
15422
15428function getExecutableContent()
15429{
15430 $arrayofregexextension = array(
15431 'htm',
15432 'html',
15433 'shtml',
15434 'js',
15435 'phar',
15436 'php',
15437 'php3',
15438 'php4',
15439 'php5',
15440 'phtml',
15441 'pht',
15442 'pl',
15443 'py',
15444 'cgi',
15445 'ksh',
15446 'sh',
15447 'shtml',
15448 'bash',
15449 'bat',
15450 'cmd',
15451 'wpk',
15452 'exe',
15453 'dmg',
15454 'appimage'
15455 );
15456
15457 return $arrayofregexextension;
15458}
15459
15466function isAFileWithExecutableContent($filename)
15467{
15468 $arrayofregexextension = getExecutableContent();
15469
15470 foreach ($arrayofregexextension as $fileextension) {
15471 if (preg_match('/\.' . preg_quote($fileextension, '/') . '$/i', $filename)) {
15472 return true;
15473 }
15474 }
15475
15476 return false;
15477}
15478
15486function newToken()
15487{
15488 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
15489}
15490
15498function currentToken()
15499{
15500 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
15501}
15502
15508function getNonce()
15509{
15510 global $conf;
15511
15512 if (empty($conf->cache['nonce'])) {
15513 include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
15514 $conf->cache['nonce'] = dolGetRandomBytes(8);
15515 }
15516
15517 return $conf->cache['nonce'];
15518}
15519
15520
15534function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
15535{
15536 global $langs;
15537
15538 print '<div class="div-table-responsive-no-min">';
15539 print '<table class="noborder centpercent">';
15540 print '<tr class="liste_titre">';
15541
15542 print ($emptyColumns < 1) ? '<th>' : '<th colspan="' . ($emptyColumns + 1) . '">';
15543
15544 print '<span class="valignmiddle">' . $langs->trans($header) . '</span>';
15545
15546 if (!empty($link)) {
15547 if (!empty($arguments)) {
15548 print '<a href="' . DOL_URL_ROOT . '/' . $link . '?' . $arguments . '">';
15549 } else {
15550 print '<a href="' . DOL_URL_ROOT . '/' . $link . '">';
15551 }
15552 }
15553
15554 if ($number > -1) {
15555 print '<span class="badge marginleftonlyshort">' . $number . '</span>';
15556 } elseif (!empty($link)) {
15557 print '<span class="badge marginleftonlyshort">...</span>';
15558 }
15559
15560 if (!empty($link)) {
15561 print '</a>';
15562 }
15563
15564 print '</th>';
15565
15566 if ($number < 0 && !empty($link)) {
15567 print '<th class="right">';
15568 print '</th>';
15569 }
15570
15571 print '</tr>';
15572}
15573
15582function finishSimpleTable($addLineBreak = false)
15583{
15584 print '</table>';
15585 print '</div>';
15586
15587 if ($addLineBreak) {
15588 print '<br>';
15589 }
15590}
15591
15603function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
15604{
15605 global $langs;
15606
15607 if ($num === 0) {
15608 print '<tr class="oddeven">';
15609 print '<td colspan="' . $tableColumnCount . '"><span class="opacitymedium">' . $langs->trans($noneWord) . '</span></td>';
15610 print '</tr>';
15611 return;
15612 }
15613
15614 if ($nbofloop === 0) {
15615 // don't show a summary line
15616 return;
15617 }
15618
15619 /* Case already handled above, commented to satisfy phpstan.
15620 if ($num === 0) {
15621 $colspan = $tableColumnCount;
15622 } else
15623 */
15624 if ($num > $nbofloop) {
15625 $colspan = $tableColumnCount;
15626 } else {
15627 $colspan = $tableColumnCount - 1;
15628 }
15629
15630 if ($extraRightColumn) {
15631 $colspan--;
15632 }
15633
15634 print '<tr class="liste_total">';
15635
15636 if ($nbofloop > 0 && $num > $nbofloop) {
15637 print '<td colspan="' . $colspan . '" class="right">' . $langs->trans("XMoreLines", ($num - $nbofloop)) . '</td>';
15638 } else {
15639 print '<td colspan="' . $colspan . '" class="right"> ' . $langs->trans("Total") . '</td>';
15640 print '<td class="right centpercent">' . price($total) . '</td>';
15641 }
15642
15643 if ($extraRightColumn) {
15644 print '<td></td>';
15645 }
15646
15647 print '</tr>';
15648}
15649
15658function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
15659{
15660 if ($method == -1) {
15661 $method = 0;
15662 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
15663 $method = 1;
15664 }
15665 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
15666 $method = 2;
15667 }
15668 }
15669
15670 // Be sure we don't have output buffering enabled to have readfile working correctly
15671 while (ob_get_level()) {
15672 ob_end_flush();
15673 }
15674
15675 // Solution 0
15676 if ($method == 0) {
15677 readfile($fullpath_original_file_osencoded);
15678 } elseif ($method == 1) {
15679 // Solution 1
15680 $handle = fopen($fullpath_original_file_osencoded, "rb");
15681 while (!feof($handle)) {
15682 print fread($handle, 8192);
15683 }
15684 fclose($handle);
15685 } elseif ($method == 2) {
15686 // Solution 2
15687 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
15688 $handle2 = fopen("php://output", "wb");
15689 stream_copy_to_stream($handle1, $handle2);
15690 fclose($handle1);
15691 fclose($handle2);
15692 }
15693}
15694
15704function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
15705{
15706 global $langs;
15707
15708 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
15709
15710 $result = '<span class="clipboardCP' . ($showonlyonhover ? ' clipboardCPShowOnHover valignmiddle' : '') . '">';
15711 if ($texttoshow === 'none') {
15712 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15713 $result .= '<span class="clipboardCPValueToPrint"></span>';
15714 } elseif ($texttoshow) {
15715 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15716 $result .= '<span class="clipboardCPValueToPrint">' . dol_escape_htmltag($texttoshow, 1, 1) . '</span>';
15717 } else {
15718 $result .= '<' . $tag . ' class="clipboardCPValue">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15719 }
15720 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="' . dolPrintHTML($langs->trans("ClickToCopyToClipboard")) . '"></span>';
15721 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
15722 $result .= '<span class="clipboardCPText"></span>';
15723 $result .= '</span>';
15724
15725 return $result;
15726}
15727
15728
15736function jsonOrUnserialize($stringtodecode, $assoc = true)
15737{
15738 $result = json_decode($stringtodecode, $assoc);
15739 if ($result === null) {
15740 $result = unserialize($stringtodecode); // For backward compatibility. Is no more used in recent versions.
15741 }
15742
15743 return $result;
15744}
15745
15746
15763function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
15764{
15765 global $db, $user;
15766
15767 if (is_null($filter) || !is_string($filter) || $filter === '') {
15768 return '';
15769 }
15770 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
15771 $filter = '(' . $filter . ')';
15772 }
15773
15774 $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'
15775 $firstandlastparenthesis = 0;
15776
15777 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
15778 if ($noerror) {
15779 return '1 = 2';
15780 } else {
15781 return 'Filter syntax error - ' . $errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
15782 }
15783 }
15784
15785 // Test the filter syntax
15786 $t = preg_replace_callback('/' . $regexstring . '/i', 'dolForgeDummyCriteriaCallback', $filter);
15787 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
15788 // If the string result contains something else than '()', the syntax was wrong
15789
15790 if (preg_match('/[^\‍(\‍)]/', $t)) {
15791 $tmperrorstr = 'Bad syntax of the search string';
15792 $errorstr = 'Bad syntax of the search string: ' . $filter;
15793 if ($noerror) {
15794 return '1 = 2';
15795 } else {
15796 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - " . $errorstr, LOG_WARNING);
15797 return 'Filter error - ' . $tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
15798 }
15799 }
15800
15801 $ret = ($noand ? "" : " AND ") . ($nopar ? "" : '(') . preg_replace_callback('/' . $regexstring . '/i', 'dolForgeSQLCriteriaCallback', $filter) . ($nopar ? "" : ')');
15802
15803 if (is_object($db)) {
15804 $ret = str_replace('__NOW__', "'" . $db->idate(dol_now()) . "'", $ret);
15805 }
15806 if (is_object($user)) {
15807 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
15808 }
15809
15810 return $ret;
15811}
15812
15820function dolForgeExplodeAnd($sqlfilters)
15821{
15822 $arrayofandtags = array();
15823 $nbofchars = dol_strlen($sqlfilters);
15824
15825 $error = '';
15826 $parenthesislevel = 0;
15827 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
15828 if (!$result) {
15829 return array();
15830 }
15831 if ($parenthesislevel >= 1) {
15832 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
15833 }
15834
15835 $i = 0;
15836 $s = '';
15837 $countparenthesis = 0;
15838 while ($i < $nbofchars) {
15839 $char = dol_substr($sqlfilters, $i, 1);
15840
15841 if ($char == '(') {
15842 $countparenthesis++;
15843 } elseif ($char == ')') {
15844 $countparenthesis--;
15845 }
15846
15847 if ($countparenthesis == 0) {
15848 $char2 = dol_substr($sqlfilters, $i + 1, 1);
15849 $char3 = dol_substr($sqlfilters, $i + 2, 1);
15850 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
15851 // We found a AND
15852 $s = trim($s);
15853 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15854 $s = '(' . $s . ')';
15855 }
15856 $arrayofandtags[] = $s;
15857 $s = '';
15858 $i += 2;
15859 } else {
15860 $s .= $char;
15861 }
15862 } else {
15863 $s .= $char;
15864 }
15865 $i++;
15866 }
15867 if ($s) {
15868 $s = trim($s);
15869 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15870 $s = '(' . $s . ')';
15871 }
15872 $arrayofandtags[] = $s;
15873 }
15874
15875 return $arrayofandtags;
15876}
15877
15887function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
15888{
15889 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
15890 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
15891 $tmp = $sqlfilters;
15892
15893 $nb = dol_strlen($tmp);
15894 $counter = 0;
15895 $parenthesislevel = 0;
15896
15897 $error = '';
15898
15899 $i = 0;
15900 while ($i < $nb) {
15901 $char = dol_substr($tmp, $i, 1);
15902
15903 if ($char == '(') {
15904 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
15905 // We open a parenthesis and it is the first char
15906 $parenthesislevel++;
15907 }
15908 $counter++;
15909 } elseif ($char == ')') {
15910 $nbcharremaining = ($nb - $i - 1);
15911 if ($nbcharremaining >= $counter) {
15912 $parenthesislevel = min($parenthesislevel, $counter - 1);
15913 }
15914 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
15915 $parenthesislevel = $counter;
15916 }
15917 $counter--;
15918 }
15919
15920 if ($counter < 0) {
15921 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15922 $parenthesislevel = 0;
15923 dol_syslog($error, LOG_WARNING);
15924 return false;
15925 }
15926
15927 $i++;
15928 }
15929
15930 if ($counter > 0) {
15931 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15932 $parenthesislevel = 0;
15933 dol_syslog($error, LOG_WARNING);
15934 return false;
15935 }
15936
15937 return true;
15938}
15939
15947function dolForgeDummyCriteriaCallback($matches)
15948{
15949 //dol_syslog("Convert matches ".$matches[1]);
15950 if (empty($matches[1])) {
15951 return '';
15952 }
15953 $tmp = explode(':', $matches[1]);
15954 if (count($tmp) < 3) {
15955 return '';
15956 }
15957
15958 return '()'; // An empty criteria
15959}
15960
15969function dolForgeSQLCriteriaCallback($matches)
15970{
15971 global $db;
15972
15973 //dol_syslog("Convert matches ".$matches[1]);
15974 if (empty($matches[1])) {
15975 return '';
15976 }
15977 $tmp = explode(':', $matches[1], 3);
15978 if (count($tmp) < 3) {
15979 return '';
15980 }
15981
15982 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
15983
15984 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
15985
15986 $realOperator = [
15987 'NOTLIKE' => 'NOT LIKE',
15988 'ISNOT' => 'IS NOT',
15989 'NOTIN' => 'NOT IN',
15990 '!=' => '<>',
15991 ];
15992
15993 if (array_key_exists($operator, $realOperator)) {
15994 $operator = $realOperator[$operator];
15995 }
15996
15997 $tmpescaped = $tmp[2];
15998
15999 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
16000
16001 $regbis = array();
16002
16003 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)
16004 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
16005 $tmpescaped2 = '(';
16006 // Explode and sanitize each element in list
16007 $tmpelemarray = explode(',', $tmpescaped);
16008 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
16009 $reg = array();
16010 $tmpelem = trim($tmpelem);
16011 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
16012 $tmpelemarray[$tmpkey] = "'" . $db->escape($db->sanitize($reg[1], 2, 1, 1, 1)) . "'";
16013 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
16014 } elseif (preg_match('/^[0-9]+$/', (string) $tmpelem)) { // if only 0-9 chars, no .
16015 $tmpelemarray[$tmpkey] = (int) $tmpelem;
16016 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
16017 $tmpelemarray[$tmpkey] = (float) $tmpelem;
16018 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
16019 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
16020 } else {
16021 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
16022 }
16023 }
16024 $tmpescaped2 .= implode(',', $tmpelemarray);
16025 $tmpescaped2 .= ')';
16026
16027 $tmpescaped = $tmpescaped2;
16028 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
16029 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
16030 $tmpescaped = $regbis[1];
16031 }
16032 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
16033 $tmpescaped = "'" . $db->escape($tmpescaped) . "'"; // We do not escape the _ and % so the LIKE will work as expected
16034 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
16035 // TODO Retrieve type of field for $operand field name.
16036 // So we can complete format. For example we could complete a year with month and day.
16037 $tmpescaped = "'" . $db->escape($regbis[1]) . "'";
16038 } else {
16039 if (strtoupper($tmpescaped) == 'NULL') {
16040 $tmpescaped = 'NULL';
16041 } elseif (preg_match('/^[0-9]+$/', (string) $tmpescaped)) { // if only 0-9 chars, no .
16042 $tmpescaped = (int) $tmpescaped;
16043 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
16044 $tmpescaped = (float) $tmpescaped;
16045 } else {
16046 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
16047 }
16048 }
16049
16050 return '(' . $db->escape($operand) . ' ' . strtoupper($operator) . ' ' . $tmpescaped . ')';
16051}
16052
16053
16063function getTimelineIcon($actionstatic, &$histo, $key)
16064{
16065 global $langs;
16066
16067 $out = '<!-- timeline icon -->' . "\n";
16068 $iconClass = 'fa fa-comments';
16069 $img_picto = '';
16070 $colorClass = '';
16071 $pictoTitle = '';
16072
16073 if ($histo[$key]['percent'] == -1) {
16074 $colorClass = 'timeline-icon-not-applicble';
16075 $pictoTitle = $langs->trans('StatusNotApplicable');
16076 } elseif ($histo[$key]['percent'] == 0) {
16077 $colorClass = 'timeline-icon-todo';
16078 $pictoTitle = $langs->trans('StatusActionToDo') . ' (0%)';
16079 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
16080 $colorClass = 'timeline-icon-in-progress';
16081 $pictoTitle = $langs->trans('StatusActionInProcess') . ' (' . $histo[$key]['percent'] . '%)';
16082 } elseif ($histo[$key]['percent'] >= 100) {
16083 $colorClass = 'timeline-icon-done';
16084 $pictoTitle = $langs->trans('StatusActionDone') . ' (100%)';
16085 }
16086
16087 if ($actionstatic->code == 'AC_TICKET_CREATE') {
16088 $iconClass = 'fa fa-ticket';
16089 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
16090 $iconClass = 'fa fa-pencilxxx';
16091 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16092 $iconClass = 'fa fa-comments';
16093 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16094 $iconClass = 'fa fa-mask';
16095 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16096 if ($actionstatic->type_picto) {
16097 $img_picto = img_picto('', $actionstatic->type_picto);
16098 } else {
16099 if ($actionstatic->type_code == 'AC_RDV') {
16100 $iconClass = 'fa fa-handshake';
16101 } elseif ($actionstatic->type_code == 'AC_TEL') {
16102 $iconClass = 'fa fa-phone';
16103 } elseif ($actionstatic->type_code == 'AC_FAX') {
16104 $iconClass = 'fa fa-fax';
16105 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
16106 $iconClass = 'fa fa-envelope';
16107 } elseif ($actionstatic->type_code == 'AC_INT') {
16108 $iconClass = 'fa fa-shipping-fast';
16109 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
16110 $iconClass = 'fa fa-robot';
16111 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
16112 $iconClass = 'fa fa-robot';
16113 }
16114 }
16115 }
16116
16117 $out .= '<i class="' . $iconClass . ' ' . $colorClass . '" title="' . $pictoTitle . '">' . $img_picto . '</i>' . "\n";
16118 return $out;
16119}
16120
16128{
16129 global $db;
16130
16131 $documents = array();
16132
16133 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
16134 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'ecm_files ecm';
16135 $sql .= " WHERE ecm.filepath = 'agenda/" . ((int) $object->id) . "'";
16136 //$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
16137 $sql .= ' OR ecm.agenda_id = ' . (int) $object->id;
16138 $sql .= ' ORDER BY ecm.position ASC';
16139
16140 $resql = $db->query($sql);
16141 if ($resql) {
16142 if ($db->num_rows($resql)) {
16143 while ($obj = $db->fetch_object($resql)) {
16144 $documents[$obj->id] = $obj;
16145 }
16146 }
16147 }
16148
16149 return $documents;
16150}
16151
16152
16170function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
16171{
16172 global $user, $conf;
16173 global $form;
16174
16175 global $param, $massactionbutton;
16176
16177 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16178
16179 // Check parameters
16180 if (!is_object($filterobj) && !is_object($objcon)) {
16181 dol_print_error(null, 'BadParameter');
16182 }
16183
16184 $histo = array();
16185 '@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';
16186
16187 $numaction = 0;
16188 $now = dol_now();
16189
16190 $sortfield_list = explode(',', $sortfield);
16191 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
16192 $sortfield_new_list = array();
16193 foreach ($sortfield_list as $sortfield_value) {
16194 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
16195 }
16196 $sortfield_new = implode(',', $sortfield_new_list);
16197
16198 $sql = null;
16199 $sql2 = null;
16200
16201 if (isModEnabled('agenda')) {
16202 // Search histo on actioncomm
16203 if (is_object($objcon) && $objcon->id > 0) {
16204 $sql = "SELECT DISTINCT a.id, a.label as label,";
16205 } else {
16206 $sql = "SELECT a.id, a.label as label,";
16207 }
16208 $sql .= " a.datep as dp,";
16209 $sql .= " a.note as message,";
16210 $sql .= " a.datep2 as dp2,";
16211 $sql .= " a.percent as percent, 'action' as type,";
16212 $sql .= " a.fk_element, a.elementtype,";
16213 $sql .= " a.fk_contact,";
16214 $sql .= " a.email_from as msg_from,";
16215 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
16216 $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";
16217 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16218 $sql .= ", sp.lastname, sp.firstname";
16219 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16220 $sql .= ", m.lastname, m.firstname";
16221 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur', 'Propal', 'Expedition'))) {
16222 $sql .= ", o.ref";
16223 } else {
16224 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16225 $sql .= ", o.ref";
16226 }
16227 }
16228 $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as a";
16229 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as u on u.rowid = a.fk_user_action";
16230 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_actioncomm as c ON a.fk_action = c.id";
16231
16232 $force_filter_contact = $filterobj instanceof User;
16233
16234 if (is_object($objcon) && $objcon->id > 0) {
16235 $force_filter_contact = true;
16236 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "actioncomm_resources as r ON a.id = r.fk_actioncomm";
16237 $sql .= " AND r.element_type = '" . $db->escape($objcon->table_element) . "' AND r.fk_element = " . ((int) $objcon->id);
16238 }
16239
16240 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
16241 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople as sp ON a.fk_contact = sp.rowid";
16242 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
16243 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "element_resources as er";
16244 $sql .= " ON er.resource_type = 'dolresource'";
16245 $sql .= " AND er.element_id = a.id";
16246 $sql .= " AND er.resource_id = " . ((int) $filterobj->id);
16247 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16248 $sql .= ", " . MAIN_DB_PREFIX . "adherent as m";
16249 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16250 $sql .= ", " . MAIN_DB_PREFIX . "commande_fournisseur as o";
16251 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16252 $sql .= ", " . MAIN_DB_PREFIX . "product as o";
16253 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16254 $sql .= ", " . MAIN_DB_PREFIX . "ticket as o";
16255 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16256 $sql .= ", " . MAIN_DB_PREFIX . "bom_bom as o";
16257 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16258 $sql .= ", " . MAIN_DB_PREFIX . "contrat as o";
16259 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16260 $sql .= ", " . MAIN_DB_PREFIX . "facture as o";
16261 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16262 $sql .= ", " . MAIN_DB_PREFIX . "facture_fourn as o";
16263 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16264 $sql .= ", " . MAIN_DB_PREFIX . "commande as o";
16265 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16266 $sql .= ", " . MAIN_DB_PREFIX . "expedition as o";
16267 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16268 $sql .= ", " . MAIN_DB_PREFIX . "propal as o";
16269 } else {
16270 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16271 $sql .= ", " . MAIN_DB_PREFIX . $filterobj->table_element . " as o";
16272 }
16273 }
16274 $sql .= " WHERE a.entity IN (" . getEntity('agenda') . ")";
16275 if (!$force_filter_contact) {
16276 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
16277 $sql .= " AND a.fk_soc = " . ((int) $filterobj->id);
16278 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
16279 $sql .= " AND a.fk_project = o.rowid AND a.fk_project = " . ((int) $filterobj->id);
16280 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16281 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
16282 if ($filterobj->id) {
16283 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16284 }
16285 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16286 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
16287 if ($filterobj->id) {
16288 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16289 }
16290 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16291 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'shipping'";
16292 if ($filterobj->id) {
16293 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16294 }
16295 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16296 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'propal'";
16297 if ($filterobj->id) {
16298 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16299 }
16300 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16301 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
16302 if ($filterobj->id) {
16303 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16304 }
16305 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16306 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
16307 if ($filterobj->id) {
16308 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16309 }
16310 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16311 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
16312 if ($filterobj->id) {
16313 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16314 }
16315 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16316 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
16317 if ($filterobj->id) {
16318 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16319 }
16320 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16321 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
16322 if ($filterobj->id) {
16323 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16324 }
16325 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
16326 $sql .= " AND a.fk_contact = sp.rowid";
16327 if ($filterobj->id) {
16328 $sql .= " AND a.fk_contact = " . ((int) $filterobj->id);
16329 }
16330 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16331 $sql .= " AND a.fk_element = o.rowid";
16332 if ($filterobj->id) {
16333 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice'";
16334 }
16335 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16336 $sql .= " AND a.fk_element = o.rowid";
16337 if ($filterobj->id) {
16338 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice_supplier'";
16339 }
16340 } else {
16341 if (is_object($filterobj) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16342 $sql .= " AND a.fk_element = o.rowid";
16343 $sql .= " AND a.elementtype = '" . $db->escape($filterobj->element) . "'";
16344 if ($filterobj->id) {
16345 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16346 }
16347 }
16348 }
16349 } else {
16350 $sql .= " AND u.rowid = " . ((int) $filterobj->id);
16351 }
16352
16353 // Condition on actioncode
16354 if (!empty($actioncode) && $actioncode != '-1') {
16355 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16356 if ($actioncode == 'AC_NON_AUTO') {
16357 $sql .= " AND c.type != 'systemauto'";
16358 } elseif ($actioncode == 'AC_ALL_AUTO') {
16359 $sql .= " AND c.type = 'systemauto'";
16360 } else {
16361 if ($actioncode == 'AC_OTH') {
16362 $sql .= " AND c.type != 'systemauto'";
16363 } elseif ($actioncode == 'AC_OTH_AUTO') {
16364 $sql .= " AND c.type = 'systemauto'";
16365 }
16366 }
16367 } else {
16368 if ($actioncode == 'AC_NON_AUTO') {
16369 $sql .= " AND c.type != 'systemauto'";
16370 } elseif ($actioncode == 'AC_ALL_AUTO') {
16371 $sql .= " AND c.type = 'systemauto'";
16372 } else {
16373 $sql .= " AND c.code = '" . $db->escape($actioncode) . "'";
16374 }
16375 }
16376 }
16377 if ($donetodo == 'todo') {
16378 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '" . $db->idate($now) . "'))";
16379 } elseif ($donetodo == 'done') {
16380 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '" . $db->idate($now) . "'))";
16381 }
16382 if (is_array($filters) && $filters['search_agenda_label']) {
16383 $sql .= natural_search('a.label', $filters['search_agenda_label']);
16384 }
16385 }
16386
16387 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
16388 if (
16389 isModEnabled('mailing') && !empty($objcon->email)
16390 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')
16391 ) {
16392 $langs->load("mails");
16393
16394 $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";
16395 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
16396 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
16397 $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
16398 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16399 $sql2 .= ", '' as lastname, '' as firstname";
16400 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16401 $sql2 .= ", '' as lastname, '' as firstname";
16402 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16403 $sql2 .= ", '' as ref";
16404 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16405 $sql2 .= ", '' as ref";
16406 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16407 $sql2 .= ", '' as ref";
16408 }
16409 $sql2 .= " FROM " . MAIN_DB_PREFIX . "mailing as m, " . MAIN_DB_PREFIX . "mailing_cibles as mc, " . MAIN_DB_PREFIX . "user as u";
16410 $sql2 .= " WHERE mc.email = '" . $db->escape($objcon->email) . "'"; // Search is done on email.
16411 $sql2 .= " AND mc.statut = 1";
16412 $sql2 .= " AND u.rowid = m.fk_user_valid";
16413 $sql2 .= " AND mc.fk_mailing=m.rowid";
16414 }
16415
16416 $num = 0;
16417 $MAXWITHOUTPAGINATION = getDolGlobalInt('AGENDA_MAX_EVENTS_ON_PAGE_WITHOUT_PAGINATION', 100);
16418
16419 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
16420 if (!empty($sql) && !empty($sql2)) {
16421 $sql = $sql . " UNION " . $sql2;
16422 } elseif (empty($sql) && !empty($sql2)) {
16423 $sql = $sql2;
16424 }
16425
16426 //TODO Add navigation with this limits...
16427 $offset = 0;
16428 $limit = $MAXWITHOUTPAGINATION;
16429
16430 // Complete request and execute it with limit
16431 $sql .= $db->order($sortfield_new, $sortorder);
16432 if ($limit) {
16433 $sql .= $db->plimit($limit + 1, $offset);
16434 }
16435
16436 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
16437
16438 $resql = $db->query($sql);
16439 if ($resql) {
16440 $i = 0;
16441 $num = $db->num_rows($resql);
16442
16443 $imaxinloop = ($limit ? min($num, $limit) : $num);
16444 while ($i < $imaxinloop) {
16445 $obj = $db->fetch_object($resql);
16446
16447 if ($obj->type == 'action') {
16448 $contactaction = new ActionComm($db);
16449 $contactaction->id = $obj->id;
16450 $result = $contactaction->fetchResources();
16451 if ($result < 0) {
16452 dol_print_error($db);
16453 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
16454 }
16455
16456 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
16457 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
16458 $tododone = '';
16459 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
16460 $tododone = 'todo';
16461 }
16462
16463 $histo[$numaction] = array(
16464 'type' => $obj->type,
16465 'tododone' => $tododone,
16466 'id' => $obj->id,
16467 'datestart' => $db->jdate($obj->dp),
16468 'dateend' => $db->jdate($obj->dp2),
16469 'note' => $obj->label,
16470 'message' => $obj->message,
16471 'percent' => $obj->percent,
16472
16473 'userid' => $obj->user_id,
16474 'login' => $obj->user_login,
16475 'userfirstname' => $obj->user_firstname,
16476 'userlastname' => $obj->user_lastname,
16477 'userphoto' => $obj->user_photo,
16478 'msg_from' => $obj->msg_from,
16479
16480 'contact_id' => $obj->fk_contact,
16481 'socpeopleassigned' => $contactaction->socpeopleassigned,
16482 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
16483 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
16484 'fk_element' => $obj->fk_element,
16485 'elementtype' => $obj->elementtype,
16486 // Type of event
16487 'acode' => $obj->acode,
16488 'alabel' => $obj->alabel,
16489 'libelle' => $obj->alabel, // deprecated
16490 'apicto' => $obj->apicto
16491 );
16492 } else {
16493 $histo[$numaction] = array(
16494 'type' => $obj->type,
16495 'tododone' => 'done',
16496 'id' => $obj->id,
16497 'datestart' => $db->jdate($obj->dp),
16498 'dateend' => $db->jdate($obj->dp2),
16499 'note' => $obj->label,
16500 'message' => $obj->message,
16501 'percent' => $obj->percent,
16502 'acode' => $obj->acode,
16503
16504 'userid' => $obj->user_id,
16505 'login' => $obj->user_login,
16506 'userfirstname' => $obj->user_firstname,
16507 'userlastname' => $obj->user_lastname,
16508 'userphoto' => $obj->user_photo
16509 );
16510 }
16511
16512 $numaction++;
16513 $i++;
16514 }
16515 } else {
16516 dol_print_error($db);
16517 }
16518 }
16519
16520 // Set $out to show events
16521 $out = '';
16522
16523 if (!isModEnabled('agenda')) {
16524 $langs->loadLangs(array("admin", "errors"));
16525 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
16526 }
16527
16528 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
16529 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
16530
16531 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16532 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
16533 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
16534 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
16535
16536 $formactions = new FormActions($db);
16537
16538 $actionstatic = new ActionComm($db);
16539 $userstatic = new User($db);
16540 $contactstatic = new Contact($db);
16541 $userGetNomUrlCache = array();
16542 $contactGetNomUrlCache = array();
16543
16544 $out .= '<div class="filters-container" >';
16545 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
16546 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
16547
16548 if (
16549 $objcon && get_class($objcon) == 'Contact' &&
16550 (is_null($filterobj) || get_class($filterobj) == 'Societe')
16551 ) {
16552 $out .= '<input type="hidden" name="id" value="' . $objcon->id . '" />';
16553 } else {
16554 $out .= '<input type="hidden" name="id" value="' . $filterobj->id . '" />';
16555 }
16556 if (($filterobj && get_class($filterobj) == 'Societe')) {
16557 $out .= '<input type="hidden" name="socid" value="' . $filterobj->id . '" />';
16558 } else {
16559 $out .= '<input type="hidden" name="userid" value="' . $filterobj->id . '" />';
16560 }
16561
16562 $out .= "\n";
16563
16564 $out .= '<div class="div-table-responsive-no-min">';
16565 $out .= '<table class="noborder borderbottom centpercent">';
16566
16567 $out .= '<tr class="liste_titre">';
16568
16569 // Action column
16570 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16571 $out .= '<th class="liste_titre width50 middle">';
16572 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16573 $out .= $searchpicto;
16574 $out .= '</th>';
16575 }
16576
16577 // Date
16578 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ') . "\n";
16579
16580 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">' . $langs->trans("Search") . ' : </strong></th>';
16581 if ($donetodo) {
16582 $out .= '<th class="liste_titre"></th>';
16583 }
16584 // Type of event
16585 $out .= '<th class="liste_titre">';
16586 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="' . $langs->trans("ActionType") . '"></span>';
16587 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
16588 $out .= '</th>';
16589 // Label
16590 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
16591 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="' . $filters['search_agenda_label'] . '" placeholder="' . $langs->trans("Label") . '">';
16592 $out .= '</th>';
16593
16594 // Action column
16595 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16596 $out .= '<th class="liste_titre width50 middle">';
16597 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16598 $out .= $searchpicto;
16599 $out .= '</th>';
16600 }
16601
16602 $out .= '</tr>';
16603
16604 $out .= '</table>';
16605
16606 $out .= '</form>';
16607 $out .= '</div>';
16608
16609 $out .= "\n";
16610
16611 $out .= '<ul class="timeline">';
16612
16613 if ($donetodo) {
16614 $tmp = '';
16615 if ($filterobj instanceof Societe) {
16616 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16617 }
16618 if ($filterobj instanceof User) {
16619 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16620 }
16621 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
16622 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
16623 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
16624 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
16625 if ($filterobj instanceof Societe) {
16626 $tmp .= '</a>';
16627 }
16628 if ($filterobj instanceof User) {
16629 $tmp .= '</a>';
16630 }
16631 $out .= getTitleFieldOfList($tmp);
16632 }
16633
16634 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/cactioncomm.class.php';
16635 $caction = new CActionComm($db);
16636 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
16637
16638 $actualCycleDate = false;
16639
16640 // Loop on each event to show it
16641 foreach ($histo as $key => $value) {
16642 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
16643
16644 $actionstatic->type_picto = $histo[$key]['apicto'];
16645 $actionstatic->type_code = $histo[$key]['acode'];
16646
16647 $labeltype = $actionstatic->type_code;
16648 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
16649 $labeltype = 'AC_OTH';
16650 }
16651 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16652 $labeltype = $langs->trans("Message");
16653 } else {
16654 if (!empty($arraylist[$labeltype])) {
16655 $labeltype = $arraylist[$labeltype];
16656 }
16657 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
16658 $labeltype .= ' - ' . $arraylist[$actionstatic->code]; // Use code in priority on type_code
16659 }
16660 }
16661
16662 $url = DOL_URL_ROOT . '/comm/action/card.php?id=' . $histo[$key]['id'];
16663
16664 $tmpa = dol_getdate($histo[$key]['datestart'], false);
16665
16666 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'] . '-' . $tmpa['yday']) {
16667 $actualCycleDate = $tmpa['year'] . '-' . $tmpa['yday'];
16668 $out .= '<!-- timeline time label -->';
16669 $out .= '<li class="time-label">';
16670 $out .= '<span class="timeline-badge-date">';
16671 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
16672 $out .= '</span>';
16673 $out .= '</li>';
16674 $out .= '<!-- /.timeline-label -->';
16675 }
16676
16677
16678 $out .= '<!-- timeline item -->' . "\n";
16679 $out .= '<li class="timeline-code-' . (!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none") . '">';
16680
16681 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
16682 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
16683 //$out .= $timelineicon;
16684 //var_dump($timelineicon);
16685 $out .= $typeicon;
16686
16687 $out .= '<div class="timeline-item">' . "\n";
16688
16689 $out .= '<span class="time timeline-header-action2">';
16690
16691 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
16692 $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") . ' ';
16693 $out .= $histo[$key]['id'];
16694 $out .= '</a> ';
16695 } else {
16696 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle') . ' ';
16697 }
16698
16699 if (
16700 $user->hasRight('agenda', 'allactions', 'create') ||
16701 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))
16702 ) {
16703 $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) . '">';
16704 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
16705 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
16706 $out .= '</a>';
16707 }
16708
16709 $out .= '</span>';
16710
16711 // Date
16712 $out .= '<span class="time"><i class="fa fa-clock valignmiddle"></i> ';
16713 $out .= '<span class="valignmiddle marginrightonly">';
16714 $out .= dol_print_date($histo[$key]['datestart'], 'day', 'tzuserrel');
16715 //$out .= '</span>';
16716 //$out .= '<span class="valignmiddle">'.
16717 $out .= ' '.dol_print_date($histo[$key]['datestart'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16718 //$out .= '</span>';
16719 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
16720 $tmpa = dol_getdate($histo[$key]['datestart'], true);
16721 $tmpb = dol_getdate($histo[$key]['dateend'], true);
16722 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
16723 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 1);
16724 } else {
16725 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'day', 'tzuserrel');
16726 //$out .= '<span class="valignmiddle marginrightonly">';
16727 $out .= ' '.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16728 //$out .= '</span>';
16729 }
16730 }
16731 $late = 0;
16732 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16733 $late = 1;
16734 }
16735 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16736 $late = 1;
16737 }
16738 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
16739 $late = 1;
16740 }
16741 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16742 $late = 1;
16743 }
16744 if ($late) {
16745 $out .= img_warning($langs->trans("Late")) . ' ';
16746 }
16747 $out .= "</span></span>\n";
16748
16749 $out .= '<span class="time">';
16750 $out .= $actionstatic->getLibStatut(2);
16751 $out .= '</span>';
16752
16753 // Ref
16754 $out .= '<h3 class="timeline-header">';
16755
16756 // Author of event
16757 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
16758 if ($histo[$key]['userid'] > 0) {
16759 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
16760 $userstatic->fetch($histo[$key]['userid']);
16761 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
16762 }
16763 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
16764 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
16765 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
16766 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
16767 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
16768 } else {
16769 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
16770 }
16771 }
16772 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
16773 } else {
16774 $out .= '<img class="photomemberphoto userphoto" alt="" src="/public/theme/common/user_anonymous.png">'.$langs->trans("Anonymous");
16775 }
16776 $out .= '</div>';
16777
16778 // Title
16779 $out .= ' <div class="messaging-title inline-block">';
16780 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
16781 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
16782 $out .= $labeltype . ' - ';
16783 }
16784
16785 $libelle = '';
16786
16787 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16788 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
16789 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16790 $out .= $langs->trans('TicketNewMessage');
16791 } elseif (isset($histo[$key]['type'])) {
16792 if ($histo[$key]['type'] == 'action') {
16793 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16794 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
16795 $libelle = $histo[$key]['note'];
16796 $actionstatic->id = $histo[$key]['id'];
16797 if ($libelle != $labeltype) {
16798 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16799 }
16800 } elseif ($histo[$key]['type'] == 'mailing') {
16801 $out .= '<a href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
16802 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16803 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : 'Send mass mailing');
16804 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16805 } else {
16806 $libelle .= $histo[$key]['note'];
16807 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16808 }
16809 }
16810 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
16811
16812 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
16813 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
16814 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
16815 } else {
16816 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
16817 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
16818 }
16819 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
16820 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
16821 }
16822 if ($link) {
16823 $out .= ' - ' . $link;
16824 }
16825 }
16826
16827 $out .= '</div>';
16828
16829 $out .= '</h3>';
16830
16831 // Message
16832 if (
16833 !empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
16834 && $actionstatic->code != 'AC_TICKET_CREATE'
16835 && $actionstatic->code != 'AC_TICKET_MODIFY'
16836 ) {
16837 $out .= '<div class="timeline-body wordbreak small">';
16838 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
16839 $newmess = dol_htmlentitiesbr($histo[$key]['message']);
16840 $truncatedText = dolGetFirstLineOfText($newmess, $truncateLines);
16841 // dolGetFirstLineOfText() cuts on <br> without caring about tag balance, so a message wrapped in
16842 // a block tag leaves the excerpt with an unclosed tag. The browser then nests the read more link
16843 // and the full text inside the excerpt, and hiding the excerpt hides the whole message (#39035).
16844 $truncatedText = dolCloseUnclosedHtmlTags($truncatedText);
16845 if ($truncateLines > 0 && strlen($newmess) > strlen($truncatedText)) {
16846 $out .= '<div class="readmore-block --closed" >';
16847 $out .= ' <div class="readmore-block__excerpt">';
16848 $out .= dolPrintHTML($truncatedText);
16849 $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>';
16850 $out .= ' </div>';
16851 $out .= ' <div class="readmore-block__full-text" >';
16852 $out .= dolPrintHTML($newmess);
16853 $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>';
16854 $out .= ' </div>';
16855 $out .= '</div>';
16856 } else {
16857 $out .= dolPrintHTML($newmess);
16858 }
16859
16860 $out .= '</div>';
16861 }
16862
16863 // Timeline footer
16864 $footer = '';
16865
16866 // Contact for this action
16867 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
16868 $contactList = '';
16869 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
16870 if (empty($conf->cache['contact'][$cid])) {
16871 $contact = new Contact($db);
16872 $contact->fetch($cid);
16873 $conf->cache['contact'][$cid] = $contact;
16874 } else {
16875 $contact = $conf->cache['contact'][$cid];
16876 }
16877
16878 if ($contact) {
16879 $contactList .= !empty($contactList) ? ', ' : '';
16880 $contactList .= $contact->getNomUrl(1);
16881 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16882 if (!empty($contact->phone_pro)) {
16883 $contactList .= '(' . dol_print_phone($contact->phone_pro) . ')';
16884 }
16885 }
16886 }
16887 }
16888
16889 $footer .= $langs->trans('ActionOnContact') . ' : ' . $contactList;
16890 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
16891 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
16892 $contact = new Contact($db);
16893 $result = $contact->fetch($histo[$key]['contact_id']);
16894 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
16895 } else {
16896 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
16897 $result = ($contact instanceof Contact) ? $contact->id : 0;
16898 }
16899
16900 if ($result > 0) {
16901 $footer .= $contact->getNomUrl(1);
16902 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16903 if (!empty($contact->phone_pro)) {
16904 $footer .= '(' . dol_print_phone($contact->phone_pro) . ')';
16905 }
16906 }
16907 }
16908 }
16909
16910 $documents = getActionCommEcmList($actionstatic);
16911 if (!empty($documents)) {
16912 $footer .= '<div class="timeline-documents-container">';
16913 foreach ($documents as $doc) {
16914 $footer .= '<span id="document_' . $doc->id . '" class="timeline-documents" ';
16915 $footer .= ' data-id="' . $doc->id . '" ';
16916 $footer .= ' data-path="' . $doc->filepath . '"';
16917 $footer .= ' data-filename="' . dol_escape_htmltag($doc->filename) . '" ';
16918 $footer .= '>';
16919
16920 $filePath = DOL_DATA_ROOT . '/' . $doc->filepath . '/' . $doc->filename;
16921 $mime = dol_mimetype($filePath);
16922 if (empty($doc->agenda_id)) {
16923 $dir_ref = $actionstatic->id;
16924 $modulepart = 'actions';
16925 } else {
16926 $split_dir = explode('/', $doc->filepath);
16927 $modulepart = array_shift($split_dir);
16928 $dir_ref = implode('/', $split_dir);
16929 }
16930
16931 $file = $dir_ref . '/' . $doc->filename;
16932 $thumb = $dir_ref . '/thumbs/' . substr($doc->filename, 0, strrpos($doc->filename, '.')) . '_mini' . substr($doc->filename, strrpos($doc->filename, '.'));
16933 $doclink = dol_buildpath('document.php', 1) . '?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($file) . '&entity=' . $conf->entity;
16934 $viewlink = dol_buildpath('viewimage.php', 1) . '?modulepart=' . $modulepart . '&file=' . urlencode($thumb) . '&entity=' . $conf->entity;
16935
16936
16937
16938 $mimeAttr = ' mime="' . $mime . '" ';
16939 $class = '';
16940 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
16941 $class .= ' documentpreview';
16942 }
16943
16944 $footer .= '<a href="' . $doclink . '" class="btn-link ' . $class . '" target="_blank" rel="noopener noreferrer" ' . $mimeAttr . ' >';
16945 $footer .= img_mime($filePath) . ' ' . $doc->filename;
16946 $footer .= '</a>';
16947
16948 $footer .= '</span>';
16949 }
16950 $footer .= '</div>';
16951 }
16952
16953 if (!empty($footer)) {
16954 $out .= '<div class="timeline-footer">' . $footer . '</div>';
16955 }
16956
16957 $out .= '</div>' . "\n"; // end timeline-item
16958
16959 $out .= '</li>';
16960 $out .= '<!-- END timeline item -->';
16961 }
16962
16963 $out .= "</ul>\n";
16964
16965 // Code to manage the click on button data-read-more-action to show full description of an event
16966 $out .= '<script>
16967 jQuery(document).ready(function () {
16968 $(document).on("click", "[data-read-more-action]", function(e){
16969 console.log("We click on data-read-more-action");
16970 let readMoreBloc = $(this).closest(".readmore-block");
16971 if(readMoreBloc.length > 0){
16972 e.preventDefault();
16973 if($(this).attr("data-read-more-action") == "close"){
16974 readMoreBloc.addClass("--closed").removeClass("--open");
16975 $("html, body").animate({
16976 scrollTop: readMoreBloc.offset().top - 200
16977 }, 100);
16978 }else{
16979 readMoreBloc.addClass("--open").removeClass("--closed");
16980 }
16981 }
16982 });
16983 });
16984 </script>';
16985
16986
16987 if (empty($histo)) {
16988 $out .= '<span class="opacitymedium">' . $langs->trans("NoRecordFound") . '</span>';
16989 }
16990
16991 if ($num > $MAXWITHOUTPAGINATION) {
16992 $langs->load("errors");
16993 $colspan = 9;
16994 $out .= '<center><span class="opacitymedium">...' . $langs->trans("WarningTooManyDataPleaseUseMoreFilters", $MAXWITHOUTPAGINATION) . '...</span></center>';
16995 }
16996 }
16997
16998 if ($noprint) {
16999 return $out;
17000 } else {
17001 print $out;
17002 return null;
17003 }
17004}
17005
17017function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
17018{
17019 if ($timestamp === null) {
17020 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
17021 }
17022 $TParam = array(
17023 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
17024 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
17025 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
17026 );
17027 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
17028 $TParam = array_merge($TParam, array(
17029 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
17030 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
17031 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
17032 ));
17033 }
17034
17035 return '&' . http_build_query($TParam);
17036}
17037
17056function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
17057{
17058 global $conf, $db, $langs, $hookmanager;
17059 global $action, $object;
17060
17061 if (!is_object($langs)) {
17062 include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
17063 $langs = new Translate('', $conf);
17064 $langs->setDefaultLang();
17065 }
17066
17067 $langs->load("errors");
17068
17069 if ($printheader) {
17070 if (function_exists("llxHeader")) {
17071 llxHeader('');
17072 } elseif (function_exists("llxHeaderVierge")) {
17073 llxHeaderVierge('');
17074 }
17075 }
17076
17077 print '<div class="error">';
17078 if (empty($message)) {
17079 print $langs->trans("ErrorRecordNotFound");
17080 } else {
17081 print $langs->trans($message);
17082 }
17083 print '</div>';
17084 print '<br>';
17085
17086 if (empty($showonlymessage)) {
17087 if (empty($hookmanager)) {
17088 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
17089 $hookmanager = new HookManager($db);
17090 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
17091 $hookmanager->initHooks(array('main'));
17092 }
17093
17094 $parameters = array('message' => $message, 'params' => $params);
17095 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
17096 print $hookmanager->resPrint;
17097 }
17098
17099 if ($printfooter && function_exists("llxFooter")) {
17100 llxFooter();
17101 if (is_object($db)) {
17102 $db->close();
17103 }
17104 }
17105 exit(0);
17106}
17107
17136function array_merge_recursive_distinct(array $array1, array $array2): array
17137{
17138 $merged = $array1;
17139
17140 foreach ($array2 as $key => $value) {
17141 if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
17142 $merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
17143 } else {
17144 $merged[$key] = $value;
17145 }
17146 }
17147
17148 return $merged;
17149}
17150
17157function getObjectSocId($obj)
17158{
17159 if (!empty($obj->socid)) {
17160 return (int) $obj->socid;
17161 } elseif (!empty($obj->soc_id)) {
17162 return (int) $obj->soc_id;
17163 } elseif (!empty($obj->societe_id)) {
17164 return (int) $obj->societe_id;
17165 }
17166 return null;
17167}
$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...
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=2, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
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_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).
dolCloseUnclosedHtmlTags($text)
Close the HTML tags left open in a truncated HTML string.
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