dolibarr 23.0.3
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2025 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2019-2023 Thibault Foucart <support@ptibogxiv.net>
18 * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20 * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21 * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22 * Copyright (C) 2022-2026 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
24 * Copyright (C) 2023-2024 Joachim Kueter <git-jk@bloxera.com>
25 * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26 * Copyright (C) 2024 Josep Lluís Amador Teruel <joseplluis@lliuretic.cat>
27 * Copyright (C) 2024 Benoît PASCAL <contact@p-ben.com>
28 * Copyright (C) 2025 Vincent Maury <vmaury@timgroup.fr>
29 * Copyright (C) 2026 Benjamin Falière <benjamin@faliere.com>
30 *
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 3 of the License, or
34 * (at your option) any later version.
35 *
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program. If not, see <https://www.gnu.org/licenses/>.
43 * or see https://www.gnu.org/
44 */
45
52//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
53
54// Function for better PHP x compatibility
55if (!function_exists('utf8_encode')) {
63 function utf8_encode($elements)
64 {
65 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
66 }
67}
68
69if (!function_exists('utf8_decode')) {
77 function utf8_decode($elements)
78 {
79 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
80 }
81}
82if (!function_exists('str_starts_with')) {
91 function str_starts_with($haystack, $needle)
92 {
93 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
94 }
95}
96if (!function_exists('str_ends_with')) {
105 function str_ends_with($haystack, $needle)
106 {
107 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
108 }
109}
110if (!function_exists('str_contains')) {
119 function str_contains($haystack, $needle)
120 {
121 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
122 }
123}
124
125
137function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
138{
139 global $conf;
140
141 $subdirectory = '';
142 if (!is_object($object) && empty($module)) {
143 return null;
144 }
145 if (empty($module) && !empty($object->element)) {
146 $module = $object->element;
147 }
148
149 // Special case for backward compatibility
150 switch ($module) {
151 case 'fichinter':
152 $module = 'ficheinter';
153 break;
154 case 'invoice_supplier':
155 $module = 'supplier_invoice';
156 break;
157 case 'order_supplier':
158 $module = 'supplier_order';
159 break;
160 case 'recruitmentjobposition':
161 $module = 'recruitment';
162 $subdirectory = '/recruitmentjobposition';
163 break;
164 case 'recruitmentcandidature':
165 $module = 'recruitment';
166 $subdirectory = '/recruitmentcandidature';
167 break;
168 case 'knowledgerecord':
169 $module = 'knowledgemanagement';
170 $subdirectory = '/knowledgerecord';
171 break;
172 case 'service':
173 case 'produit':
174 $module = 'product';
175 break;
176 case 'action':
177 case 'actioncomm':
178 case 'event':
179 $module = 'agenda';
180 break;
181 default:
182 break;
183 }
184
185 // Get the relative path of directory
186 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
187 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
188 $s = '';
189 if ($mode != 'outputrel') {
190 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)] . $subdirectory;
191 }
192 if ($forobject && $object->id > 0) {
193 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
194 }
195 return $s;
196 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
197 $s = '';
198 if ($mode != 'outputrel') {
199 $s = $conf->$module->dir_output . $subdirectory;
200 }
201 if ($forobject && $object->id > 0) {
202 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
203 }
204 return $s;
205 } else {
206 return 'error-diroutput-not-defined-for-this-object=' . $module;
207 }
208 } elseif ($mode == 'temp') {
209 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
210 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
211 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
212 return $conf->$module->dir_temp;
213 } else {
214 return 'error-dirtemp-not-defined-for-this-object=' . $module;
215 }
216 } else {
217 return 'error-bad-value-for-mode';
218 }
219}
220
230function getMultidirTemp($object, $module = '', $forobject = 0)
231{
232 return getMultidirOutput($object, $module, $forobject, 'temp');
233}
234
244function getMultidirVersion($object, $module = '', $forobject = 0)
245{
246 return getMultidirOutput($object, $module, $forobject, 'version');
247}
248
249
258function getDolGlobalString($key, $default = '')
259{
260 global $conf;
261 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
262}
263
273function getDolGlobalInt($key, $default = 0)
274{
275 global $conf;
276 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
277}
278
288function getDolGlobalFloat($key, $default = 0)
289{
290 global $conf;
291 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
292}
293
302function getDolGlobalBool($key, $default = false)
303{
304 global $conf;
305 return (bool) ($conf->global->$key ?? $default);
306}
307
314{
315 global $conf;
316 return (string) $conf->currency;
317}
318
325{
326 global $conf;
327 return (string) $conf->dol_optimize_smallscreen;
328}
329
335function getDolEntity()
336{
337 global $conf;
338 return (int) $conf->entity;
339}
340
346function getDolDBType()
347{
348 global $conf;
349 return $conf->db->type;
350}
351
359{
360 return str_replace('_', '', basename(dirname($s)).basename($s, '.php'));
361}
362
372function getDolUserString($key, $default = '', $tmpuser = null)
373{
374 if (empty($tmpuser)) {
375 global $user;
376 $tmpuser = $user;
377 }
378
379 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
380}
381
390function getDolUserInt($key, $default = 0, $tmpuser = null)
391{
392 if (empty($tmpuser)) {
393 global $user;
394 $tmpuser = $user;
395 }
396
397 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
398}
399
400
410define(
411 'MODULE_MAPPING',
412 array(
413 // Map deprecated names to new names
414 'adherent' => 'member', // Has new directory
415 'member_type' => 'adherent_type', // No directory, but file called adherent_type
416 'banque' => 'bank', // Has new directory
417 'contrat' => 'contract', // Has new directory
418 'entrepot' => 'stock', // Has new directory
419 'projet' => 'project', // Has new directory
420 'categorie' => 'category', // Has old directory
421 'commande' => 'order', // Has old directory
422 'expedition' => 'shipping', // Has old directory
423 'facture' => 'invoice', // Has old directory
424 'fichinter' => 'intervention', // Has old directory
425 'ficheinter' => 'intervention', // Backup for 'fichinter'
426 'propale' => 'propal', // Has old directory
427 'socpeople' => 'contact', // Has old directory
428 'fournisseur' => 'supplier', // Has old directory
429
430 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
431 'product_price' => 'productprice', // NO directory
432 'product_fournisseur_price' => 'productsupplierprice', // NO directory
433 )
434);
435
442function isModEnabled($module)
443{
444 global $conf;
445
446 // Fix old names (map to new names)
447 $arrayconv = MODULE_MAPPING;
448 $arrayconvbis = array_flip(MODULE_MAPPING);
449
450 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
451 // Special cases: both use the same module.
452 $arrayconv['supplier_order'] = 'fournisseur';
453 $arrayconv['supplier_invoice'] = 'fournisseur';
454 }
455
456 $module_alt = $module;
457 if (!empty($arrayconv[$module])) {
458 $module_alt = $arrayconv[$module];
459 }
460 $module_bis = $module;
461 if (!empty($arrayconvbis[$module])) {
462 $module_bis = $arrayconvbis[$module];
463 }
464
465 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
466}
467
478function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
479{
480 global $conf;
481
482 // For compatibility with bad naming on module
483 $moduletomoduletouse = array(
484 'invoice' => 'facture',
485 );
486 $moduleParmsMapping = array(
487 'product' => 'produit',
488 );
489
490 if (!empty($moduletomoduletouse[$module])) {
491 $module = $moduletomoduletouse[$module];
492 }
493
494 $warningDelayPath = $parmlevel1;
495 if (!empty($moduleParmsMapping[$warningDelayPath])) {
496 $warningDelayPath = $moduleParmsMapping[$warningDelayPath];
497 }
498
499 if ($parmlevel2) {
500 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->$parmlevel2) && !empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
501 return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
502 }
503 } else {
504 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->warning_delay)) {
505 return (int) $conf->$module->$warningDelayPath->warning_delay;
506 }
507 }
508
509 return 0;
510}
511
518function isDolTms($timestamp)
519{
520 if ($timestamp === '') {
521 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page ' . $_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
522 return false;
523 }
524 if (is_null($timestamp) || !is_numeric($timestamp)) {
525 return false;
526 }
527
528 return true;
529}
530
542function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
543{
544 require_once DOL_DOCUMENT_ROOT . "/core/db/" . $type . '.class.php';
545
546 $class = 'DoliDB' . ucfirst($type);
547 $db = new $class($type, $host, $user, $pass, $name, $port);
548 return $db;
549}
550
568function getEntity($element, $shared = 1, $currentobject = null)
569{
570 global $conf, $mc, $hookmanager, $object, $action, $db;
571
572 if (!is_object($hookmanager)) {
573 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
574 $hookmanager = new HookManager($db);
575 }
576
577 // fix different element names (France to English)
578 switch ($element) {
579 case 'projet':
580 $element = 'project';
581 break;
582 case 'contrat':
583 $element = 'contract';
584 break; // "/contrat/class/contrat.class.php"
585 case 'order_supplier':
586 $element = 'supplier_order';
587 break; // "/fourn/class/fournisseur.commande.class.php"
588 case 'invoice_supplier':
589 $element = 'supplier_invoice';
590 break; // "/fourn/class/fournisseur.facture.class.php"
591 }
592
593 if (is_object($mc)) {
594 $out = $mc->getEntity($element, $shared, $currentobject);
595 } else {
596 $out = '';
597 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
598 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
599 $addzero[] = 'c_holiday_types';
600 }
601 if (in_array($element, $addzero)) {
602 $out .= '0,';
603 }
604 $out .= ((int) $conf->entity);
605 }
606
607 // Manipulate entities to query on the fly
608 $parameters = array(
609 'element' => $element,
610 'shared' => $shared,
611 'object' => $object,
612 'currentobject' => $currentobject,
613 'out' => $out
614 );
615 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
616 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
617
618 if (is_numeric($reshook)) {
619 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
620 $out .= ',' . $hookmanager->resPrint; // add
621 } elseif ($reshook == 1) {
622 $out = $hookmanager->resPrint; // replace
623 }
624 }
625
626 return $out;
627}
628
635function setEntity($currentobject)
636{
637 global $conf, $mc;
638
639 if (is_object($mc) && method_exists($mc, 'setEntity')) {
640 return $mc->setEntity($currentobject);
641 } else {
642 return ((is_object($currentobject) && $currentobject->id > 0 && ((int) $currentobject->entity) > 0) ? (int) $currentobject->entity : $conf->entity);
643 }
644}
645
652function isASecretKey($keyname)
653{
654 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
655}
656
657
664function num2Alpha($n)
665{
666 $r = '';
667 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
668 $r = chr($n % 26 + 0x41) . $r;
669 }
670 return $r;
671}
672
673
690function getBrowserInfo($user_agent)
691{
692 include_once DOL_DOCUMENT_ROOT . '/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
693
694 $name = 'unknown';
695 $version = '';
696 $os = 'unknown';
697 $phone = '';
698
699 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
700
701 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal Bad definition of Mobile_Detect function
702 $detectmobile = new Mobile_Detect(null, $user_agent);
703 $tablet = $detectmobile->isTablet();
704
705 if ($detectmobile->isMobile()) {
706 $phone = 'unknown';
707
708 // If phone/smartphone, we set phone os name.
709 if ($detectmobile->is('AndroidOS')) {
710 $os = $phone = 'android';
711 } elseif ($detectmobile->is('BlackBerryOS')) {
712 $os = $phone = 'blackberry';
713 } elseif ($detectmobile->is('iOS')) {
714 $os = 'ios';
715 $phone = 'iphone';
716 } elseif ($detectmobile->is('PalmOS')) {
717 $os = $phone = 'palm';
718 } elseif ($detectmobile->is('SymbianOS')) {
719 $os = 'symbian';
720 } elseif ($detectmobile->is('webOS')) {
721 $os = 'webos';
722 } elseif ($detectmobile->is('MaemoOS')) {
723 $os = 'maemo';
724 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
725 $os = 'windows';
726 }
727 }
728
729 // OS
730 if (preg_match('/linux/i', $user_agent)) {
731 $os = 'linux';
732 } elseif (preg_match('/macintosh/i', $user_agent)) {
733 $os = 'macintosh';
734 } elseif (preg_match('/windows/i', $user_agent)) {
735 $os = 'windows';
736 }
737
738 // Name
739 $reg = array();
740 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
741 $name = 'firefox';
742 $version = empty($reg[2]) ? '' : $reg[2];
743 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
744 $name = 'edge';
745 $version = empty($reg[2]) ? '' : $reg[2];
746 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
747 $name = 'chrome';
748 $version = empty($reg[2]) ? '' : $reg[2];
749 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
750 // we can have 'chrome (Mozilla...) chrome x.y' in one string
751 $name = 'chrome';
752 } elseif (preg_match('/iceweasel/i', $user_agent)) {
753 $name = 'iceweasel';
754 } elseif (preg_match('/epiphany/i', $user_agent)) {
755 $name = 'epiphany';
756 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
757 $name = 'safari';
758 $version = empty($reg[2]) ? '' : $reg[2];
759 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
760 // Safari is often present in string for mobile but its not.
761 $name = 'opera';
762 $version = empty($reg[2]) ? '' : $reg[2];
763 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
764 $name = 'ie';
765 $version = end($reg);
766 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
767 // MS products at end
768 $name = 'ie';
769 $version = end($reg);
770 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
771 // MS products at end
772 $name = 'textbrowser';
773 $version = empty($reg[3]) ? '' : $reg[3];
774 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
775 // MS products at end
776 $name = 'textbrowser';
777 $version = empty($reg[1]) ? '' : $reg[1];
778 }
779
780 if ($tablet) {
781 $layout = 'tablet';
782 } elseif ($phone) {
783 $layout = 'phone';
784 } else {
785 $layout = 'classic';
786 }
787
788 return array(
789 'browsername' => $name,
790 'browserversion' => $version,
791 'browseros' => $os,
792 'browserua' => $user_agent,
793 'layout' => $layout, // tablet, phone, classic
794 'phone' => $phone, // deprecated
795 'tablet' => $tablet // deprecated
796 );
797}
798
804function dol_shutdown()
805{
806 global $db;
807 $disconnectdone = false;
808 $depth = 0;
809 if (is_object($db) && !empty($db->connected)) {
810 $depth = $db->transaction_opened;
811 $disconnectdone = $db->close();
812 }
813 dol_syslog("--- End access to " . (empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]) . (($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was ' . $depth . ')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
814}
815
825function GETPOSTISSET($paramname)
826{
827 $isset = false;
828
829 $relativepathstring = $_SERVER["PHP_SELF"];
830 // Clean $relativepathstring
831 if (constant('DOL_URL_ROOT')) {
832 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
833 }
834 $relativepathstring = ltrim($relativepathstring, '/');
835 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
836
837 // Code for search criteria persistence.
838 // Retrieve values if restore_lastsearch_values
839 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
840 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
841 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
842 if (is_array($tmp)) {
843 foreach ($tmp as $key => $val) {
844 if ($key == $paramname) { // We are on the requested parameter
845 $isset = true;
846 break;
847 }
848 }
849 }
850 }
851 // If there is saved contextpage, limit, page or mode
852 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
853 $isset = true;
854 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
855 $isset = true;
856 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
857 $isset = true;
858 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
859 $isset = true;
860 }
861 } else {
862 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
863 }
864
865 return $isset;
866}
867
876function GETPOSTISARRAY($paramname, $method = 0)
877{
878 // for $method test need return the same $val as GETPOST
879 if (empty($method)) {
880 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
881 } elseif ($method == 1) {
882 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
883 } elseif ($method == 2) {
884 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
885 } elseif ($method == 3) {
886 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
887 } else {
888 $val = 'BadFirstParameterForGETPOST';
889 }
890
891 return is_array($val);
892}
893
894
904function GETPOSTINT($paramname, $method = 0)
905{
906 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
907}
908
922function GETPOSTFLOAT($paramname, $rounding = '', $option = 2)
923{
924 // price2num() can be used to round to an expected accuracy and/or to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.)
925 return (float) price2num(GETPOST($paramname), $rounding, $option);
926}
927
943function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto', $saverestore = '')
944{
945 $m = array();
946 if ($hourTime === 'getpost' || $hourTime === 'getpostend') {
947 $hour = (GETPOSTISSET($prefix . 'hour') && GETPOSTINT($prefix . 'hour') >= 0) ? GETPOSTINT($prefix . 'hour') : ($hourTime === 'getpostend' ? 23 : 0);
948 $minute = (GETPOSTISSET($prefix . 'min') && GETPOSTINT($prefix . 'min') >= 0) ? GETPOSTINT($prefix . 'min') : ($hourTime === 'getpostend' ? 59 : 0);
949 $second = (GETPOSTISSET($prefix . 'sec') && GETPOSTINT($prefix . 'sec') >= 0) ? GETPOSTINT($prefix . 'sec') : ($hourTime === 'getpostend' ? 59 : 0);
950 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
951 $hour = intval($m[1]);
952 $minute = intval($m[2]);
953 $second = intval($m[3]);
954 } elseif ($hourTime === 'end') {
955 $hour = 23;
956 $minute = 59;
957 $second = 59;
958 } else {
959 $hour = $minute = $second = 0;
960 }
961
962 if (
963 $saverestore
964 && !GETPOSTISSET($prefix . 'day')
965 && !GETPOSTISSET($prefix . 'month')
966 && !GETPOSTISSET($prefix . 'year')
967 && isset($_SESSION['DOLDATE_' . $saverestore . '_day'])
968 && isset($_SESSION['DOLDATE_' . $saverestore . '_month'])
969 && isset($_SESSION['DOLDATE_' . $saverestore . '_year'])
970 ) {
971 $day = $_SESSION['DOLDATE_' . $saverestore . '_day'];
972 $month = $_SESSION['DOLDATE_' . $saverestore . '_month'];
973 $year = $_SESSION['DOLDATE_' . $saverestore . '_year'];
974 } else {
975 $month = GETPOSTINT($prefix . 'month');
976 $day = GETPOSTINT($prefix . 'day');
977 $year = GETPOSTINT($prefix . 'year');
978 }
979
980 // normalize out of range values
981 $hour = (int) min($hour, 23);
982 $minute = (int) min($minute, 59);
983 $second = (int) min($second, 59);
984
985 if ($saverestore) {
986 $_SESSION['DOLDATE_' . $saverestore . '_day'] = $day;
987 $_SESSION['DOLDATE_' . $saverestore . '_month'] = $month;
988 $_SESSION['DOLDATE_' . $saverestore . '_year'] = $year;
989 }
990
991 //print "$hour, $minute, $second, $month, $day, $year, $gm<br>";
992 return dol_mktime($hour, $minute, $second, $month, $day, $year, $gm);
993}
994
1035function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
1036{
1037 global $mysoc, $user, $conf;
1038
1039 if (empty($paramname)) { // Explicit test for null for phan.
1040 return 'BadFirstParameterForGETPOST';
1041 }
1042 if (empty($check)) {
1043 dol_syslog("Deprecated use of GETPOST, called with 1st param = " . $paramname . " and a 2nd param that is '', when calling page " . $_SERVER["PHP_SELF"], LOG_WARNING);
1044 // Enable this line to know who call the GETPOST with '' $check parameter.
1045 //var_dump(getCallerInfoString());
1046 }
1047
1048 if (empty($method)) {
1049 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
1050 } elseif ($method == 1) {
1051 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
1052 } elseif ($method == 2) {
1053 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
1054 } elseif ($method == 3) {
1055 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
1056 } else {
1057 return 'BadThirdParameterForGETPOST';
1058 }
1059
1060 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
1061
1062 if (empty($method) || $method == 3 || $method == 4) {
1063 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
1064 // Clean $relativepathstring
1065 if (constant('DOL_URL_ROOT')) {
1066 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
1067 }
1068 $relativepathstring = ltrim($relativepathstring, '/');
1069 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1070
1071 // Code for search criteria persistence.
1072 // Retrieve saved values if restore_lastsearch_values is set
1073 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
1074 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
1075 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
1076 if (is_array($tmp)) {
1077 foreach ($tmp as $key => $val) {
1078 if ($key == $paramname) { // We are on the requested parameter
1079 $out = $val;
1080 break;
1081 }
1082 }
1083 }
1084 }
1085 // If there is saved contextpage, page or limit
1086 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
1087 $out = $_SESSION['lastsearch_contextpage_' . $relativepathstring];
1088 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
1089 $out = $_SESSION['lastsearch_limit_' . $relativepathstring];
1090 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
1091 $out = $_SESSION['lastsearch_page_' . $relativepathstring];
1092 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
1093 $out = $_SESSION['lastsearch_mode_' . $relativepathstring];
1094 }
1095 } elseif (!isset($_GET['sortfield'])) {
1096 // Else, retrieve default values if we are not doing a sort
1097 // If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set
1098 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1099 // Search default value from $object->field
1100 global $object;
1101 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
1102 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
1103 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
1104 $out = $object->fields[$paramname]['default'];
1105 }
1106 }
1107 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1108 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1109 // Now search in setup to overwrite default values
1110 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
1111 if (isset($user->default_values[$relativepathstring]['createform'])) {
1112 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
1113 $qualified = 0;
1114 if ($defkey != '_noquery_') {
1115 $tmpqueryarraytohave = explode('&', $defkey);
1116 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1117 $foundintru = 0;
1118 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1119 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1120 $foundintru = 1;
1121 }
1122 }
1123 if (!$foundintru) {
1124 $qualified = 1;
1125 }
1126 } else {
1127 $qualified = 1;
1128 }
1129
1130 if ($qualified) {
1131 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
1132 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
1133 break;
1134 }
1135 }
1136 }
1137 }
1138 }
1139 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1140 // Management of default search_filters and sort order
1141 if (!empty($user->default_values)) {
1142 // $user->default_values defined from menu 'Setup - Default values'
1143 //var_dump($user->default_values[$relativepathstring]);
1144 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
1145 // Sorted on which fields ? ASC or DESC ?
1146 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
1147 // Even if paramname is sortfield, data are stored into ['sortorder...']
1148 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
1149 $qualified = 0;
1150 if ($defkey != '_noquery_') {
1151 $tmpqueryarraytohave = explode('&', $defkey);
1152 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1153 $foundintru = 0;
1154 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1155 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1156 $foundintru = 1;
1157 }
1158 }
1159 if (!$foundintru) {
1160 $qualified = 1;
1161 }
1162 } else {
1163 $qualified = 1;
1164 }
1165
1166 if ($qualified) {
1167 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1168 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
1169 if ($out) {
1170 $out .= ', ';
1171 }
1172 if ($paramname == 'sortfield') {
1173 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
1174 }
1175 if ($paramname == 'sortorder') {
1176 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
1177 }
1178 }
1179 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
1180 }
1181 }
1182 }
1183 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
1184 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
1185 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
1186 continue;
1187 }
1188 $qualified = 0;
1189 if ($defkey != '_noquery_') {
1190 $tmpqueryarraytohave = explode('&', $defkey);
1191 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1192 $foundintru = 0;
1193 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1194 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1195 $foundintru = 1;
1196 }
1197 }
1198 if (!$foundintru) {
1199 $qualified = 1;
1200 }
1201 } else {
1202 $qualified = 1;
1203 }
1204
1205 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
1206 // We must keep $_POST and $_GET here
1207 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
1208 // We made a search from quick search menu, do we still use default filter ?
1209 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
1210 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1211 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1212 }
1213 } else {
1214 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1215 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1216 }
1217 break;
1218 }
1219 }
1220 }
1221 }
1222 }
1223 }
1224 }
1225 }
1226
1227 // Substitution variables for GETPOST (used to get final url with variable parameters or final default value, when using variable parameters __XXX__ in the GET URL)
1228 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1229 // We do this only if var is a GET. If it is a POST, may be we want to post the text with vars as the setup text.
1230 '@phan-var-force string $paramname';
1231 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1232 $reg = array();
1233 $regreplace = array();
1234 $maxloop = 20;
1235 $loopnb = 0; // Protection against infinite loop
1236
1237 while (preg_match('/__([A-Z0-9]+(?:_[A-Z0-9]+){0,3})__/i', $out, $reg) && ($loopnb < $maxloop)) { // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side.
1238 $loopnb++;
1239 $newout = '';
1240
1241 if ($reg[1] == 'DAY') {
1242 $tmp = dol_getdate(dol_now(), true);
1243 $newout = $tmp['mday'];
1244 } elseif ($reg[1] == 'MONTH') {
1245 $tmp = dol_getdate(dol_now(), true);
1246 $newout = $tmp['mon'];
1247 } elseif ($reg[1] == 'YEAR') {
1248 $tmp = dol_getdate(dol_now(), true);
1249 $newout = $tmp['year'];
1250 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1251 $tmp = dol_getdate(dol_now(), true);
1252 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1253 $newout = $tmp2['day'];
1254 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1255 $tmp = dol_getdate(dol_now(), true);
1256 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1257 $newout = $tmp2['month'];
1258 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1259 $tmp = dol_getdate(dol_now(), true);
1260 $newout = ($tmp['year'] - 1);
1261 } elseif ($reg[1] == 'NEXT_DAY') {
1262 $tmp = dol_getdate(dol_now(), true);
1263 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1264 $newout = $tmp2['day'];
1265 } elseif ($reg[1] == 'NEXT_MONTH') {
1266 $tmp = dol_getdate(dol_now(), true);
1267 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1268 $newout = $tmp2['month'];
1269 } elseif ($reg[1] == 'NEXT_YEAR') {
1270 $tmp = dol_getdate(dol_now(), true);
1271 $newout = ($tmp['year'] + 1);
1272 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1273 $newout = $mysoc->country_id;
1274 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1275 $newout = $user->id;
1276 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1277 $newout = $user->fk_user;
1278 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1279 $newout = $conf->entity;
1280 } elseif ($reg[1] == 'ID') {
1281 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1282 } else {
1283 $newout = 'REGREPLACE_' . $loopnb; // Key not found, we replace with temporary string to reload later
1284 $regreplace[$loopnb] = $reg[0];
1285 }
1286 //var_dump('__'.$reg[1].'__ -> '.$newout);
1287 $out = preg_replace('/__' . preg_quote($reg[1], '/') . '__/', $newout, $out);
1288 }
1289 if (!empty($regreplace)) {
1290 foreach ($regreplace as $key => $value) {
1291 $out = preg_replace('/REGREPLACE_' . $key . '/', $value, $out);
1292 }
1293 }
1294 }
1295
1296 // Check type of variable and make sanitization according to this
1297 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1298 $tmpcheck = 'alphanohtml';
1299 if (empty($out)) {
1300 $out = array();
1301 } elseif (!is_array($out)) {
1302 $out = explode(',', $out);
1303 } else {
1304 $tmparray = explode(':', $check);
1305 if (!empty($tmparray[1])) {
1306 $tmpcheck = $tmparray[1];
1307 }
1308 }
1309 foreach ($out as $outkey => $outval) {
1310 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1311 }
1312 } else {
1313 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1314 // we use the < or > to make a search on a numeric value to do higher or lower so we can add a space to break html tags
1315 if (strpos($paramname, 'search_') === 0) {
1316 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1317 }
1318
1319 // @phan-suppress-next-line UnknownSanitizeType
1320 $out = sanitizeVal($out, $check, $filter, $options);
1321 }
1322
1323 // Sanitizing for special parameters.
1324 // Note: There is no reason to allow the backtopage/backtopageforcancel/backtopagejs, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
1325 // @TODO Merge backtopage with backtourl
1326 // @TODO Rename backtolist into backtopagelist
1327 if (preg_match('/^backto/i', $paramname)) {
1328 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1329 $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retrieve it after other replacements.
1330 do {
1331 $oldstringtoclean = $out;
1332 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1333 $out = preg_replace(array('/^[^\?]*%/'), '', $out); // We remove any % chars before the ?. Example in url: '/product/stock/card.php?action=create&backtopage=%2Fdolibarr_dev%2Fhtdocs%2Fpro%25duct%2Fcard.php%3Fid%3Dabc'
1334 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1335 } while ($oldstringtoclean != $out);
1336 }
1337
1338 // Code for search criteria persistence.
1339 // Save data into session if key start with 'search_'
1340 if (empty($method) || $method == 3 || $method == 4) {
1341 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1342 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1343
1344 // We save search key only if $out not empty that means:
1345 // - posted value not empty, or
1346 // - if posted value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
1347
1348 if ($out != '' && isset($user)) { // $out = '0' or 'abc', it is a search criteria to keep
1349 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1350 }
1351 }
1352 }
1353
1354 return $out;
1355}
1356
1366function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1367{
1368 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1369 // Check is done after replacement
1370 if ($out === null) {
1371 $out = '';
1372 }
1373 switch ($check) {
1374 case 'none':
1375 case 'password':
1376 break;
1377 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1378 if (!is_numeric($out)) {
1379 $out = '';
1380 }
1381 break;
1382 case 'intcomma':
1383 if (is_array($out)) {
1384 $out = implode(',', $out);
1385 }
1386 if (preg_match('/[^0-9,-]+/i', $out)) {
1387 $out = '';
1388 }
1389 break;
1390 case 'san_alpha':
1391 dol_syslog("Use of parameter value 'san_alpha' in GETPOST is deprecated. Use 'alphanohtml', 'aZ09comma', ...", LOG_WARNING);
1392 $out = filter_var($out, FILTER_SANITIZE_STRING);
1393 break;
1394 case 'email':
1395 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1396 break;
1397 case 'url':
1398 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1399 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1400 // TODO Allow ( ) but only into password of https://login:password@domain...
1401 break;
1402 case 'aZ':
1403 if (!is_array($out)) {
1404 $out = trim($out);
1405 if (preg_match('/[^a-z]+/i', $out)) {
1406 $out = '';
1407 }
1408 }
1409 break;
1410 case 'aZ09':
1411 if (!is_array($out)) {
1412 $out = trim($out);
1413 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1414 $out = '';
1415 }
1416 }
1417 break;
1418 case 'aZ09arobase': // great to sanitize $objecttype parameter
1419 if (!is_array($out)) {
1420 $out = trim($out);
1421 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1422 $out = '';
1423 }
1424 }
1425 break;
1426 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1427 if (!is_array($out)) {
1428 $out = trim($out);
1429 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1430 $out = '';
1431 }
1432 }
1433 break;
1434 case 'alpha': // No html and no ../ and "
1435 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1436 if (!is_array($out)) {
1437 $out = trim($out);
1438 do {
1439 $oldstringtoclean = $out;
1440 // Remove html tags
1441 $out = dol_string_nohtmltag($out, 0);
1442 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1443 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1444 // Remove also other dangerous string sequences
1445 // '../' or '..\' is dangerous because it allows dir transversals
1446 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1447 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1448 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1449 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1450 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1451 } while ($oldstringtoclean != $out);
1452 // keep lines feed
1453 }
1454 break;
1455 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email@domain.com>". Less secured than 'alphanohtml'
1456 if (!is_array($out)) {
1457 $out = trim($out);
1458 do {
1459 $oldstringtoclean = $out;
1460 // Decode html entities
1461 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1462 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1463 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1464 // Remove also other dangerous string sequences
1465 // '../' or '..\' is dangerous because it allows dir transversals
1466 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1467 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1468 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1469 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1470 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1471 } while ($oldstringtoclean != $out);
1472 }
1473 break;
1474 case 'nohtml': // No html. Valid for JSON strings.
1475 $out = dol_string_nohtmltag($out, 0);
1476 break;
1477 case 'restricthtmlnolink':
1478 case 'restricthtml': // Recommended for most html textarea
1479 case 'restricthtmlallowclass':
1480 case 'restricthtmlallowiframe':
1481 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1482 case 'restricthtmlallowunvalid':
1483 $out = dol_htmlwithnojs($out, 1, $check);
1484 break;
1485 case 'custom':
1486 if (!empty($out)) {
1487 if (empty($filter)) {
1488 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1489 }
1490 if (is_null($options)) {
1491 $options = 0;
1492 }
1493 $out = filter_var($out, $filter, $options);
1494 }
1495 break;
1496 default:
1497 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1498 $out = GETPOST($out, 'alphanohtml');
1499 break;
1500 }
1501
1502 return $out;
1503}
1504
1513function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1514{
1515 global $dolibarr_main_force_https;
1516
1517 if ($expire == -1) {
1518 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1519 }
1520
1521 if (PHP_VERSION_ID < 70300) {
1522 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1523 } else {
1524 // Only available for php >= 7.3
1525 $cookieparams = array(
1526 'expires' => empty($cookievalue) ? 0 : $expire,
1527 'path' => '/',
1528 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1529 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1530 'httponly' => true,
1531 'samesite' => 'Lax' // None || Lax || Strict
1532 );
1533 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1534 }
1535 if (empty($cookievalue)) {
1536 unset($_COOKIE[$cookiename]);
1537 }
1538}
1539
1540if (!function_exists('dol_getprefix')) {
1551 function dol_getprefix($mode = '')
1552 {
1553 // If prefix is for email (we need to have $conf already loaded for this case)
1554 if ($mode == 'email') {
1555 global $conf;
1556
1557 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1558 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1559 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1560 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1561 return $_SERVER["SERVER_NAME"];
1562 }
1563 }
1564
1565 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1566 if (!empty($conf->file->instance_unique_id)) {
1567 return sha1('dolibarr' . $conf->file->instance_unique_id);
1568 }
1569
1570 // For backward compatibility when instance_unique_id is not set
1571 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1572 }
1573
1574 // If prefix is for session (no need to have $conf loaded)
1575 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1576 $tmp_instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance
1577
1578 // The recommended value (may be not defined for old versions)
1579 if (!empty($tmp_instance_unique_id)) {
1580 return sha1('dolibarr' . $tmp_instance_unique_id);
1581 }
1582
1583 // For backward compatibility when instance_unique_id is not set
1584 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1585 return sha1($_SERVER["SERVER_NAME"] . $_SERVER["DOCUMENT_ROOT"] . DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1586 } else {
1587 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1588 }
1589 }
1590}
1591
1602function dol_include_once($relpath, $classname = '')
1603{
1604 global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we make "include". Other globals var must be retrieved with $GLOBALS['var']
1605
1606 if (strpos($relpath, '..') !== false) {
1607 // Found a not valid path
1608 dol_syslog('functions::dol_include_once Tried to load a file with a path including a forbidden sequence ".." : ' . $relpath, LOG_WARNING);
1609 return false;
1610 }
1611 if (!preg_match('/\.php$/', $relpath)) {
1612 // Found a not valid path
1613 dol_syslog('functions::dol_include_once Tried to load a file that is not a PHP file : ' . $relpath, LOG_WARNING);
1614 return false;
1615 }
1616
1617 $fullpath = dol_buildpath($relpath);
1618
1619 if (!file_exists($fullpath)) {
1620 dol_syslog('functions::dol_include_once Tried to load unexisting file: ' . $relpath, LOG_WARNING);
1621 return false;
1622 }
1623 if (!empty($classname) && !class_exists($classname)) {
1624 return include $fullpath;
1625 } else {
1626 return include_once $fullpath;
1627 }
1628}
1629
1630
1644function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1645{
1646 global $conf;
1647
1648 $path = preg_replace('/^\//', '', $path);
1649
1650 if (empty($type)) { // For a filesystem path
1651 $res = DOL_DOCUMENT_ROOT . '/' . $path; // Standard default path
1652 if (is_array($conf->file->dol_document_root)) {
1653 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1654 if ($key == 'main') {
1655 continue;
1656 }
1657 // if (@file_exists($dirroot.'/'.$path)) {
1658 if (@file_exists($dirroot . '/' . $path)) { // avoid [php:warn]
1659 if ($key != 'main' && preg_match('/^core\//', $path)) { // When searching into an alternative custom path, we don't want path like 'core/...' because path should be 'modulename/core/...'
1660 continue;
1661 }
1662 $res = $dirroot . '/' . $path;
1663 return $res;
1664 }
1665 }
1666 }
1667 if ($returnemptyifnotfound) {
1668 // Not found into alternate dir
1669 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1670 return '';
1671 }
1672 }
1673 } else {
1674 // For an url path
1675 // We try to get local path of file on filesystem from url
1676 // Note that trying to know if a file on disk exist by forging path on disk from url
1677 // works only for some web server and some setup. This is bugged when
1678 // using proxy, rewriting, virtual path, etc...
1679 $res = '';
1680 if ($type == 1) {
1681 $res = DOL_URL_ROOT . '/' . $path; // Standard value
1682 }
1683 if ($type == 2) {
1684 $res = DOL_MAIN_URL_ROOT . '/' . $path; // Standard value
1685 }
1686 if ($type == 3) {
1687 $res = DOL_URL_ROOT . '/' . $path;
1688 }
1689
1690 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1691 if ($key == 'main') {
1692 if ($type == 3) {
1693 /*global $dolibarr_main_url_root;*/
1694
1695 // Define $urlwithroot
1696 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1697 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1698 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1699
1700 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . '/' . $path; // Test on start with http is for old conf syntax
1701 }
1702 continue;
1703 }
1704 $regs = array();
1705 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1706 if (!empty($regs[1])) {
1707 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1708 //if (file_exists($dirroot.'/'.$regs[1])) {
1709 if (@file_exists($dirroot . '/' . $regs[1])) { // avoid [php:warn]
1710 if ($type == 1) {
1711 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1712 } elseif ($type == 2) {
1713 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1714 } elseif ($type == 3) {
1715 /*global $dolibarr_main_url_root;*/
1716
1717 // Define $urlwithroot
1718 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1719 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1720 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1721
1722 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . $conf->file->dol_url_root[$key] . '/' . $path; // Test on start with http is for old conf syntax
1723 }
1724 break;
1725 }
1726 }
1727 }
1728 }
1729
1730 return $res;
1731}
1732
1741function dolBuildUrl($url, $params = [], $addtoken = false)
1742{
1743 global $db, $hookmanager;
1744
1745 if (!is_object($hookmanager)) {
1746 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
1747 $hookmanager = new HookManager($db);
1748 }
1749 if ((!isset($params['mainmenu']) || empty($params['mainmenu'])) && GETPOSTISSET('mainmenu')) {
1750 $params = array_merge($params, ['mainmenu' => (GETPOST('mainmenu', 'restricthtml'))]);
1751 }
1752 if ((!isset($params['leftmenu'])/* || empty($params['leftmenu']) */) && GETPOSTISSET('leftmenu')) { // do not fill leftmenu if we have leftmenu=
1753 $params = array_merge($params, ['leftmenu' => (GETPOST('leftmenu', 'restricthtml'))]);
1754 }
1755 $parameters = [
1756 'path' => &$url,
1757 'params' => &$params,
1758 'addtoken' => &$addtoken,
1759 ];
1760 $hookmanager->executeHooks('buildurl', $parameters);
1761 if ($addtoken) {
1762 $params = array_merge($params, ['token' => newToken()]);
1763 }
1764 // TODO TO REMOVE
1765 if (getDolGlobalString('MAIN_DEBUG_DOL_BUILDURL')) {
1766 $params = array_merge($params, ['debug' => 'debug']);
1767 }
1768 if ($params) {
1769 $url .= '?' . http_build_query($params);
1770 }
1771
1772 return $url;
1773}
1774
1785function dol_get_object_properties($obj, $properties = [])
1786{
1787 // Get real properties using get_object_vars() if $properties is empty
1788 if (empty($properties)) {
1789 return get_object_vars($obj);
1790 }
1791
1792 $existingProperties = [];
1793 $realProperties = get_object_vars($obj);
1794
1795 // Get the real or magic property values
1796 foreach ($properties as $property) {
1797 if (array_key_exists($property, $realProperties)) {
1798 // Real property, add the value
1799 $existingProperties[$property] = $obj->{$property};
1800 } elseif (property_exists($obj, $property)) {
1801 // Magic property
1802 $existingProperties[$property] = $obj->{$property};
1803 }
1804 }
1805
1806 return $existingProperties;
1807}
1808
1809
1825function dol_clone($srcobject, $native = 2)
1826{
1827 if ($native == 0) {
1828 // deprecated method, use the method with native = 2 instead
1829 dol_syslog("Warning, call to dol_clone() with the deprecated parameter native=0, use 2 instead", LOG_WARNING);
1830
1831 $tmpsavdb = null;
1832 if (isset($srcobject->db) && isset($srcobject->db->db) && is_object($srcobject->db->db) && get_class($srcobject->db->db) == 'PgSql\Connection') {
1833 $tmpsavdb = $srcobject->db;
1834 unset($srcobject->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1835 }
1836
1837 $myclone = unserialize(serialize($srcobject)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1838
1839 if (!empty($tmpsavdb)) {
1840 $srcobject->db = $tmpsavdb;
1841 }
1842 } elseif ($native == 2) {
1843 // recommended method to have a full secured isolated cloned object
1844 $myclone = new stdClass();
1845 $tmparray = get_object_vars($srcobject); // return only public properties
1846
1847 if (is_array($tmparray)) {
1848 foreach ($tmparray as $propertykey => $propertyval) {
1849 if (is_scalar($propertyval) || is_array($propertyval)) {
1850 $myclone->$propertykey = $propertyval;
1851 }
1852 }
1853 }
1854 } else {
1855 $myclone = clone $srcobject; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (referring to the same target/variable)
1856 }
1857
1858 return $myclone;
1859}
1860
1861
1870function dol_clone_in_array($srcobject, $startlevel = 0)
1871{
1872 if (is_object($srcobject)) {
1873 $srcobject = get_object_vars($srcobject); // exclude private/protected properties
1874 }
1875
1876 if (is_array($srcobject)) {
1877 $result = [];
1878 foreach ($srcobject as $key => $value) {
1879 if (in_array($key, array('db', 'fields', 'error', 'errorhidden', 'errors', 'oldcopy', 'linkedObjects', 'linked_objects'))) {
1880 continue;
1881 }
1882 $result[$key] = dol_clone_in_array($value, $startlevel + 1);
1883 }
1884 return $result;
1885 }
1886
1887 return $srcobject;
1888}
1889
1890
1900function dol_size($size, $type = '')
1901{
1902 global $conf;
1903 if (empty($conf->dol_optimize_smallscreen)) {
1904 return $size;
1905 }
1906 if ($type == 'width' && $size > 250) {
1907 return 250;
1908 } else {
1909 return 10;
1910 }
1911}
1912
1913
1927function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0, $allowdash = 0)
1928{
1929 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1930 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1931 // Char '/' and '\' are file delimiters.
1932 // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1933 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1934 if ($includequotes) {
1935 $filesystem_forbidden_chars[] = "'";
1936 }
1937 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1938 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1939 if (empty($allowdash)) {
1940 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1941 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1942 }
1943 $tmp = str_replace('..', '', $tmp);
1944 $tmp = str_replace('~', $newstr, $tmp);
1945 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1946
1947 return $tmp;
1948}
1949
1950
1963function dol_sanitizePathName($str, $newstr = '_', $unaccent = 0, $allowdash = 0)
1964{
1965 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1966 // Char '>' '<' '|' '$' ';' and '`' are special chars for shells.
1967 // Char '?' and '*' are for wild card chars.
1968 // Char '"' is dangerous.
1969 // Char '°' is just not expected.
1970 // Chars '-' and '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1971 // Chars '--' and '~' can be used for path transversal
1972 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1973
1974 $tmp = $str;
1975 if ($unaccent) {
1976 $tmp = dol_string_unaccent($tmp);
1977 }
1978 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1979 $tmp = preg_replace('/\-\-+/', $newstr, $tmp);
1980 if (empty($allowdash)) {
1981 $tmp = preg_replace('/\s+\-([^\s])/', ' '.$newstr.'$1', $tmp);
1982 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1983 }
1984 $tmp = str_replace('..', $newstr, $tmp);
1985 $tmp = str_replace('~', $newstr, $tmp);
1986 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1987
1988 return $tmp;
1989}
1990
1998function dol_sanitizeUrl($stringtoclean, $type = 1)
1999{
2000 // We clean string because some hacks try to obfuscate evil strings by inserting non printable chars. Example: 'java(ascci09)scr(ascii00)ipt' is processed like 'javascript' (whatever is place of evil ascii char)
2001 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
2002 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2003 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
2004 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
2005
2006 $stringtoclean = str_replace('\\', '/', $stringtoclean);
2007 if ($type == 1) {
2008 // removing : should disable links to external url like http:aaa)
2009 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
2010 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
2011 }
2012
2013 do {
2014 $oldstringtoclean = $stringtoclean;
2015 // removing '&colon' should disable links to external url like http:aaa)
2016 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
2017 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
2018 } while ($oldstringtoclean != $stringtoclean);
2019
2020 if ($type == 1) {
2021 // removing '//' should disable links to external url like //aaa or http//)
2022 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
2023 }
2024
2025 return $stringtoclean;
2026}
2027
2034function dol_sanitizeEmail($stringtoclean)
2035{
2036 do {
2037 $oldstringtoclean = $stringtoclean;
2038 $stringtoclean = str_ireplace(array('"', ':', '[', ']', "\n", "\r", '\\', '\/'), '', $stringtoclean);
2039 } while ($oldstringtoclean != $stringtoclean);
2040
2041 return $stringtoclean;
2042}
2043
2052function dol_sanitizeKeyCode($str)
2053{
2054 return preg_replace('/[^\w]+/', '', $str);
2055}
2056
2057
2066function dol_string_unaccent($str)
2067{
2068 if (is_null($str)) {
2069 return '';
2070 }
2071
2072 if (utf8_check($str)) {
2073 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
2074 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
2075 return $transliterator->transliterate($str);
2076 }
2077 // See http://www.utf8-chartable.de/
2078 $string = rawurlencode($str);
2079 $replacements = array(
2080 '%C3%80' => 'A',
2081 '%C3%81' => 'A',
2082 '%C3%82' => 'A',
2083 '%C3%83' => 'A',
2084 '%C3%84' => 'A',
2085 '%C3%85' => 'A',
2086 '%C3%87' => 'C',
2087 '%C3%88' => 'E',
2088 '%C3%89' => 'E',
2089 '%C3%8A' => 'E',
2090 '%C3%8B' => 'E',
2091 '%C3%8C' => 'I',
2092 '%C3%8D' => 'I',
2093 '%C3%8E' => 'I',
2094 '%C3%8F' => 'I',
2095 '%C3%91' => 'N',
2096 '%C3%92' => 'O',
2097 '%C3%93' => 'O',
2098 '%C3%94' => 'O',
2099 '%C3%95' => 'O',
2100 '%C3%96' => 'O',
2101 '%C5%A0' => 'S',
2102 '%C3%99' => 'U',
2103 '%C3%9A' => 'U',
2104 '%C3%9B' => 'U',
2105 '%C3%9C' => 'U',
2106 '%C3%9D' => 'Y',
2107 '%C5%B8' => 'y',
2108 '%C3%A0' => 'a',
2109 '%C3%A1' => 'a',
2110 '%C3%A2' => 'a',
2111 '%C3%A3' => 'a',
2112 '%C3%A4' => 'a',
2113 '%C3%A5' => 'a',
2114 '%C3%A7' => 'c',
2115 '%C3%A8' => 'e',
2116 '%C3%A9' => 'e',
2117 '%C3%AA' => 'e',
2118 '%C3%AB' => 'e',
2119 '%C3%AC' => 'i',
2120 '%C3%AD' => 'i',
2121 '%C3%AE' => 'i',
2122 '%C3%AF' => 'i',
2123 '%C3%B1' => 'n',
2124 '%C3%B2' => 'o',
2125 '%C3%B3' => 'o',
2126 '%C3%B4' => 'o',
2127 '%C3%B5' => 'o',
2128 '%C3%B6' => 'o',
2129 '%C5%A1' => 's',
2130 '%C3%B9' => 'u',
2131 '%C3%BA' => 'u',
2132 '%C3%BB' => 'u',
2133 '%C3%BC' => 'u',
2134 '%C3%BD' => 'y',
2135 '%C3%BF' => 'y',
2136 '%CC%80' => '',
2137 '%CC%81' => '',
2138 '%CC%82' => '',
2139 '%CC%83' => '',
2140 '%CC%84' => '',
2141 '%CC%85' => '',
2142 '%CC%86' => '',
2143 '%CC%87' => '',
2144 '%CC%88' => '',
2145 '%CC%89' => '',
2146 '%CC%8A' => '',
2147 '%CC%8B' => '',
2148 '%CC%8C' => '',
2149 '%CC%8D' => '',
2150 '%CC%8E' => '',
2151 '%CC%8F' => '',
2152 '%CC%90' => '',
2153 '%CC%91' => '',
2154 '%CC%A7' => '',
2155 );
2156 $string = strtr($string, $replacements);
2157 return rawurldecode($string);
2158 } else {
2159 // See http://www.ascii-code.com/
2160 $string = strtr(
2161 $str,
2162 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
2163 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
2164 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
2165 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
2166 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
2167 \xF9\xFA\xFB\xFC\xFD\xFF",
2168 "AAAAAAC
2169 EEEEIIIIDN
2170 OOOOOUUUY
2171 aaaaaaceeee
2172 iiiidnooooo
2173 uuuuyy"
2174 );
2175 $string = strtr($string, array("\xC4" => "Ae", "\xC6" => "AE", "\xD6" => "Oe", "\xDC" => "Ue", "\xDE" => "TH", "\xDF" => "ss", "\xE4" => "ae", "\xE6" => "ae", "\xF6" => "oe", "\xFC" => "ue", "\xFE" => "th"));
2176 return $string;
2177 }
2178}
2179
2193function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
2194{
2195 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
2196 if (empty($keepspaces)) {
2197 $forbidden_chars_to_replace[] = " ";
2198 }
2199 $forbidden_chars_to_remove = array();
2200 //$forbidden_chars_to_remove=array("(",")");
2201
2202 if (is_array($badcharstoreplace)) {
2203 $forbidden_chars_to_replace = $badcharstoreplace;
2204 }
2205 if (is_array($badcharstoremove)) {
2206 $forbidden_chars_to_remove = $badcharstoremove;
2207 }
2208
2209 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2210 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
2211}
2212
2213
2227function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2228{
2229 if ($removetabcrlf) {
2230 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2231 } else {
2232 return preg_replace('/[\x00-\x08\x11-\x12\x14-\x1F\x7F]/u', '', $str); // /u operator should make UTF8 valid characters being ignored so are not included into the replace
2233 }
2234}
2235
2242function dolSlugify($stringtoslugify)
2243{
2244 $slug = dol_string_unaccent($stringtoslugify);
2245
2246 // Convert special characters to their ASCII equivalents
2247 if (function_exists('iconv')) {
2248 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2249 }
2250
2251 // Convert to lowercase
2252 $slug = strtolower($slug);
2253
2254 // Replace non-alphanumeric characters with hyphens
2255 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2256
2257 // Remove leading and trailing hyphens
2258 $slug = trim($slug, '-');
2259
2260 return $slug;
2261}
2262
2271function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2272{
2273 if (is_null($stringtoescape)) {
2274 return '';
2275 }
2276
2277 // escape quotes and backslashes, newlines, etc.
2278 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2279 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2280 if (empty($noescapebackslashn)) {
2281 $substitjs["\n"] = '\\n';
2282 $substitjs['\\'] = '\\\\';
2283 }
2284 if (empty($mode)) {
2285 $substitjs["'"] = "\\'";
2286 $substitjs['"'] = "\\'";
2287 } elseif ($mode == 1) {
2288 $substitjs["'"] = "\\'";
2289 } elseif ($mode == 2) {
2290 $substitjs['"'] = '\\"';
2291 } elseif ($mode == 3) {
2292 $substitjs["'"] = "\\'";
2293 $substitjs['"'] = "\\\"";
2294 }
2295 return strtr((string) $stringtoescape, $substitjs);
2296}
2297
2307function dol_escape_uri($stringtoescape)
2308{
2309 return rawurlencode($stringtoescape);
2310}
2311
2318function dol_escape_json($stringtoescape)
2319{
2320 return str_replace('"', '\"', $stringtoescape);
2321}
2322
2330function dol_escape_php($stringtoescape, $stringforquotes = 2)
2331{
2332 if (is_null($stringtoescape)) {
2333 return '';
2334 }
2335
2336 if ($stringforquotes == 2) {
2337 return str_replace('"', "'", $stringtoescape);
2338 } elseif ($stringforquotes == 1) {
2339 // We remove the \ char.
2340 // If we allow the \ char, we can have $stringtoescape =
2341 // abc\';phpcodedanger; so the escapement will become
2342 // abc\\';phpcodedanger; and injecting this into
2343 // $a='...' will give $ac='abc\\';phpcodedanger;
2344 $stringtoescape = str_replace('\\', '', $stringtoescape);
2345 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2346 }
2347
2348 return 'Bad parameter for stringforquotes in dol_escape_php';
2349}
2350
2357function dol_escape_all($stringtoescape)
2358{
2359 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2360}
2361
2368function dol_escape_xml($stringtoescape)
2369{
2370 return $stringtoescape;
2371}
2372
2382function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2383{
2384 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2385}
2386
2395function dolPrintText($s)
2396{
2397 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2398}
2399
2410function dolPrintHTML($s, $allowiframe = 0)
2411{
2412 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2413 //$isAlreadyHTML = dol_textishtml($s);
2414
2415 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2416 // encode only special char like é but not &, <, >, ", ' if already HTML.
2417 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2418
2419 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2420}
2421
2432function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2433{
2434 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2435 if (!empty($allowothertags) && is_array($allowothertags)) {
2436 $allowedtags = array_merge($allowedtags, $allowothertags);
2437 }
2438 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2439 // The dol_escape_htmltag will escape html tags.
2440 if ($escapeonlyhtmltags) {
2441 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2442 } else {
2443 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2444 }
2445}
2446
2455function dolPrintHTMLForAttributeUrl($s)
2456{
2457 // The dol_htmlentitiesbr has been removed compared to dolPrintHTMLForAttribute because we know content is a HTML URL string (even if we have no way to detect it automatically)
2458 // The dol_escape_htmltag will escape html chars.
2459 $escapeonlyhtmltags = 1;
2460 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2461}
2462
2472function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2473{
2474 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2475}
2476
2483function dolPrintPassword($s)
2484{
2485 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2486}
2487
2488
2505function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2506{
2507 if ($noescapetags == 'common') {
2508 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,ol,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
2509 // Add also html5 tags
2510 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2511 }
2512 if ($cleanalsojavascript) {
2513 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2514 }
2515
2516 // escape quotes and backslashes, newlines, etc.
2517 if ($escapeonlyhtmltags) {
2518 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2519 } else {
2520 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2521 // Because content can be or not already HTML.
2522 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2523 // In a future, we should not need this
2524
2525 $tmp = (string) $stringtoescape;
2526
2527 // We protect the 6 special entities that we don't want to decode.
2528 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2529 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2530 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2531 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2532 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2533 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2534
2535 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2536
2537 // We restore the 6 special entities that we don't want to have been decoded by previous command
2538 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2539 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2540 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2541 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2542 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2543 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2544
2545 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2546 }
2547 if (!$keepb) {
2548 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2549 }
2550 if (!$keepn) {
2551 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2552 } elseif ($keepn == -1) {
2553 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2554 }
2555
2556 if ($escapeonlyhtmltags) {
2557 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2558 return $tmp;
2559 } else {
2560 // Now we protect all the tags we want to keep
2561 $tmparrayoftags = array();
2562 if ($noescapetags) {
2563 $tmparrayoftags = explode(',', $noescapetags);
2564 }
2565
2566 if (count($tmparrayoftags)) {
2567 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2568
2569 $reg = array();
2570 // Remove reserved keywords. They are forbidden in a source string
2571 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2572
2573 foreach ($tmparrayoftags as $tagtoreplace) {
2574 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2575 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . '>/', '__BEGINTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2576 $tmp = str_ireplace('</' . $tagtoreplace . '>', '__ENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2577 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . ' \/>/', '__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2578
2579 // For case of tag with attributes
2580 do {
2581 $tmpold = $tmp;
2582
2583 if (preg_match('/<' . preg_quote($tagtoreplace, '/') . '(\s+)([^>]+)>/', $tmp, $reg)) {
2584 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2585 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2586 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2587 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2588 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2589 $tmp = str_replace('<' . $tagtoreplace . $reg[1] . $reg[2] . '>', '__BEGINTAGTOREPLACE' . $tagtoreplace . '[' . $tmpattributes . ']__', $tmp);
2590 }
2591
2592 $diff = strcmp($tmpold, $tmp);
2593 } while ($diff);
2594 }
2595
2596 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2597 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2598 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2599 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2600 }
2601
2602 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2603 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2604
2605 //print $result;
2606
2607 if (count($tmparrayoftags)) {
2608 // Restore protected tags
2609 foreach ($tmparrayoftags as $tagtoreplace) {
2610 $result = str_ireplace('__BEGINTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . '>', $result);
2611 $result = preg_replace('/__BEGINTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1>', $result);
2612 $result = str_ireplace('__ENDTAGTOREPLACE' . $tagtoreplace . '__', '</' . $tagtoreplace . '>', $result);
2613 $result = str_ireplace('__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . ' />', $result);
2614 $result = preg_replace('/__BEGINENDTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1 />', $result);
2615 }
2616
2617 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2618
2619 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2620 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2621 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2622 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2623 }
2624
2625 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2626
2627 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2628
2629 return $result;
2630 }
2631}
2632
2640function dol_strtolower($string, $encoding = "UTF-8")
2641{
2642 if (function_exists('mb_strtolower')) {
2643 return mb_strtolower($string, $encoding);
2644 } else {
2645 return strtolower($string);
2646 }
2647}
2648
2657function dol_strtoupper($string, $encoding = "UTF-8")
2658{
2659 if (function_exists('mb_strtoupper')) {
2660 return mb_strtoupper($string, $encoding);
2661 } else {
2662 return strtoupper($string);
2663 }
2664}
2665
2674function dol_ucfirst($string, $encoding = "UTF-8")
2675{
2676 if (function_exists('mb_substr')) {
2677 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding) . mb_substr($string, 1, null, $encoding);
2678 } else {
2679 return ucfirst($string);
2680 }
2681}
2682
2691function dol_ucwords($string, $encoding = "UTF-8")
2692{
2693 if (function_exists('mb_convert_case')) {
2694 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2695 } else {
2696 return ucwords($string);
2697 }
2698}
2699
2700
2706function getCallerInfoString()
2707{
2708 $backtrace = debug_backtrace();
2709 $msg = "";
2710 if (count($backtrace) >= 1) {
2711 $pos = 1;
2712 if (count($backtrace) == 1) {
2713 $pos = 0;
2714 }
2715 $trace = $backtrace[$pos];
2716 if (isset($trace['file'], $trace['line'])) {
2717 $msg = " From {$trace['file']}:{$trace['line']}.";
2718 }
2719 }
2720 return $msg;
2721}
2722
2745function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2746{
2747 global $conf, $user, $debugbar;
2748
2749 // If syslog module enabled
2750 if (!isModEnabled('syslog')) {
2751 return;
2752 }
2753
2754 // Check if we are into execution of code of a website
2755 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2756 global $website, $websitekey;
2757 if (is_object($website) && !empty($website->ref)) {
2758 $suffixinfilename .= '_website_' . $website->ref;
2759 } elseif (!empty($websitekey)) {
2760 $suffixinfilename .= '_website_' . $websitekey;
2761 }
2762 }
2763
2764 // Check if we have a forced suffix
2765 if (defined('USESUFFIXINLOG')) {
2766 $suffixinfilename .= constant('USESUFFIXINLOG');
2767 }
2768
2769 if ($ident < 0) {
2770 foreach ($conf->loghandlers as $loghandlerinstance) {
2771 $loghandlerinstance->setIdent($ident);
2772 }
2773 }
2774
2775 if (!empty($message)) {
2776 // Test log level
2777 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2778 $logLevels = array(LOG_EMERG => 'EMERG', LOG_ALERT => 'ALERT', LOG_CRIT => 'CRITICAL', LOG_ERR => 'ERR', LOG_WARNING => 'WARN', LOG_NOTICE => 'NOTICE', LOG_INFO => 'INFO', LOG_DEBUG => 'DEBUG');
2779
2780 if (!array_key_exists($level, $logLevels)) {
2781 dol_syslog('Error Bad Log Level ' . $level, LOG_ERR);
2782 $level = LOG_ERR;
2783 }
2784 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2785 return;
2786 }
2787
2788 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2789 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2790 }
2791
2792 // If adding log inside HTML page is required
2793 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2794 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))
2795 ) {
2796 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2797 $osuser = " " . sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2798
2799 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S") . " " . sprintf("%-7s", $logLevels[$level]) . " " . $ospid . " " . $osuser . " " . $message;
2800 }
2801
2802 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2803 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2804 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2805 print "\n\n<!-- Log start\n";
2806 print dol_escape_htmltag($message) . "\n";
2807 print "Log end -->\n";
2808 }
2809
2810 $data = array(
2811 'message' => $message,
2812 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2813 'level' => $level,
2814 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2815 'ip' => '',
2816 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2817 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2818 );
2819
2820 // For log, we want the reliable IP first.
2821 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2822 if (!empty($remoteip)) {
2823 $data['ip'] = $remoteip;
2824 // This is when server run behind a reverse proxy
2825 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2826 // $data['ip'] is last
2827 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2828 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2829 $data['ip'] = '';
2830 $foundremoteip = 0;
2831 $j = 0;
2832 foreach ($tmpips as $tmpip) {
2833 $tmpip = trim($tmpip);
2834 if (strtolower($tmpip) == strtolower($remoteip)) {
2835 $foundremoteip = 1;
2836 }
2837 if (empty($data['ip'])) {
2838 $data['ip'] = $tmpip;
2839 } else {
2840 $j++;
2841 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2842 }
2843 }
2844 if (!$foundremoteip) {
2845 $j++;
2846 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2847 }
2848 $data['ip'] .= (($j > 0) ? ']' : '');
2849 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2850 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2851 $data['ip'] = '';
2852 $foundremoteip = 0;
2853 $j = 0;
2854 foreach ($tmpips as $tmpip) {
2855 $tmpip = trim($tmpip);
2856 if (strtolower($tmpip) == strtolower($remoteip)) {
2857 $foundremoteip = 1;
2858 }
2859 if (empty($data['ip'])) {
2860 $data['ip'] = $tmpip;
2861 } else {
2862 $j++;
2863 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2864 }
2865 }
2866 if (!$foundremoteip) {
2867 $j++;
2868 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2869 }
2870 $data['ip'] .= (($j > 0) ? ']' : '');
2871 }
2872 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2873 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2874 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2875 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2876 // This is when PHP session is ran outside a web server, like from Windows command line (Not always defined, but useful if OS defines it).
2877 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2878 } else {
2879 $data['ip'] = '???';
2880 }
2881
2882 if (!empty($_SERVER['USERNAME'])) {
2883 // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2884 $data['osuser'] = (string) $_SERVER['USERNAME'];
2885 } elseif (!empty($_SERVER['LOGNAME'])) {
2886 // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2887 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2888 }
2889
2890 // Loop on each log handler and send output
2891 foreach ($conf->loghandlers as $loghandlerinstance) {
2892 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2893 continue;
2894 }
2895 $loghandlerinstance->export($data, $suffixinfilename);
2896 }
2897 unset($data);
2898 }
2899
2900 if ($ident > 0) {
2901 foreach ($conf->loghandlers as $loghandlerinstance) {
2902 $loghandlerinstance->setIdent($ident);
2903 }
2904 }
2905}
2906
2918function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2919{
2920 global $langs, $db;
2921
2922 $form = new Form($db);
2923
2924 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2925 if (empty($templatenameforexport)) {
2926 $templatenameforexport = 'website_' . $website->ref;
2927 }
2928
2929 $out = '';
2930 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="' . dol_escape_htmltag($buttonstring) . '"/>';
2931
2932 // for generate popup
2933 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2934 $out .= 'jQuery(document).ready(function () {';
2935 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2936 $out .= ' var dialogHtml = \'';
2937
2938 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2939 $dialogcontent .= ' <div style="margin-top: 20px;">';
2940 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>' . $langs->trans("ExportSiteLabel") . '...</label><br>';
2941 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2942 $dialogcontent .= ' </div>';
2943 $dialogcontent .= ' <br>';
2944 $dialogcontent .= ' <div style="margin-top: 20px;">';
2945 $dialogcontent .= ' <strong>' . $langs->trans("ExportSiteGitLabel") . ' ' . $form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '') . '</strong><br>';
2946 $dialogcontent .= ' <form action="' . dol_escape_htmltag($overwriteGitUrl) . '" method="POST">';
2947 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2948 $dialogcontent .= ' <input type="hidden" name="token" value="' . newToken() . '">';
2949 $dialogcontent .= ' <input type="text" autofocus name="export_path" id="export-path-' . $name . '" placeholder="' . $langs->trans('ExportPath') . '" style="width:400px " value="' . dol_escape_htmltag($templatenameforexport) . '"/><br>';
2950 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2951 $dialogcontent .= ' </form>';
2952 $dialogcontent .= ' </div>';
2953 $dialogcontent .= ' </div>';
2954
2955 $out .= dol_escape_js($dialogcontent);
2956
2957 $out .= '\';';
2958
2959
2960 // Add the content of the dialog to the body of the page
2961 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2962 $out .= ' if ($dialog.length > 0) {
2963 $dialog.remove();
2964 }
2965 jQuery("body").append(dialogHtml);';
2966
2967 // Configuration of popup
2968 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2969 $out .= ' autoOpen: false,';
2970 $out .= ' modal: true,';
2971 $out .= ' height: 290,';
2972 $out .= ' width: "40%",';
2973 $out .= ' title: "' . dol_escape_js($label) . '",';
2974 $out .= ' });';
2975
2976 // Simulate a click on the original "submit" input to export the site.
2977 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2978 $out .= ' console.log("Clic on exportsite.");';
2979 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2980 $out .= ' console.log("element founded:", target.length > 0);';
2981 $out .= ' if (target.length > 0) { target.click(); }';
2982 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2983 $out .= ' });';
2984
2985 // open popup
2986 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2987 $out .= ' return false;';
2988 $out .= ' });';
2989 $out .= '});';
2990 $out .= '</script>';
2991
2992 return $out;
2993}
2994
2995
3012function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
3013{
3014 global $conf;
3015
3016 if (strpos($url, '?') > 0) {
3017 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
3018 } else {
3019 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
3020 }
3021
3022 if (preg_match('/^https/i', $url)) {
3023 $urltoopen = $url;
3024 } else {
3025 $urltoopen = DOL_URL_ROOT . $url;
3026 }
3027
3028 $out = '';
3029
3030 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
3031 $out .= '<!-- a link for button to open url into a dialog popup -->';
3032 $out .= '<a ' . ($accesskey ? ' accesskey="' . $accesskey . '"' : '') . ' class="cursorpointer reposition button_' . $name . ($morecss ? ' ' . $morecss : '') . '"' . $disabled . ' title="' . dol_escape_htmltag($label) . '"';
3033 if (empty($conf->use_javascript_ajax)) {
3034 $out .= ' href="' . $urltoopen . '" target="_blank"';
3035 } elseif ($jsonopen) {
3036 $out .= ' href="#" onclick="' . $jsonopen . '"';
3037 } else {
3038 $out .= ' href="#"';
3039 }
3040 $out .= '>' . $buttonstring . '</a>';
3041
3042 if (!empty($conf->use_javascript_ajax)) {
3043 // Add code to open url using the popup.
3044 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
3045 $out .= '<div id="idfordialog' . $name . '" class="hidden">' . (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '') . '</div>';
3046
3047 $out .= '<!-- Add js code to open dialog popup on dialog -->';
3048 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">
3049 jQuery(document).ready(function () {
3050 jQuery(".button_' . $name . '").click(function () {
3051 console.log(\'Open popup with jQuery(...).dialog() on URL ' . dol_escape_js($urltoopen) . '\');
3052 var $tmpdialog = $(\'#idfordialog' . $name . '\');
3053 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog' . $name . '" style="border: 0px;" src="' . $urltoopen . '" width="100%" height="98%"></iframe>\');
3054 $tmpdialog.dialog({
3055 autoOpen: false,
3056 modal: true,
3057 height: (window.innerHeight - 150),
3058 width: \'80%\',
3059 title: \'' . dol_escape_js($label) . '\',
3060 open: function (event, ui) {
3061 console.log("open popup name=' . $name . '");
3062 },
3063 close: function (event, ui) {
3064 console.log("Popup is closed, run jsonclose = ' . $jsonclose . '");
3065 ' . (empty($jsonclose) ? '' : $jsonclose . ';') . '
3066 }
3067 });
3068
3069 $tmpdialog.dialog(\'open\');
3070 return false;
3071 });
3072 });
3073 </script>';
3074 }
3075 return $out;
3076}
3077
3094function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
3095{
3096 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
3097}
3098
3116function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
3117{
3118 global $conf, $langs, $hookmanager;
3119
3120 // Show title
3121 $showtitle = 1;
3122 if (!empty($conf->dol_optimize_smallscreen)) {
3123 $showtitle = 0;
3124 }
3125
3126 $out = "\n" . '<!-- dol_fiche_head - dol_get_fiche_head -->';
3127
3128 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3129 $out .= '<div class="tabs' . ($picto ? '' : ' nopaddingleft') . '" data-role="controlgroup" data-type="horizontal">' . "\n";
3130 }
3131
3132 // Show right part
3133 if ($morehtmlright) {
3134 $out .= '<div class="inline-block floatright tabsElem">' . $morehtmlright . '</div>'; // Output right area first so when space is missing, text is in front of tabs and not under.
3135 }
3136
3137 // Show tabs
3138
3139 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
3140 $maxkey = -1;
3141 if (is_array($links) && !empty($links)) {
3142 $keys = array_keys($links);
3143 if (count($keys)) {
3144 $maxkey = max($keys);
3145 }
3146 }
3147
3148 // Show tabs
3149 // if =0 we don't use the feature
3150 if (empty($limittoshow)) {
3151 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
3152 }
3153 if (!empty($conf->dol_optimize_smallscreen)) {
3154 $limittoshow = 2;
3155 }
3156
3157 $displaytab = 0;
3158 $nbintab = 0;
3159 $popuptab = 0;
3160 $outmore = '';
3161 for ($i = 0; $i <= $maxkey; $i++) {
3162 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3163 // If active tab is already present
3164 if ($i >= $limittoshow) {
3165 $limittoshow--;
3166 }
3167 }
3168 }
3169
3170 for ($i = 0; $i <= $maxkey; $i++) {
3171 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3172 $isactive = true;
3173 } else {
3174 $isactive = false;
3175 }
3176
3177 if ($i < $limittoshow || $isactive) {
3178 // Output entry with a visible tab
3179 $out .= '<div class="inline-block tabsElem' . ($isactive ? ' tabsElemActive' : '') . ((!$isactive && getDolGlobalString('MAIN_HIDE_INACTIVETAB_ON_PRINT')) ? ' hideonprint' : '') . '"><!-- id tab = ' . (empty($links[$i][2]) ? '' : dol_escape_htmltag($links[$i][2])) . ' -->';
3180
3181 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3182 if (!empty($links[$i][0])) {
3183 $out .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3184 } else {
3185 $out .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3186 }
3187 } elseif (!empty($links[$i][1])) {
3188 //print "x $i $active ".$links[$i][2]." z";
3189 $out .= '<div class="tab tab' . ($isactive ? 'active' : 'unactive') . '" style="margin: 0 !important">';
3190
3191 if (!empty($links[$i][0])) {
3192 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
3193 $out .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="tab inline-block valignmiddle' . ($morecss ? ' ' . $morecss : '') . (!empty($links[$i][5]) ? ' ' . $links[$i][5] : '') . '" href="' . $links[$i][0] . '" title="' . dol_escape_htmltag($titletoshow) . '">';
3194 }
3195
3196 if ($displaytab == 0 && $picto) {
3197 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
3198 }
3199
3200 $out .= $links[$i][1];
3201 if (!empty($links[$i][0])) {
3202 $out .= '</a>' . "\n";
3203 }
3204 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
3205 $out .= '</div>';
3206 }
3207
3208 $out .= '</div>';
3209 } else {
3210 // Add entry into the combo popup with the other tabs
3211 if (!$popuptab) {
3212 $popuptab = 1;
3213 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
3214 }
3215 $outmore_content = '';
3216
3217 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3218 if (!empty($links[$i][0])) {
3219 $outmore_content .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3220 } else {
3221 $outmore_content .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3222 }
3223 } elseif (!empty($links[$i][1])) {
3224 $outmore_content .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="wordwrap inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
3225 $outmore_content .= preg_replace('/([a-z])\|([a-z])/i', '\\1 | \\2', $links[$i][1]); // Replace x|y with x | y to allow wrap on long composed texts.
3226 $outmore_content .= '</a>' . "\n";
3227 }
3228 if ($outmore_content !== '') {
3229 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
3230 }
3231
3232 $nbintab++;
3233 }
3234
3235 $displaytab = $i + 1;
3236 }
3237 if ($popuptab) {
3238 $outmore .= '</div>';
3239 }
3240
3241 if ($popuptab) { // If there is some tabs not shown
3242 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3243 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3244 $widthofpopup = 240;
3245
3246 $tabsname = $moretabssuffix;
3247 if (empty($tabsname)) {
3248 $tabsname = str_replace("@", "", $picto);
3249 }
3250 $out .= '<div id="moretabs' . $tabsname . '" class="inline-block tabsElem valignmiddle">';
3251 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3252 $out .= '<div class="tab valignmiddle"><a href="#" class="tab moretab inline-block tabunactive valignmiddle"><span class="hideonsmartphone">' . $langs->trans("More") . '</span>... (' . $nbintab . ')</a></div>'; // Do not use "reposition" class in the "More".
3253 }
3254 $out .= '<div id="moretabsList' . $tabsname . '" style="width: ' . $widthofpopup . 'px; position: absolute; ' . $left . ': -999em; text-align: ' . $left . '; margin:0px; padding:2px; z-index:10;">';
3255 $out .= $outmore;
3256 $out .= '</div>';
3257 $out .= '<div></div>';
3258 $out .= "</div>\n";
3259
3260 $out .= '<script nonce="' . getNonce() . '">';
3261 $out .= "$('#moretabs" . $tabsname . "').mouseenter( function() {
3262 var x = this.offsetLeft, y = this.offsetTop;
3263 console.log('mouseenter " . $left . " x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3264 if ((window.innerWidth - x) < " . ($widthofpopup + 10) . ") {
3265 $('#moretabsList" . $tabsname . "').css('" . $right . "','8px');
3266 }
3267 $('#moretabsList" . $tabsname . "').css('" . $left . "','auto');
3268 });
3269 ";
3270 $out .= "$('#moretabs" . $tabsname . "').mouseleave( function() { console.log('mouseleave " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','-999em');});";
3271 $out .= "</script>";
3272 }
3273
3274 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3275 $out .= "</div>\n";
3276 }
3277
3278 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3279 $out .= "\n" . '<div id="dragDropAreaTabBar" class="tabBar' . ($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '') . ($notab == -4 ? '' : ' tabBarWithBottom'))));
3280 $out .= ($morecssdiv ? ' ' . $morecssdiv : '');
3281 $out .= '">' . "\n";
3282 }
3283 if (!empty($dragdropfile)) {
3284 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3285 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3286 }
3287 $parameters = array('tabname' => $active, 'out' => $out);
3288 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3289 if ($reshook > 0) {
3290 $out = $hookmanager->resPrint;
3291 }
3292
3293 return $out;
3294}
3295
3303function dol_fiche_end($notab = 0)
3304{
3305 print dol_get_fiche_end($notab);
3306}
3307
3314function dol_get_fiche_end($notab = 0)
3315{
3316 if (!$notab || $notab == -1) {
3317 return "\n</div>\n";
3318 } else {
3319 return '';
3320 }
3321}
3322
3342function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3343{
3344 global $conf, $form, $user, $langs, $hookmanager, $action;
3345
3346 $error = 0;
3347
3348 $maxvisiblephotos = 1;
3349 $showimage = 1;
3350 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3351 // @phan-suppress-next-line PhanUndeclaredMethod
3352 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3353 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3354 $showbarcode = 0;
3355 }
3356 $modulepart = 'unknown';
3357
3358 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3359 $modulepart = $object->element;
3360 } elseif ($object->element == 'member') {
3361 $modulepart = 'memberphoto';
3362 } elseif ($object->element == 'user') {
3363 $modulepart = 'userphoto';
3364 }
3365
3366 if (class_exists("Imagick")) {
3367 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3368 $modulepart = $object->element;
3369 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3370 $modulepart = 'ficheinter';
3371 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3372 $modulepart = 'contract';
3373 } elseif ($object->element == 'order_supplier') {
3374 $modulepart = 'supplier_order';
3375 } elseif ($object->element == 'invoice_supplier') {
3376 $modulepart = 'supplier_invoice';
3377 }
3378 }
3379
3380 if ($object->element == 'product') {
3382 '@phan-var-force Product $object';
3383 $width = 80;
3384 $cssclass = 'photowithmargin photoref';
3385 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3386 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3387 if ($conf->browser->layout == 'phone') {
3388 $maxvisiblephotos = 1;
3389 }
3390 if ($showimage) {
3391 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('product', $conf->product->multidir_output[$entity], 1, $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '') . '</div>';
3392 } else {
3393 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3394 $nophoto = '';
3395 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3396 } else { // Show no photo link
3397 $nophoto = '/public/theme/common/nophoto.png';
3398 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" title="' . dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))) . '" alt="No photo"' . ($width ? ' style="width: ' . $width . 'px"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '"></div>';
3399 }
3400 }
3401 } elseif ($object->element == 'category') {
3403 '@phan-var-force Categorie $object';
3404 $width = 80;
3405 $cssclass = 'photowithmargin photoref';
3406 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3407 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3408 if ($conf->browser->layout == 'phone') {
3409 $maxvisiblephotos = 1;
3410 }
3411 if ($showimage) {
3412 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('category', $conf->categorie->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '') . '</div>';
3413 } else {
3414 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3415 $nophoto = '';
3416 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3417 } else { // Show no photo link
3418 $nophoto = '/public/theme/common/nophoto.png';
3419 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" title="' . dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))) . '" alt="No photo"' . ($width ? ' style="width: ' . $width . 'px"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '"></div>';
3420 }
3421 }
3422 } elseif ($object->element == 'bom') {
3424 '@phan-var-force Bom $object';
3425 $width = 80;
3426 $cssclass = 'photowithmargin photoref';
3427 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3428 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3429 if ($conf->browser->layout == 'phone') {
3430 $maxvisiblephotos = 1;
3431 }
3432 if ($showimage) {
3433 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('bom', $conf->bom->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '') . '</div>';
3434 } else {
3435 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3436 $nophoto = '';
3437 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3438 } else { // Show no photo link
3439 $nophoto = '/public/theme/common/nophoto.png';
3440 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" title="' . dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))) . '" alt="No photo"' . ($width ? ' style="width: ' . $width . 'px"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '"></div>';
3441 }
3442 }
3443 } elseif ($object->element == 'ticket') {
3444 $width = 80;
3445 $cssclass = 'photoref';
3447 '@phan-var-force Ticket $object';
3448 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity] . '/' . $object->ref);
3449 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3450 if ($conf->browser->layout == 'phone') {
3451 $maxvisiblephotos = 1;
3452 }
3453
3454 if ($showimage) {
3455 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3456 if ($object->nbphoto > 0) {
3457 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $showphoto . '</div>';
3458 } else {
3459 $showimage = 0;
3460 }
3461 }
3462 if (!$showimage) {
3463 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3464 $nophoto = '';
3465 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3466 } else { // Show no photo link
3467 $nophoto = img_picto('No photo', 'object_ticket');
3468 $morehtmlleft .= '<!-- No photo to show -->';
3469 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3470 $morehtmlleft .= $nophoto;
3471 $morehtmlleft .= '</div></div>';
3472 }
3473 }
3474 } else {
3475 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3476 $phototoshow = '';
3477 // Check if a preview file is available
3478 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3479 $objectref = dol_sanitizeFileName($object->ref);
3480 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity]) . "/";
3481 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3482 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3483 $subdir .= ((!empty($subdir) && !preg_match('/\/$/', $subdir)) ? '/' : '') . $objectref; // the objectref dir is not included into get_exdir when used with level=2, so we add it at end
3484 } else {
3485 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3486 }
3487 if (empty($subdir)) {
3488 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3489 }
3490
3491 $filepath = $dir_output . $subdir . "/";
3492
3493 $filepdf = $filepath . $objectref . ".pdf";
3494 $relativepath = $subdir . '/' . $objectref . '.pdf';
3495
3496 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3497 $fileimage = $filepdf . '_preview.png';
3498 $relativepathimage = $relativepath . '_preview.png';
3499
3500 $pdfexists = file_exists($filepdf);
3501
3502 // If PDF file exists
3503 if ($pdfexists) {
3504 // Conversion du PDF en image png si fichier png non existent
3505 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3506 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3507 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3508 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3509 if ($ret < 0) {
3510 $error++;
3511 }
3512 }
3513 }
3514 }
3515
3516 if ($pdfexists && !$error) {
3517 $heightforphotref = 80;
3518 if (!empty($conf->dol_optimize_smallscreen)) {
3519 $heightforphotref = 60;
3520 }
3521 // If the preview file is found
3522 if (file_exists($fileimage)) {
3523 $phototoshow = '<div class="photoref">';
3524 $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithborder" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($relativepathimage) . '">';
3525 $phototoshow .= '</div>';
3526 }
3527 }
3528 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3529 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3530 }
3531
3532 if ($phototoshow) {
3533 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3534 $morehtmlleft .= $phototoshow;
3535 $morehtmlleft .= '</div>';
3536 }
3537 }
3538
3539 if (empty($phototoshow)) { // Show No photo link (picto of object)
3540 if ($object->element == 'action') {
3541 $width = 80;
3542 $cssclass = 'photorefcenter';
3543 $nophoto = img_picto('No photo', 'title_agenda');
3544 } else {
3545 $width = 14;
3546 $cssclass = 'photorefcenter';
3547 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3548 $prefix = 'object_';
3549 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3550 $picto = 'project'; // instead of projectpub
3551 }
3552 if (strpos($picto, 'fontawesome_') !== false) {
3553 $prefix = '';
3554 }
3555 $nophoto = img_picto('No photo', $prefix . $picto);
3556 }
3557 $morehtmlleft .= '<!-- No photo to show -->';
3558 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3559 $morehtmlleft .= $nophoto;
3560 $morehtmlleft .= '</div></div>';
3561 }
3562 }
3563
3564 // Show barcode
3565 if ($showbarcode) {
3566 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $form->showbarcode($object, 100, 'photoref valignmiddle') . '</div>';
3567 }
3568
3569 if ($object->element == 'societe') {
3571 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3572 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3573 } else {
3574 $morehtmlstatus .= $object->getLibStatut(6);
3575 }
3576 } elseif ($object->element == 'product') {
3578 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3579 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3580 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3581 } else {
3582 $morehtmlstatus .= '<span class="statusrefsell">' . $object->getLibStatut(6, 0) . '</span>';
3583 }
3584 $morehtmlstatus .= ' &nbsp; ';
3585 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3586 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3587 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'status_buy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3588 } else {
3589 $morehtmlstatus .= '<span class="statusrefbuy">' . $object->getLibStatut(6, 1) . '</span>';
3590 }
3591 } elseif (in_array($object->element, array('salary'))) {
3593 '@phan-var-force Salary $object';
3594 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3595 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3596 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3597 }
3598 $morehtmlstatus .= $tmptxt;
3599 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) {
3601 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3602 if (!isset($object->alreadypaid)) {
3603 $object->totalpaid = $object->getSommePaiement(0);
3604 $object->totalcreditnotes = $object->getSumCreditNotesUsed(0);
3605 $object->totaldeposits = $object->getSumDepositsUsed(0);
3606 $object->alreadypaid = $object->totalpaid + $object->totalcreditnotes + $object->totaldeposits;
3607 }
3608 $tmptxt = $object->getLibStatut(6, (float) $object->alreadypaid);
3609 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3610 $tmptxt = $object->getLibStatut(5, (float) $object->alreadypaid);
3611 }
3612 $morehtmlstatus .= $tmptxt;
3613 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid like for invoices
3615 '@phan-var-force ChargeSociales|Loan|Tva $object';
3616 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3617 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3618 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3619 }
3620 $morehtmlstatus .= $tmptxt;
3621 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3623 if ($object->status == 0) {
3624 $morehtmlstatus .= $object->getLibStatut(5);
3625 } else {
3626 $morehtmlstatus .= $object->getLibStatut(4);
3627 }
3628 } elseif ($object->element == 'facturerec') {
3630 '@phan-var-force FactureRec $object';
3631 if ($object->frequency == 0) {
3632 $morehtmlstatus .= $object->getLibStatut(2);
3633 } else {
3634 $morehtmlstatus .= $object->getLibStatut(5);
3635 }
3636 } elseif ($object->element == 'project_task') {
3638 $tmptxt = $object->getLibStatut(4);
3639 $morehtmlstatus .= $tmptxt;
3640 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3641 $tmptxt = $object->getLibStatut(6);
3642 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3643 $tmptxt = $object->getLibStatut(5);
3644 }
3645 $morehtmlstatus .= $tmptxt;
3646 }
3647
3648 // Say if object was dispatched/transferred "into accountancy"
3649 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3650 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3651 if (method_exists($object, 'getVentilExportCompta')) {
3652 $accounted = $object->getVentilExportCompta(1);
3653 $langs->load("accountancy");
3654 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">' . ($accounted > 0 ? '<a href="' . DOL_URL_ROOT . '/accountancy/bookkeeping/list.php?search_mvt_num=' . ((int) $accounted) . '">' . $langs->trans("Accounted") . '</a>' : $langs->trans("NotYetAccounted")) . '</span>';
3655 }
3656 }
3657
3658 // Add alias for thirdparty
3659 if (!empty($object->name_alias)) {
3661 '@phan-var-force Societe $object';
3662 $morehtmlref .= '<div class="refidno opacitymedium">' . dol_escape_htmltag($object->name_alias) . '</div>';
3663 }
3664
3665 // Add label
3666 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3668 if (!empty($object->label)) {
3669 $morehtmlref .= '<div class="refidno banner-object-label">' . $object->label . '</div>';
3670 }
3671 }
3672 // Show address and email
3673 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3674 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3675 if ($moreaddress) {
3676 $morehtmlref .= '<div class="refidno refaddress">';
3677 $morehtmlref .= $moreaddress;
3678 $morehtmlref .= '</div>';
3679 }
3680 }
3681 if (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') == '1' || preg_match('/' . preg_quote($object->element, '/') . '/i', getDolGlobalString('MAIN_SHOW_TECHNICAL_ID'))) && !empty($object->id)) {
3682 $morehtmlref .= '<div style="clear: both;"></div>';
3683 $morehtmlref .= '<div class="refidno opacitymedium">';
3684 $morehtmlref .= $langs->trans("TechnicalID") . ': ' . ((int) $object->id);
3685 $morehtmlref .= '</div>';
3686 }
3687
3688 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3689 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3690 if ($reshook < 0) {
3691 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3692 } elseif (empty($reshook)) {
3693 $morehtmlref .= $hookmanager->resPrint;
3694 } elseif ($reshook > 0) {
3695 $morehtmlref = $hookmanager->resPrint;
3696 }
3697
3698 // $morehtml is the right part (link "Back to list")
3699 // $morehtmlref is the part after the ref
3700 // $morehtmlleft is the picto or photo of banner
3701 // $morehtmlstatus is part under the status
3702 // $morehtmlright is part of htmlright
3703
3704 print '<div class="' . ($onlybanner ? 'arearefnobottom ' : 'arearef ') . 'heightref valignmiddle centpercent object-banner-tab-container" data-module-part="'.dolPrintHTMLForAttribute($modulepart).'">';
3705 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3706 print '</div>';
3707 print '<div class="underrefbanner clearboth"></div>';
3708}
3709
3719function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3720{
3721 global $langs;
3722 $ret = '';
3723 if ($fieldrequired) {
3724 $ret .= '<span class="fieldrequired">';
3725 }
3726 $ret .= '<label for="' . $fieldkey . '">';
3727 $ret .= $langs->trans($langkey);
3728 $ret .= '</label>';
3729 if ($fieldrequired) {
3730 $ret .= '</span>';
3731 }
3732 return $ret;
3733}
3734
3748function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3749{
3750 global $langs, $hookmanager;
3751
3752 $ret = '';
3753 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3754
3755 // See format of addresses on https://en.wikipedia.org/wiki/Address
3756 // Address
3757 if (empty($mode)) {
3758 $ret .= (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3759 }
3760 // Zip/Town/State
3761 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3762 // US: title firstname name \n address lines \n town, state, zip \n country
3763 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3764 $ret .= (($ret && $town) ? $sep : '') . $town;
3765
3766 if (!empty($object->state)) {
3767 $ret .= ($ret ? ($town ? ", " : $sep) : '') . $object->state;
3768 }
3769 if (!empty($object->zip)) {
3770 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '') . $object->zip;
3771 }
3772 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3773 // UK: title firstname name \n address lines \n town state \n zip \n country
3774 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3775 $ret .= ($ret ? $sep : '') . $town;
3776 if (!empty($object->state)) {
3777 $ret .= ($ret ? ", " : '') . $object->state;
3778 }
3779 if (!empty($object->zip)) {
3780 $ret .= ($ret ? $sep : '') . $object->zip;
3781 }
3782 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3783 // ES: title firstname name \n address lines \n zip town \n state \n country
3784 $ret .= ($ret ? $sep : '') . $object->zip;
3785 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3786 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3787 if (!empty($object->state)) {
3788 $ret .= $sep . $object->state;
3789 }
3790 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3791 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3792 // See https://www.sljfaq.org/afaq/addresses.html
3793 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3794 $ret .= ($ret ? $sep : '') . ($object->state ? $object->state . ', ' : '') . $town . ($object->zip ? ' ' : '') . $object->zip;
3795 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3796 // IT: title firstname name\n address lines \n zip town state_code \n country
3797 $ret .= ($ret ? $sep : '') . $object->zip;
3798 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3799 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3800 $ret .= (empty($object->state_code) ? '' : (' ' . $object->state_code));
3801 } else {
3802 // Other: title firstname name \n address lines \n zip town[, state] \n country
3803 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3804 $ret .= !empty($object->zip) ? (($ret ? $sep : '') . $object->zip) : '';
3805 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')) . $town) : '');
3806 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3807 $ret .= ($ret ? ", " : '') . $object->state;
3808 }
3809 }
3810
3811 if (!is_object($outputlangs)) {
3812 $outputlangs = $langs;
3813 }
3814 if ($withcountry) {
3815 $langs->load("dict");
3816 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '') . $outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country" . $object->country_code)));
3817 }
3818 if ($hookmanager) {
3819 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs, 'mode' => $mode, 'extralangcode' => $extralangcode);
3820 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3821 if ($reshook > 0) {
3822 $ret = '';
3823 }
3824 $ret .= $hookmanager->resPrint;
3825 }
3826
3827 return $ret;
3828}
3829
3830
3831
3841function dol_strftime($fmt, $ts = false, $is_gmt = false)
3842{
3843 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3844 return dol_print_date($ts, $fmt, $is_gmt);
3845 } else {
3846 return 'Error date outside supported range';
3847 }
3848}
3849
3872function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false, $decorate = 0)
3873{
3874 global $conf, $langs;
3875
3876 // If date undefined or "", we return ""
3877 if (dol_strlen((string) $time) == 0) {
3878 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3879 }
3880
3881 if ($tzoutput === 'auto') {
3882 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3883 }
3884
3885 // Clean parameters
3886 $to_gmt = false; // false if we want date in server timezone, true if we want to add offset
3887 $offsettz = $offsetdst = 0;
3888 if ($tzoutput) {
3889 $to_gmt = true; // For backward compatibility
3890 if (is_string($tzoutput)) {
3891 if ($tzoutput == 'tzserver') {
3892 $to_gmt = false;
3893 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3894 // @phan-suppress-next-line PhanPluginRedundantAssignment
3895 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3896 // @phan-suppress-next-line PhanPluginRedundantAssignment
3897 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3898 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3899 $to_gmt = true;
3900 // if no session (by example in cron) may use MAIN_DOLIBARR_USER_TIMEZONE instead UTC
3901 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? getDolGlobalString('MAIN_DOLIBARR_USER_TIMEZONE', 'UTC') : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3902
3903 if (class_exists('DateTimeZone')) {
3904 try {
3905 $user_date_tz = new DateTimeZone($offsettzstring);
3906 } catch (Exception $e) {
3907 // Bad value for $offsettzstring
3908 dol_syslog("DateInvalidTimeZoneException for timezone string '".$offsettzstring."'. Falling back to UTC.", LOG_ERR);
3909 $user_date_tz = new DateTimeZone('UTC'); // Force valid timezone as UTC
3910 }
3911 $user_dt = new DateTime();
3912 $user_dt->setTimezone($user_date_tz);
3913 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3914 $offsettz = $user_dt->getOffset(); // should include dst ?
3915 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3916 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3917 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3918 }
3919 }
3920 }
3921 }
3922 if (!is_object($outputlangs)) {
3923 $outputlangs = $langs;
3924 }
3925 if (!$format) {
3926 $format = 'daytextshort';
3927 }
3928
3929 // Do we have to reduce the length of date (year on 2 chars) to save space.
3930 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3931 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3932 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3933 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3934 if ($formatwithoutreduce != $format) {
3935 $format = $formatwithoutreduce;
3936 $reduceformat = 1;
3937 } // so format 'dayreduceformat' is processed like day
3938
3939 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3940 // TODO Add format daysmallyear and dayhoursmallyear
3941 if ($format == 'day') {
3942 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3943 } elseif ($format == 'hour') {
3944 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3945 } elseif ($format == 'hourduration') {
3946 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3947 } elseif ($format == 'daytext') {
3948 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3949 } elseif ($format == 'daytextshort') {
3950 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3951 } elseif ($format == 'dayhour') {
3952 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3953 } elseif ($format == 'dayhoursec') {
3954 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3955 } elseif ($format == 'dayhourtext') {
3956 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3957 } elseif ($format == 'dayhourtextshort') {
3958 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3959 } elseif ($format == 'dayhourlog') {
3960 // Format not sensitive to language
3961 $format = '%Y%m%d%H%M%S';
3962 } elseif ($format == 'dayhourlogsmall') {
3963 // Format not sensitive to language
3964 $format = '%y%m%d%H%M';
3965 } elseif ($format == 'dayhourldap') {
3966 $format = '%Y%m%d%H%M%SZ';
3967 } elseif ($format == 'dayhourxcard') {
3968 $format = '%Y%m%dT%H%M%SZ';
3969 } elseif ($format == 'dayxcard') {
3970 $format = '%Y%m%d';
3971 } elseif ($format == 'dayrfc') {
3972 $format = '%Y-%m-%d'; // DATE_RFC3339
3973 } elseif ($format == 'dayhourrfc') {
3974 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3975 } elseif ($format == 'standard') {
3976 $format = '%Y-%m-%d %H:%M:%S';
3977 }
3978
3979 if ($reduceformat) {
3980 $format = str_replace('%Y', '%y', $format);
3981 $format = str_replace('yyyy', 'yy', $format);
3982 }
3983
3984 // Clean format
3985 if (preg_match('/%b/i', $format)) { // There is some text to translate
3986 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3987 $format = str_replace('%b', '__b__', $format);
3988 $format = str_replace('%B', '__B__', $format);
3989 }
3990 if (preg_match('/%a/i', $format)) { // There is some text to translate
3991 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3992 $format = str_replace('%a', '__a__', $format);
3993 $format = str_replace('%A', '__A__', $format);
3994 }
3995
3996 // Analyze date
3997 $reg = array();
3998 if (preg_match('/^([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])$/i', (string) $time, $reg)) { // Deprecated. Ex: 1970-01-01, 1970-01-01 01:00:00, 19700101010000
3999 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString());
4000 return '';
4001 } elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', (string) $time, $reg)) { // Still available to solve problems in extrafields of type date
4002 // This part of code should not be used anymore.
4003 dol_syslog("Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString(), LOG_WARNING);
4004 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
4005 $syear = (!empty($reg[1]) ? $reg[1] : '');
4006 $smonth = (!empty($reg[2]) ? $reg[2] : '');
4007 $sday = (!empty($reg[3]) ? $reg[3] : '');
4008 $shour = (!empty($reg[4]) ? $reg[4] : '');
4009 $smin = (!empty($reg[5]) ? $reg[5] : '');
4010 $ssec = (!empty($reg[6]) ? $reg[6] : '');
4011
4012 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
4013
4014 if ($to_gmt) {
4015 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4016 } else {
4017 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4018 }
4019 $dtts = new DateTime();
4020 $dtts->setTimestamp($time);
4021 $dtts->setTimezone($tzo);
4022 $newformat = str_replace(
4023 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4024 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4025 $format
4026 );
4027 $ret = $dtts->format($newformat);
4028 $ret = str_replace(
4029 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4030 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4031 $ret
4032 );
4033 } else {
4034 // Date is a timestamps
4035 if ($time < 100000000000) { // Protection against bad date values
4036 $dtts = new DateTime();
4037 //var_dump($tzoutput.' '.$offsettzstring.' '.$offsettz.$offsetdst.' x '.$to_gmt);
4038 if ($to_gmt) { // to_gmt means "not in php server timezone" (if tzoutput = 'gmt', offsets should be 0 but if = 'tzuser...', offsets may be defined
4039 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4040
4041 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4042 $dtts->setTimezone($tzo); // important: must be before the setTimestamp
4043 $dtts->setTimestamp($timetouse);
4044 } else {
4045 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4046
4047 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4048 $dtts->setTimestamp($timetouse); // TODO May be we can invert setTimestamp and setTimezone
4049 $dtts->setTimezone($tzo);
4050 }
4051
4052 $newformat = str_replace(
4053 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4054 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4055 $format
4056 );
4057
4058 $ret = $dtts->format($newformat);
4059 //var_dump($timetouse, $offsettz, $offsetdst, $tzo, $newformat, $ret);
4060 $ret = str_replace(
4061 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4062 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4063 $ret
4064 );
4065 } else {
4066 $ret = 'Bad value ' . $time . ' for date';
4067 }
4068 }
4069
4070 if (preg_match('/__b__/i', $format)) {
4071 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4072
4073 if ($to_gmt) {
4074 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4075 } else {
4076 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4077 }
4078 $dtts = new DateTime();
4079 $dtts->setTimestamp($timetouse);
4080 $dtts->setTimezone($tzo);
4081 $month = (int) $dtts->format("m");
4082 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
4083 if ($encodetooutput) {
4084 $monthtext = $outputlangs->transnoentities('Month' . $month);
4085 $monthtextshort = $outputlangs->transnoentities('MonthShort' . $month);
4086 } else {
4087 $monthtext = $outputlangs->transnoentitiesnoconv('Month' . $month);
4088 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort' . $month);
4089 }
4090 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
4091 $ret = str_replace('__b__', $monthtextshort, $ret);
4092 $ret = str_replace('__B__', $monthtext, $ret);
4093 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
4094 //return $ret;
4095 }
4096 if (preg_match('/__a__/i', $format)) {
4097 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
4098 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
4099
4100 if ($to_gmt) {
4101 $tzo = new DateTimeZone('UTC');
4102 } else {
4103 $tzo = new DateTimeZone(date_default_timezone_get());
4104 }
4105 $dtts = new DateTime();
4106 $dtts->setTimestamp($timetouse);
4107 $dtts->setTimezone($tzo);
4108 $w = $dtts->format("w");
4109 $dayweek = $outputlangs->transnoentitiesnoconv('Day' . $w);
4110
4111 $ret = str_replace('__A__', $dayweek, $ret);
4112 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
4113 }
4114
4115 if ($decorate) {
4116 $ret = preg_replace('/(\d\d:\d\d [AP]M)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4117 $ret = preg_replace('/(\d\d:\d\d)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4118 }
4119
4120 return $ret;
4121}
4122
4123
4144function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
4145{
4146 if ($timestamp === '') {
4147 return array();
4148 }
4149
4150 $datetimeobj = new DateTime();
4151 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
4152 if ($forcetimezone) {
4153 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
4154 }
4155 $arrayinfo = array(
4156 'year' => ((int) date_format($datetimeobj, 'Y')),
4157 'mon' => ((int) date_format($datetimeobj, 'm')),
4158 'mday' => ((int) date_format($datetimeobj, 'd')),
4159 'wday' => ((int) date_format($datetimeobj, 'w')),
4160 'yday' => ((int) date_format($datetimeobj, 'z')),
4161 'hours' => ((int) date_format($datetimeobj, 'H')),
4162 'minutes' => ((int) date_format($datetimeobj, 'i')),
4163 'seconds' => ((int) date_format($datetimeobj, 's')),
4164 '0' => $timestamp
4165 );
4166
4167 return $arrayinfo;
4168}
4169
4191function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
4192{
4193 global $conf;
4194 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
4195
4196 if ($gm === 'auto') {
4197 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
4198 }
4199 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
4200
4201 // Clean parameters
4202 if ($hour == -1 || empty($hour)) {
4203 $hour = 0;
4204 }
4205 if ($minute == -1 || empty($minute)) {
4206 $minute = 0;
4207 }
4208 if ($second == -1 || empty($second)) {
4209 $second = 0;
4210 }
4211
4212 // Check parameters
4213 if ($check) {
4214 if (!$month || !$day) {
4215 return '';
4216 }
4217 if ($day > 31) {
4218 return '';
4219 }
4220 if ($month > 12) {
4221 return '';
4222 }
4223 if ($hour < 0 || $hour > 24) {
4224 return '';
4225 }
4226 if ($minute < 0 || $minute > 60) {
4227 return '';
4228 }
4229 if ($second < 0 || $second > 60) {
4230 return '';
4231 }
4232 }
4233
4234 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
4235 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
4236 $localtz = new DateTimeZone($default_timezone);
4237 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
4238 // We use dol_tz_string first because it is more reliable.
4239 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
4240 try {
4241 $localtz = new DateTimeZone($default_timezone);
4242 } catch (Exception $e) {
4243 dol_syslog("Warning dol_tz_string contains an invalid value " . json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
4244 $default_timezone = @date_default_timezone_get();
4245 }
4246 } elseif (strrpos($gm, "tz,") !== false) {
4247 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
4248 try {
4249 $localtz = new DateTimeZone($timezone);
4250 } catch (Exception $e) {
4251 dol_syslog("Warning passed timezone contains an invalid value " . $timezone, LOG_WARNING);
4252 }
4253 }
4254
4255 if (empty($localtz)) {
4256 $localtz = new DateTimeZone('UTC');
4257 }
4258 $dt = new DateTime('now', $localtz);
4259 $dt->setDate((int) $year, (int) $month, (int) $day);
4260 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4261 $date = $dt->getTimestamp(); // should include daylight saving time
4262
4263 return $date;
4264}
4265
4266
4277function dol_now($mode = 'gmt')
4278{
4279 $ret = 0;
4280
4281 if ($mode === 'auto') {
4282 $mode = 'gmt';
4283 }
4284
4285 if ($mode == 'gmt') {
4286 $ret = time(); // Time for now at greenwich.
4287 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4288 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
4289 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4290 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4291 // } elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4292 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4293 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4294 // $ret=dol_now('gmt')+($tzsecond*3600);
4295 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4296 // Time for now with user timezone added
4297 // print 'time: '.time();
4298 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4299 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4300 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4301 }
4302
4303 return $ret;
4304}
4305
4306
4315function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4316{
4317 global $conf, $langs;
4318 $level = 1024;
4319
4320 if (!empty($conf->dol_optimize_smallscreen)) {
4321 $shortunit = 1;
4322 }
4323
4324 // Set value text
4325 if (empty($shortvalue) || $size < ($level * 10)) {
4326 $ret = $size;
4327 $textunitshort = $langs->trans("b");
4328 $textunitlong = $langs->trans("Bytes");
4329 } else {
4330 $ret = round($size / $level, 0);
4331 $textunitshort = $langs->trans("Kb");
4332 $textunitlong = $langs->trans("KiloBytes");
4333 }
4334 // Use long or short text unit
4335 if (empty($shortunit)) {
4336 $ret .= ' ' . $textunitlong;
4337 } else {
4338 $ret .= ' ' . $textunitshort;
4339 }
4340
4341 return $ret;
4342}
4343
4354function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4355{
4356 global $langs;
4357
4358 if (empty($url)) {
4359 return '';
4360 }
4361
4362 $linkstart = '<a href="';
4363 if (!preg_match('/^http/i', $url)) {
4364 $linkstart .= 'http://';
4365 }
4366 $linkstart .= $url;
4367 $linkstart .= '"';
4368 if ($target) {
4369 $linkstart .= ' target="' . $target . '"';
4370 }
4371 $linkstart .= ' title="' . $langs->trans("URL") . ': ' . $url . '"';
4372 $linkstart .= '>';
4373
4374 $link = '';
4375 if (!preg_match('/^http/i', $url)) {
4376 $link .= 'http://';
4377 }
4378 $link .= dol_trunc($url, $max);
4379
4380 $linkend = '</a>';
4381
4382 if ($morecss == 'float') { // deprecated
4383 return '<div class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '') . $link . '</div>';
4384 } else {
4385 return $linkstart . '<span class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '') . $link . '</span>' . $linkend;
4386 }
4387}
4388
4402function dol_print_email($email, $contactid = 0, $socid = 0, $addlink = 0, $max = 0, $showinvalid = 2, $withpicto = 0, $morecss = 'paddingrightonly')
4403{
4404 global $user, $langs, $hookmanager;
4405
4406 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4407 //$showinvalid = 1; $email = 'rrrrr';
4408
4409 $newemail = dol_escape_htmltag($email);
4410
4411 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4412 $withpicto = 0;
4413 }
4414
4415 if (empty($email)) {
4416 return '&nbsp;';
4417 }
4418
4419 if ($addlink == 1) {
4420 $newemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="';
4421 if (!preg_match('/^mailto:/i', $email)) {
4422 $newemail .= 'mailto:';
4423 }
4424 $newemail .= $email;
4425 $newemail .= '" target="_blank">';
4426
4427 $newemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4428
4429 if ($max > 0) {
4430 $newemail .= dol_trunc($email, $max);
4431 } else {
4432 $newemail .= $email;
4433 }
4434 $newemail .= '</a>';
4435
4436 if ($showinvalid) {
4437 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4438 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4439 $emailonly = CMailFile::getValidAddress($email, 2);
4440 if (!isValidEmail($emailonly)) {
4441 $langs->load("errors");
4442 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $emailonly), '', 'paddingrightonly');
4443 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4444 $langs->load("errors");
4445 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly), '', 'paddingrightonly');
4446 }
4447 }
4448
4449 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4450 $type = 'AC_EMAIL';
4451 $linktoaddaction = '';
4452 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4453 $linktoaddaction = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode=' . urlencode($type) . '&amp;contactid=' . ((int) $contactid) . '&amp;socid=' . ((int) $socid) . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
4454 }
4455 if ($linktoaddaction) {
4456 $newemail = '<div>' . $newemail . ' ' . $linktoaddaction . '</div>';
4457 }
4458 }
4459 } elseif ($addlink === 'thirdparty') {
4460 $tmpnewemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="' . DOL_URL_ROOT . '/societe/card.php?socid=' . $socid . '&action=presend&mode=init#formmailbeforetitle">';
4461 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4462 if ($withpicto == 1) {
4463 $tmpnewemail .= $newemail;
4464 }
4465 $tmpnewemail .= '</a>';
4466
4467 $newemail = $tmpnewemail;
4468 } else {
4469 $newemail = ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '') . $newemail;
4470
4471 if ($showinvalid) {
4472 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4473 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4474 $emailonly = CMailFile::getValidAddress($email, 2);
4475 if (!isValidEmail($emailonly)) {
4476 $langs->load("errors");
4477 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $email));
4478 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4479 $langs->load("errors");
4480 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly));
4481 }
4482 }
4483 }
4484
4485 //$rep = '<div class="nospan" style="margin-right: 10px">';
4486 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4487 //$rep .= '</div>';
4488 $rep = $newemail;
4489
4490 if ($hookmanager) {
4491 $parameters = array('cid' => $contactid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4492
4493 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4494 if ($reshook > 0) {
4495 $rep = '';
4496 }
4497 $rep .= $hookmanager->resPrint;
4498 }
4499
4500 return $rep;
4501}
4502
4508function getArrayOfSocialNetworks()
4509{
4510 global $db;
4511
4512 $socialnetworks = array();
4513 // Enable caching of array
4514 require_once DOL_DOCUMENT_ROOT . '/core/lib/memory.lib.php';
4515 $cachekey = dol_sanitizeKeyCode(str_replace(',', '_', 'socialnetworks_'.getEntity('c_socialnetworks')));
4516 $dataretrieved = dol_getcache($cachekey);
4517
4518 if (!is_null($dataretrieved)) {
4519 $socialnetworks = $dataretrieved;
4520 } else {
4521 $sql = "SELECT rowid, code, label, url, icon, active FROM " . MAIN_DB_PREFIX . "c_socialnetworks";
4522 $sql .= " WHERE entity IN (" . getEntity('c_socialnetworks').")";
4523
4524 $resql = $db->query($sql);
4525 if ($resql) {
4526 while ($obj = $db->fetch_object($resql)) {
4527 $socialnetworks[$obj->code] = array(
4528 'rowid' => $obj->rowid,
4529 'label' => $obj->label,
4530 'url' => $obj->url,
4531 'icon' => $obj->icon,
4532 'active' => $obj->active,
4533 );
4534 }
4535 }
4536 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4537 }
4538
4539 return (is_array($socialnetworks) ? $socialnetworks : array());
4540}
4541
4552function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4553{
4554 global $hookmanager, $langs, $user;
4555
4556 $htmllink = $value;
4557
4558 if (empty($value)) {
4559 return '&nbsp;';
4560 }
4561
4562 if (!empty($type)) {
4563 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4564 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4565 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4566 if ($type == 'skype') {
4567 $htmllink .= dol_escape_htmltag($value);
4568 $htmllink .= '&nbsp; <a href="skype:';
4569 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4570 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4571 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4572 $htmllink .= '</a><a href="skype:';
4573 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4574 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4575 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4576 $htmllink .= '</a>';
4577 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4578 $addlink = 'AC_SKYPE';
4579 $link = '';
4580 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4581 $link = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&backtopage=1&actioncode=' . $addlink . '&contactid=' . $contactid . '&socid=' . $socid . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
4582 }
4583 $htmllink .= ($link ? ' ' . $link : '');
4584 }
4585 } else {
4586 if (!empty($dictsocialnetworks[$type]['url'])) {
4587 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4588 if ($tmpvirginurl) {
4589 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4590 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4591
4592 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4593 if ($tmpvirginurl3) {
4594 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4595 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4596 }
4597
4598 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4599 if ($tmpvirginurl2) {
4600 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4601 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4602 }
4603 }
4604 if (preg_match('/^https?:\/\//i', $value)) {
4605 $link = $value;
4606 } else {
4607 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4608 }
4609 $valuetoshow = $value;
4610 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4611 if (preg_match('/^https?:\/\//i', $link)) {
4612 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4613 } else {
4614 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4615 }
4616 } else {
4617 $htmllink .= dol_escape_htmltag($value);
4618 }
4619 }
4620 $htmllink .= '</div>';
4621 } else {
4622 $langs->load("errors");
4623 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4624 }
4625
4626 if ($hookmanager) {
4627 $parameters = array(
4628 'value' => $value,
4629 'cid' => $contactid,
4630 'socid' => $socid,
4631 'type' => $type,
4632 'dictsocialnetworks' => $dictsocialnetworks,
4633 );
4634
4635 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4636 if ($reshook > 0) {
4637 $htmllink = '';
4638 }
4639 $htmllink .= $hookmanager->resPrint;
4640 }
4641
4642 return $htmllink;
4643}
4644
4654function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4655{
4656 global $mysoc;
4657
4658 if (empty($profID) || empty($profIDtype)) {
4659 return '';
4660 }
4661 if (empty($countrycode)) {
4662 $countrycode = $mysoc->country_code;
4663 }
4664 $newProfID = $profID;
4665 $id = substr($profIDtype, -1);
4666 $ret = '';
4667 if (strtoupper($countrycode) == 'FR') {
4668 // France
4669 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4670
4671 if ($id == 1 && dol_strlen($newProfID) == 9) {
4672 // SIREN (ex: 123 123 123)
4673 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3);
4674 }
4675 if ($id == 2 && dol_strlen($newProfID) == 14) {
4676 // SIRET (ex: 123 123 123 12345)
4677 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3) . ' ' . substr($newProfID, 9, 5);
4678 }
4679 if ($id == 3 && dol_strlen($newProfID) == 5) {
4680 // NAF/APE (ex: 69.20Z)
4681 $newProfID = substr($newProfID, 0, 2) . '.' . substr($newProfID, 2, 3);
4682 }
4683 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4684 // TVA intracommunautaire (ex: FR12 123 123 123)
4685 $newProfID = substr($newProfID, 0, 4) . ' ' . substr($newProfID, 4, 3) . ' ' . substr($newProfID, 7, 3) . ' ' . substr($newProfID, 10, 3);
4686 }
4687 }
4688 if (!empty($addcpButton)) {
4689 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4690 } else {
4691 $ret = $newProfID;
4692 }
4693 return $ret;
4694}
4695
4711function dol_print_phone($phone, $countrycode = '', $contactid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4712{
4713 global $conf, $user, $langs, $mysoc, $hookmanager;
4714
4715 // Clean phone parameter
4716 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4717 if (empty($phone)) {
4718 return '';
4719 }
4720 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4721 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4722 }
4723 if (empty($countrycode) && is_object($mysoc)) {
4724 $countrycode = $mysoc->country_code;
4725 }
4726
4727 // Short format for small screens
4728 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4729 $separ = '';
4730 }
4731
4732 $newphone = $phone;
4733 $newphonewa = $phone;
4734 if (strtoupper($countrycode) == "FR") {
4735 // France
4736 if (dol_strlen($phone) == 10) {
4737 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 2) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2);
4738 } elseif (dol_strlen($phone) == 7) {
4739 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2);
4740 } elseif (dol_strlen($phone) == 9) {
4741 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2);
4742 } elseif (dol_strlen($phone) == 11) {
4743 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4744 } elseif (dol_strlen($phone) == 12) {
4745 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4746 } elseif (dol_strlen($phone) == 13) {
4747 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3) . $separ . substr($newphone, 11, 2);
4748 }
4749 } elseif (strtoupper($countrycode) == "CA") {
4750 if (dol_strlen($phone) == 10) {
4751 $newphone = ($separ != '' ? '(' : '') . substr($newphone, 0, 3) . ($separ != '' ? ')' : '') . $separ . substr($newphone, 3, 3) . ($separ != '' ? '-' : '') . substr($newphone, 6, 4);
4752 }
4753 } elseif (strtoupper($countrycode) == "PT") { //Portugal
4754 if (dol_strlen($phone) == 13) { //ex: +351_ABC_DEF_GHI
4755 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4756 }
4757 } elseif (strtoupper($countrycode) == "SR") { //Suriname
4758 if (dol_strlen($phone) == 10) { //ex: +597_ABC_DEF
4759 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3);
4760 } elseif (dol_strlen($phone) == 11) { //ex: +597_ABC_DEFG
4761 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
4762 }
4763 } elseif (strtoupper($countrycode) == "DE") { //Allemagne
4764 if (dol_strlen($phone) == 14) { //ex: +49_ABCD_EFGH_IJK
4765 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 4) . $separ . substr($newphone, 11, 3);
4766 } elseif (dol_strlen($phone) == 13) { //ex: +49_ABC_DEFG_HIJ
4767 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 4) . $separ . substr($newphone, 10, 3);
4768 }
4769 } elseif (strtoupper($countrycode) == "ES") { //Espagne
4770 if (dol_strlen($phone) == 12) { //ex: +34_ABC_DEF_GHI
4771 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4772 }
4773 } elseif (strtoupper($countrycode) == "BF") { // Burkina Faso
4774 if (dol_strlen($phone) == 12) { //ex : +22 A BC_DE_FG_HI
4775 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4776 }
4777 } elseif (strtoupper($countrycode) == "RO") { // Roumanie
4778 if (dol_strlen($phone) == 12) { //ex : +40 AB_CDE_FG_HI
4779 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4780 }
4781 } elseif (strtoupper($countrycode) == "TR") { //Turquie
4782 if (dol_strlen($phone) == 13) { //ex : +90 ABC_DEF_GHIJ
4783 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4784 }
4785 } elseif (strtoupper($countrycode) == "US") { //Etat-Unis
4786 if (dol_strlen($phone) == 12) { //ex: +1 ABC_DEF_GHIJ
4787 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4788 }
4789 } elseif (strtoupper($countrycode) == "MX") { //Mexique
4790 if (dol_strlen($phone) == 12) { //ex: +52 ABCD_EFG_HI
4791 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4792 } elseif (dol_strlen($phone) == 11) { //ex: +52 AB_CD_EF_GH
4793 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4794 } elseif (dol_strlen($phone) == 13) { //ex: +52 ABC_DEF_GHIJ
4795 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4796 }
4797 } elseif (strtoupper($countrycode) == "ML") { //Mali
4798 if (dol_strlen($phone) == 12) { //ex: +223 AB_CD_EF_GH
4799 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4800 }
4801 } elseif (strtoupper($countrycode) == "TH") { //Thaïlande
4802 if (dol_strlen($phone) == 11) { //ex: +66_ABC_DE_FGH
4803 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4804 } elseif (dol_strlen($phone) == 12) { //ex: +66_A_BCD_EF_GHI
4805 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 3);
4806 }
4807 } elseif (strtoupper($countrycode) == "MU") {
4808 //Maurice
4809 if (dol_strlen($phone) == 11) { //ex: +230_ABC_DE_FG
4810 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4811 } elseif (dol_strlen($phone) == 12) { //ex: +230_ABCD_EF_GH
4812 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4813 }
4814 } elseif (strtoupper($countrycode) == "ZA") { //Afrique du sud
4815 if (dol_strlen($phone) == 12) { //ex: +27_AB_CDE_FG_HI
4816 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4817 }
4818 } elseif (strtoupper($countrycode) == "SY") { //Syrie
4819 if (dol_strlen($phone) == 12) { //ex: +963_AB_CD_EF_GH
4820 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4821 } elseif (dol_strlen($phone) == 13) { //ex: +963_AB_CD_EF_GHI
4822 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 3);
4823 }
4824 } elseif (strtoupper($countrycode) == "AE") { //Emirats Arabes Unis
4825 if (dol_strlen($phone) == 12) { //ex: +971_ABC_DEF_GH
4826 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4827 } elseif (dol_strlen($phone) == 13) { //ex: +971_ABC_DEF_GHI
4828 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4829 } elseif (dol_strlen($phone) == 14) { //ex: +971_ABC_DEF_GHIK
4830 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 4);
4831 }
4832 } elseif (strtoupper($countrycode) == "DZ") { //Algérie
4833 if (dol_strlen($phone) == 13) { //ex: +213_ABC_DEF_GHI
4834 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4835 }
4836 } elseif (strtoupper($countrycode) == "BE") { //Belgique
4837 if (dol_strlen($phone) == 11) { //ex: +32_ABC_DE_FGH
4838 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4839 } elseif (dol_strlen($phone) == 12) { //ex: +32_ABC_DEF_GHI
4840 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4841 }
4842 } elseif (strtoupper($countrycode) == "PF") { //Polynésie française
4843 if (dol_strlen($phone) == 12) { //ex: +689_AB_CD_EF_GH
4844 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4845 }
4846 } elseif (strtoupper($countrycode) == "CO") { //Colombie
4847 if (dol_strlen($phone) == 13) { //ex: +57_ABC_DEF_GH_IJ
4848 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
4849 }
4850 } elseif (strtoupper($countrycode) == "JO") { //Jordanie
4851 if (dol_strlen($phone) == 12) { //ex: +962_A_BCD_EF_GH
4852 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 1) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4853 }
4854 } elseif (strtoupper($countrycode) == "JM") { //Jamaïque
4855 if (dol_strlen($newphone) == 12) { //ex: +1867_ABC_DEFG
4856 $newphone = substr($newphone, 0, 5) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4857 }
4858 } elseif (strtoupper($countrycode) == "MG") { //Madagascar
4859 if (dol_strlen($phone) == 13) { //ex: +261_AB_CD_EFG_HI
4860 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3) . $separ . substr($newphone, 11, 2);
4861 }
4862 } elseif (strtoupper($countrycode) == "GB") { //Royaume uni
4863 if (dol_strlen($phone) == 13) { //ex: +44_ABCD_EFG_HIJ
4864 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4865 }
4866 } elseif (strtoupper($countrycode) == "CH") { //Suisse
4867 if (dol_strlen($phone) == 12) { //ex: +41_AB_CDE_FG_HI
4868 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4869 } elseif (dol_strlen($phone) == 15) { // +41_AB_CDE_FGH_IJKL
4870 $newphone = $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 3) . $separ . substr($newphone, 11, 4);
4871 }
4872 } elseif (strtoupper($countrycode) == "TN") { //Tunisie
4873 if (dol_strlen($phone) == 12) { //ex: +216_AB_CDE_FGH
4874 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4875 }
4876 } elseif (strtoupper($countrycode) == "GF") { //Guyane francaise
4877 if (dol_strlen($phone) == 13) { //ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4878 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
4879 }
4880 } elseif (strtoupper($countrycode) == "GP") { //Guadeloupe
4881 if (dol_strlen($phone) == 13) { //ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4882 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
4883 }
4884 } elseif (strtoupper($countrycode) == "MQ") { //Martinique
4885 if (dol_strlen($phone) == 13) { //ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4886 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
4887 }
4888 } elseif (strtoupper($countrycode) == "IT") { //Italie
4889 if (dol_strlen($phone) == 12) { //ex: +39_ABC_DEF_GHI
4890 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4891 } elseif (dol_strlen($phone) == 13) { //ex: +39_ABC_DEF_GH_IJ
4892 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
4893 }
4894 } elseif (strtoupper($countrycode) == "AU") {
4895 //Australie
4896 if (dol_strlen($phone) == 12) {
4897 //ex: +61_A_BCDE_FGHI
4898 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 4);
4899 }
4900 } elseif (strtoupper($countrycode) == "LU") {
4901 // Luxembourg
4902 if (dol_strlen($phone) == 10) { // fix 6 digits +352_AA_BB_CC
4903 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2);
4904 } elseif (dol_strlen($phone) == 11) { // fix 7 digits +352_AA_BB_CC_D
4905 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 1);
4906 } elseif (dol_strlen($phone) == 12) { // fix 8 digits +352_AA_BB_CC_DD
4907 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4908 } elseif (dol_strlen($phone) == 13) { // mobile +352_AAA_BB_CC_DD
4909 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
4910 }
4911 } elseif (strtoupper($countrycode) == "PE") {
4912 // Peru
4913 if (dol_strlen($phone) == 7) { // fix 7 chiffres without code AAA_BBBB
4914 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4);
4915 } elseif (dol_strlen($phone) == 9) { // mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4916 $newphonewa = '+51' . $newphone;
4917 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 10, 3);
4918 } elseif (dol_strlen($phone) == 11) { // fix 11 chiffres +511_AAA_BBBB
4919 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 8, 4);
4920 } elseif (dol_strlen($phone) == 12) { // mobile +51_AAA_BBB_CCC
4921 $newphonewa = $newphone;
4922 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 10, 3) . $separ . substr($newphone, 14, 3);
4923 }
4924 } elseif (strtoupper($countrycode) == "IN") { //India
4925 if (dol_strlen($phone) == 13) {
4926 if ($withpicto == 'phone') { //ex: +91_AB_CDEF_GHIJ
4927 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 4) . $separ . substr($newphone, 9, 4);
4928 } else { //ex: +91_ABCDE_FGHIJ
4929 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 5) . $separ . substr($newphone, 8, 5);
4930 }
4931 }
4932 }
4933
4934 $newphoneastart = $newphoneaend = '';
4935 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4936 if ($addlink == 'tel' || $conf->browser->layout == 'phone' || (isModEnabled('clicktodial') && getDolGlobalString('CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS'))) { // If phone or option for, we use link of phone
4937 $newphoneastart = '<a href="tel:' . urlencode($phone) . '">';
4938 $newphoneaend .= '</a>';
4939 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4940 if (empty($user->clicktodial_loaded)) {
4941 $user->fetch_clicktodial();
4942 }
4943
4944 // Define urlmask
4945 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4946 if (!empty($user->clicktodial_url)) {
4947 $urlmask = $user->clicktodial_url;
4948 }
4949
4950 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4951 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4952 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4953 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4954 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4955 // Those lines are for substitution
4956 $substitarray = array(
4957 '__PHONEFROM__' => $clicktodial_poste,
4958 '__PHONETO__' => urlencode($phone),
4959 '__LOGIN__' => $clicktodial_login,
4960 '__PASS__' => $clicktodial_password
4961 );
4962 $url = make_substitutions($url, $substitarray);
4963 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4964 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4965 $newphoneastart = '<a href="' . $url . '" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4966 $newphoneaend = '</a>';
4967 } else {
4968 // Old method
4969 $newphoneastart = '<a href="' . $url . '"';
4970 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4971 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4972 }
4973 $newphoneastart .= '>';
4974 $newphoneaend .= '</a>';
4975 }
4976 }
4977
4978 //if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4979 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4980 $type = 'AC_TEL';
4981 $addlinktoagenda = '';
4982 if ($addlink == 'AC_FAX') {
4983 $type = 'AC_FAX';
4984 }
4985 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4986 $addlinktoagenda = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&amp;backtopage=' . urlencode($_SERVER['REQUEST_URI']) . '&amp;actioncode=' . $type . ($contactid ? '&amp;contactid=' . $contactid : '') . ($socid ? '&amp;socid=' . $socid : '') . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
4987 }
4988 if ($addlinktoagenda) {
4989 $newphone = '<span>' . $newphone . ' ' . $addlinktoagenda . '</span>';
4990 }
4991 }
4992 }
4993
4994 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4995 // Link to Whatsapp
4996 $newphone .= ' <a href="https://wa.me/' . $newphonewa . '" target="_blank"'; // Use api to whatasapp contacts
4997 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4998 }
4999
5000 if (empty($titlealt)) {
5001 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
5002 }
5003 $rep = '';
5004
5005 if ($hookmanager) {
5006 $parameters = array('countrycode' => $countrycode, 'cid' => $contactid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
5007 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
5008 $rep .= $hookmanager->resPrint;
5009 }
5010 if (empty($reshook)) {
5011 $picto = '';
5012 if ($withpicto) {
5013 if ($withpicto == 'fax') {
5014 $picto = 'phoning_fax';
5015 } elseif ($withpicto == 'phone') {
5016 $picto = 'phone';
5017 } elseif ($withpicto == 'mobile') {
5018 $picto = 'phoning_mobile';
5019 } else {
5020 $picto = '';
5021 }
5022 }
5023 if ($adddivfloat == 1) {
5024 $rep .= '<div class="nospan float' . ($morecss ? ' ' . $morecss : '') . '">';
5025 } elseif (empty($adddivfloat)) {
5026 $rep .= '<span' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
5027 }
5028
5029 $rep .= $newphoneastart;
5030 $rep .= ($withpicto ? img_picto($titlealt, $picto) : '');
5031 if ($separ != 'hidenum') {
5032 $rep .= ($withpicto ? ' ' : '') . $newphone;
5033 }
5034 $rep .= $newphoneaend;
5035
5036 if ($adddivfloat == 1) {
5037 $rep .= '</div>';
5038 } elseif (empty($adddivfloat)) {
5039 $rep .= '</span>';
5040 }
5041 }
5042
5043 return $rep;
5044}
5045
5054function dol_print_ip($ip, $mode = 0, $showname = 0)
5055{
5056 global $conf;
5057
5058 $ret = '';
5059 if (!isset($conf->cache['resolveips'])) {
5060 $conf->cache['resolveips'] = array();
5061 }
5062
5063 if ($mode != 2) {
5064 $countrycode = dolGetCountryCodeFromIp($ip);
5065 if ($countrycode) { // If success, countrycode is us, fr, ...
5066 if (file_exists(DOL_DOCUMENT_ROOT . '/theme/common/flags/' . $countrycode . '.png')) {
5067 $ret .= picto_from_langcode($countrycode);
5068 } else {
5069 $ret .= '(' . $countrycode . ')';
5070 }
5071 $ret .= '&nbsp;';
5072 } else {
5073 // Nothing
5074 }
5075 }
5076
5077 if (in_array($mode, [0, 2])) {
5078 $domain = '';
5079 if ($showname) {
5080 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
5081 $domain = gethostbyaddr($ip);
5082 $conf->cache['resolveips'][$ip] = $domain; // false or domain
5083 } else {
5084 $domain = $conf->cache['resolveips'][$ip];
5085 }
5086 }
5087 if ($domain) {
5088 $ret .= $domain;
5089 } else {
5090 $ret .= $ip;
5091 }
5092 }
5093
5094 return $ret;
5095}
5096
5109function getUserRemoteIP($trusted = 0)
5110{
5111 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
5112 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
5113 // Note that if apache module remoteip has been enabled, REMOTE_ADDR can contain the real client (the value from cloudFlare HTTP_CF_CONNECTING_IP for example)
5114 // This can happen if the proxy were added in the list of trusted proxy.
5115 return $ip;
5116 }
5117
5118 // Try to guess the real IP of client (but this may not be reliable)
5119 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
5120 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
5121 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
5122 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
5123 } else {
5124 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
5125 }
5126 } else {
5127 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
5128 }
5129 } else {
5130 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
5131 }
5132 return $ip;
5133}
5134
5143function isHTTPS()
5144{
5145 $isSecure = false;
5146 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
5147 $isSecure = true;
5148 } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') {
5149 $isSecure = true;
5150 }
5151 return $isSecure;
5152}
5153
5160function dolGetCountryCodeFromIp($ip)
5161{
5162 $countrycode = '';
5163
5164 if (isModEnabled('geoipmaxmind')) {
5165 if (getDolGlobalString('GEOIP_VERSION') == 'php') {
5166 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5167 } else {
5168 $diroffile = getMultidirOutput(null, 'geoipmaxmind');
5169 $datafile = $diroffile . '/' . getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE_EMBEDDED');
5170 }
5171 //$ip='24.24.24.24';
5172 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
5173 if ($datafile) {
5174 try {
5175 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5176 $geoip = new DolGeoIP('country', $datafile);
5177 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
5178 $countrycode = $geoip->getCountryCodeFromIP($ip);
5179 } catch (Exception $e) {
5180 //print 'Error with GeoIP database: '.$e->getMessage();
5181 }
5182 }
5183 }
5184
5185 return $countrycode;
5186}
5187
5188
5195function dol_user_country()
5196{
5197 global $conf, $langs, $user;
5198
5199 //$ret=$user->xxx;
5200 $ret = '';
5201 if (isModEnabled('geoipmaxmind')) {
5202 $ip = getUserRemoteIP();
5203 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5204 //$ip='24.24.24.24';
5205 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
5206 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5207 $geoip = new DolGeoIP('country', $datafile);
5208 $countrycode = $geoip->getCountryCodeFromIP($ip);
5209 $ret = $countrycode;
5210 }
5211 return $ret;
5212}
5213
5226function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
5227{
5228 global $hookmanager;
5229
5230 $out = '';
5231
5232 if ($address) {
5233 if ($hookmanager) {
5234 $parameters = array('element' => $element, 'id' => $id);
5235 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
5236 $out .= $hookmanager->resPrint;
5237 }
5238 if (empty($reshook)) {
5239 if (empty($charfornl)) {
5240 $out .= nl2br((string) $address);
5241 } else {
5242 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
5243 }
5244
5245 // TODO Remove this block, we can add this using the hook now
5246 $showgmap = $showomap = 0;
5247 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
5248 $showgmap = 1;
5249 }
5250 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
5251 $showgmap = 1;
5252 }
5253 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
5254 $showgmap = 1;
5255 }
5256 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
5257 $showgmap = 1;
5258 }
5259 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
5260 $showomap = 1;
5261 }
5262 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
5263 $showomap = 1;
5264 }
5265 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
5266 $showomap = 1;
5267 }
5268 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
5269 $showomap = 1;
5270 }
5271 if ($showgmap) {
5272 $url = dol_buildpath('/google/gmaps.php?mode=' . $element . '&id=' . $id, 1);
5273 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5274 }
5275 if ($showomap) {
5276 $url = dol_buildpath('/openstreetmap/maps.php?mode=' . $element . '&id=' . $id, 1);
5277 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '_openstreetmap" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5278 }
5279 }
5280 }
5281 if ($noprint) {
5282 return $out;
5283 } else {
5284 print $out;
5285 return null;
5286 }
5287}
5288
5289
5299function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5300{
5301 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5302 return true;
5303 }
5304 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5305 return true;
5306 }
5307 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5308 return true;
5309 }
5310
5311 return false;
5312}
5313
5323function isValidMXRecord($domain)
5324{
5325 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5326 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5327 return 0;
5328 }
5329 if (function_exists('getmxrr')) {
5330 $mxhosts = array();
5331 $weight = array();
5332 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5333 if (count($mxhosts) > 1) {
5334 return 1;
5335 }
5336 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5337 return 1;
5338 }
5339
5340 return 0;
5341 }
5342 }
5343
5344 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5345 return -1;
5346}
5347
5355function isValidPhone($phone)
5356{
5357 return true;
5358}
5359
5360
5370function dolGetFirstLetters($s, $nbofchar = 1)
5371{
5372 $ret = '';
5373 $tmparray = explode(' ', $s);
5374 foreach ($tmparray as $tmps) {
5375 $ret .= dol_substr($tmps, 0, $nbofchar);
5376 }
5377
5378 return $ret;
5379}
5380
5381
5389function dol_strlen($string, $stringencoding = 'UTF-8')
5390{
5391 if (is_null($string)) {
5392 return 0;
5393 }
5394
5395 if (function_exists('mb_strlen')) {
5396 return mb_strlen($string, $stringencoding);
5397 } else {
5398 return strlen($string);
5399 }
5400}
5401
5412function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5413{
5414 global $langs;
5415
5416 if (empty($stringencoding)) {
5417 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5418 }
5419
5420 $ret = '';
5421 if (empty($trunconbytes)) {
5422 if (function_exists('mb_substr')) {
5423 $ret = mb_substr($string, $start, $length, $stringencoding);
5424 } else {
5425 $ret = substr($string, $start, $length);
5426 }
5427 } else {
5428 if (function_exists('mb_strcut')) {
5429 $ret = mb_strcut($string, $start, $length, $stringencoding);
5430 } else {
5431 $ret = substr($string, $start, $length);
5432 }
5433 }
5434 return $ret;
5435}
5436
5437
5451function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5452{
5453 global $conf;
5454
5455 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5456 return $string;
5457 }
5458
5459 if (empty($stringencoding)) {
5460 $stringencoding = 'UTF-8';
5461 }
5462 // reduce for small screen
5463 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5464 $size = round($size / 3);
5465 }
5466
5467 // We go always here
5468 if ($trunc == 'right') {
5469 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5470 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5471 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5472 return dol_substr($newstring, 0, $size, $stringencoding) . ($nodot ? '' : '…');
5473 } else {
5474 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5475 return $string;
5476 }
5477 } elseif ($trunc == 'middle') {
5478 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5479 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5480 $size1 = (int) round($size / 2);
5481 $size2 = (int) round($size / 2);
5482 return dol_substr($newstring, 0, $size1, $stringencoding) . '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5483 } else {
5484 return $string;
5485 }
5486 } elseif ($trunc == 'left') {
5487 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5488 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5489 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5490 return '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5491 } else {
5492 return $string;
5493 }
5494 } elseif ($trunc == 'wrap') {
5495 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5496 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5497 return dol_substr($newstring, 0, $size, $stringencoding) . "\n" . dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5498 } else {
5499 return $string;
5500 }
5501 } else {
5502 return 'BadParam3CallingDolTrunc';
5503 }
5504}
5505
5513function getPictoForType($key, $morecss = '')
5514{
5515 // Set array with type -> picto
5516 $type2picto = array(
5517 'varchar' => 'font',
5518 'text' => 'font',
5519 'html' => 'code',
5520 'int' => 'sort-numeric-down',
5521 'double' => 'sort-numeric-down',
5522 'price' => 'currency',
5523 'pricecy' => 'multicurrency',
5524 'password' => 'key',
5525 'boolean' => 'check-square',
5526 'date' => 'calendar',
5527 'datetime' => 'calendar',
5528 'duration' => 'hourglass',
5529 'phone' => 'phone',
5530 'mail' => 'email',
5531 'url' => 'url',
5532 'ip' => 'country',
5533 'select' => 'list',
5534 'sellist' => 'list',
5535 'stars' => 'fontawesome_star_fas',
5536 'radio' => 'check-circle',
5537 'checkbox' => 'list',
5538 'chkbxlst' => 'list',
5539 'link' => 'link',
5540 'icon' => "question",
5541 'point' => "country",
5542 'multipts' => 'country',
5543 'linestrg' => "country",
5544 'polygon' => "country",
5545 'separate' => 'minus'
5546 );
5547
5548 if (!empty($type2picto[$key])) {
5549 return img_picto('', $type2picto[$key], 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5550 }
5551
5552 return img_picto('', 'generic', 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5553}
5554
5555
5579function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5580{
5581 global $conf;
5582
5583 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5584 $url = DOL_URL_ROOT;
5585 $theme = isset($conf->theme) ? $conf->theme : null;
5586 $path = 'theme/' . $theme;
5587 if (empty($picto)) {
5588 $picto = 'generic';
5589 }
5590
5591 // Define fullpathpicto to use into src
5592 if ($pictoisfullpath) {
5593 // Clean parameters
5594 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5595 $picto .= '.png';
5596 }
5597 $fullpathpicto = $picto;
5598 $reg = array();
5599 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5600 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5601 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5602 }
5603 } else {
5604 // $picto can not be null since replaced with 'generic' in that case
5605 // $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5606 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5607 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5608 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5609
5610 // Fix some values of $pictowithouttext
5611 $pictoconvertkey = array(
5612 'facture' => 'bill',
5613 'shipping' => 'shipment',
5614 'fichinter' => 'intervention',
5615 'agenda' => 'calendar',
5616 'invoice_supplier' => 'supplier_invoice',
5617 'order_supplier' => 'supplier_order');
5618 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5619 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5620 }
5621
5622 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5623 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5624 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5625 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5626
5627 // Compatibility with old fontawesome versions
5628 if ($pictowithouttext == 'file-o') {
5629 $pictowithouttext = 'file';
5630 }
5631
5632 $pictowithouttextarray = explode('_', $pictowithouttext);
5633 $marginleftonlyshort = 0;
5634
5635 if (!empty($pictowithouttextarray[1])) {
5636 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5637 $fakey = 'fa-' . $pictowithouttextarray[0];
5638 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5639 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5640 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5641 } else {
5642 $fakey = 'fa-' . $pictowithouttext;
5643 $faprefix = 'fas';
5644 $facolor = '';
5645 $fasize = '';
5646 }
5647
5648 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5649 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5650 $morestyle = '';
5651 $reg = array();
5652 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5653 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5654 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5655 }
5656 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5657 $morestyle = $reg[1];
5658 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5659 }
5660 $moreatt = trim($moreatt);
5661
5662 $enabledisablehtml = '<span class="' . $faprefix . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5663 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5664 $enabledisablehtml .= '</span>';
5665
5666 return $enabledisablehtml;
5667 }
5668
5669 if (empty($srconly) && !preg_match('/[\.\/@]/', $picto)) { // If original picto code does not contains a / and no . inside, it is not a path to an image file on disk
5670 $fakey = $pictowithouttext;
5671 $facolor = '';
5672 $fasize = '';
5673 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5674 if (in_array($pictowithouttext, array('card', 'bell', 'clock', 'establishment', 'file', 'file-o', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
5675 $fa = 'far';
5676 }
5677 if (in_array($pictowithouttext, array('black-tie', 'discord', 'facebook', 'flickr', 'github', 'google', 'google-plus-g', 'instagram', 'linkedin', 'meetup', 'microsoft', 'pinterest', 'skype', 'slack', 'twitter', 'reddit', 'snapchat', 'stripe', 'stripe-s', 'tumblr', 'viadeo', 'whatsapp', 'youtube'))) {
5678 $fa = 'fab';
5679 }
5680
5681 $arrayconvpictotofa = getImgPictoConv('fa');
5682
5683 if ($pictowithouttext == 'off') {
5684 $fakey = 'fa-square';
5685 $fasize = '1.3em';
5686 } elseif ($pictowithouttext == 'on') {
5687 $fakey = 'fa-check-square';
5688 $fasize = '1.3em';
5689 } elseif ($pictowithouttext == 'listlight') {
5690 $fakey = 'fa-download';
5691 $marginleftonlyshort = 1;
5692 } elseif ($pictowithouttext == 'printer') {
5693 $fakey = 'fa-print';
5694 $fasize = '1.2em';
5695 } elseif ($pictowithouttext == 'note') {
5696 $fakey = 'fa-sticky-note';
5697 $marginleftonlyshort = 1;
5698 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5699 $convertarray = array('1uparrow' => 'caret-up', '1downarrow' => 'caret-down', '1leftarrow' => 'caret-left', '1rightarrow' => 'caret-right', '1uparrow_selected' => 'caret-up', '1downarrow_selected' => 'caret-down', '1leftarrow_selected' => 'caret-left', '1rightarrow_selected' => 'caret-right');
5700 $fakey = 'fa-' . $convertarray[$pictowithouttext];
5701 if (preg_match('/selected/', $pictowithouttext)) {
5702 $facolor = '#888';
5703 }
5704 $marginleftonlyshort = 1;
5705 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5706 $fakey = 'fa-' . $arrayconvpictotofa[$pictowithouttext];
5707 } else {
5708 $fakey = 'fa-' . $pictowithouttext;
5709 }
5710
5711 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5712 $morecss .= ' em092';
5713 }
5714 if (in_array($pictowithouttext, array('conferenceorbooth', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5715 $morecss .= ' em088';
5716 }
5717 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5718 $morecss .= ' em080';
5719 }
5720
5721 // Define $marginleftonlyshort
5722 $arrayconvpictotomarginleftonly = array(
5723 'bank',
5724 'check',
5725 'delete',
5726 'generic',
5727 'grip',
5728 'grip_title',
5729 'jabber',
5730 'grip_title',
5731 'grip',
5732 'listlight',
5733 'note',
5734 'on',
5735 'off',
5736 'playdisabled',
5737 'printer',
5738 'resize',
5739 'sign-out',
5740 'stats',
5741 'switch_on',
5742 'switch_on_grey',
5743 'switch_on_red',
5744 'switch_off',
5745 'switch_off_grey',
5746 'switch_off_red',
5747 'uparrow',
5748 '1uparrow',
5749 '1downarrow',
5750 '1leftarrow',
5751 '1rightarrow',
5752 '1uparrow_selected',
5753 '1downarrow_selected',
5754 '1leftarrow_selected',
5755 '1rightarrow_selected'
5756 );
5757 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5758 $marginleftonlyshort = 0;
5759 }
5760
5761 // Add CSS
5762 $arrayconvpictotomorcess = array(
5763 'action' => 'infobox-action',
5764 'account' => 'infobox-bank_account',
5765 'accounting_account' => 'infobox-bank_account',
5766 'accountline' => 'infobox-bank_account',
5767 'accountancy' => 'infobox-bank_account',
5768 'admin' => 'opacitymedium',
5769 'asset' => 'infobox-bank_account',
5770 'bank_account' => 'infobox-bank_account',
5771 'bill' => 'infobox-commande',
5772 'billa' => 'infobox-commande',
5773 'billr' => 'infobox-commande',
5774 'billd' => 'infobox-commande',
5775 'bookcal' => 'infobox-portal',
5776 'margin' => 'infobox-bank_account',
5777 'conferenceorbooth' => 'infobox-project',
5778 'cash-register' => 'infobox-portal',
5779 'contract' => 'infobox-contrat',
5780 'check' => 'font-status4',
5781 'conversation' => 'infobox-contrat',
5782 'donation' => 'infobox-commande',
5783 'dolly' => 'infobox-commande',
5784 'dollyrevert' => 'flip infobox-order_supplier',
5785 'ecm' => 'infobox-action',
5786 'eventorganization' => 'infobox-project',
5787 'hrm' => 'infobox-adherent',
5788 'group' => 'infobox-adherent',
5789 'intervention' => 'infobox-contrat',
5790 'incoterm' => 'infobox-supplier_proposal',
5791 'intracommreport' => 'infobox-bank_account',
5792 'currency' => 'infobox-bank_account',
5793 'multicurrency' => 'infobox-bank_account',
5794 'members' => 'infobox-adherent',
5795 'member' => 'infobox-adherent',
5796 'money-bill-alt' => 'infobox-bank_account',
5797 'order' => 'infobox-commande',
5798 'user' => 'infobox-adherent',
5799 'users' => 'infobox-adherent',
5800 'error' => 'pictoerror',
5801 'warning' => 'pictowarning',
5802 'switch_on' => 'font-status4',
5803 'switch_on_warning' => 'font-status4 warning',
5804 'switch_on_red' => 'font-status8',
5805 'switch_off_warning' => 'font-status4 warning',
5806 'switch_off_red' => 'font-status8',
5807 'holiday' => 'infobox-holiday',
5808 'info' => 'opacityhigh',
5809 'info_black' => 'purple',
5810 'invoice' => 'infobox-commande',
5811 'knowledgemanagement' => 'infobox-contrat rotate90',
5812 'loan' => 'infobox-commande',
5813 'payment' => 'infobox-bank_account',
5814 'payment_vat' => 'infobox-bank_account',
5815 'poll' => 'infobox-portal',
5816 'pos' => 'infobox-bank_account',
5817 'project' => 'infobox-project',
5818 'projecttask' => 'infobox-project',
5819 'propal' => 'infobox-propal',
5820 'proposal' => 'infobox-propal',
5821 'private' => 'infobox-project',
5822 'reception' => 'flip infobox-order_supplier',
5823 'recruitmentjobposition' => 'infobox-adherent',
5824 'recruitmentcandidature' => 'infobox-adherent',
5825 'resource' => 'infobox-action',
5826 'salary' => 'infobox-commande',
5827 'shapes' => 'infobox-adherent',
5828 'shipment' => 'infobox-commande',
5829 'store' => 'infobox-portal',
5830 'stripe' => 'infobox-bank_account',
5831 'supplier_invoice' => 'infobox-order_supplier',
5832 'supplier_invoicea' => 'infobox-order_supplier',
5833 'supplier_invoiced' => 'infobox-order_supplier',
5834 'supplier' => 'infobox-order_supplier',
5835 'supplier_order' => 'infobox-order_supplier',
5836 'supplier_proposal' => 'infobox-supplier_proposal',
5837 'ticket' => 'infobox-contrat',
5838 'title_accountancy' => 'infobox-bank_account',
5839 'title_hrm' => 'infobox-holiday',
5840 'expensereport' => 'infobox-expensereport',
5841 'trip' => 'infobox-expensereport',
5842 'title_agenda' => 'infobox-action',
5843 'vat' => 'infobox-bank_account',
5844 'webportal' => 'infobox-portal',
5845 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5846 'list-alt' => 'imgforviewmode',
5847 'calendar' => 'imgforviewmode',
5848 'calendarweek' => 'imgforviewmode',
5849 'calendarmonth' => 'imgforviewmode',
5850 'calendarday' => 'imgforviewmode',
5851 'calendarperuser' => 'imgforviewmode',
5852 'calendarpertype' => 'imgforviewmode'
5853 );
5854 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5855 $morecss .= ($morecss ? ' ' : '') . $arrayconvpictotomorcess[$pictowithouttext];
5856 }
5857
5858 // Define $color
5859 $arrayconvpictotocolor = array(
5860 'address' => '#6c6aa8',
5861 'building' => '#6c6aa8',
5862 'bom' => '#a69944',
5863 'clone' => '#999',
5864 'cog' => '#999',
5865 'companies' => '#6c6aa8',
5866 'company' => '#6c6aa8',
5867 'contact' => '#6c6aa8',
5868 'cron' => '#555',
5869 'dynamicprice' => '#a69944',
5870 'edit' => '#444',
5871 'note' => '#999',
5872 'error' => '',
5873 'help' => '#bbb',
5874 'listlight' => '#999',
5875 'language' => '#555',
5876 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5877 'lock' => '#ddd',
5878 'lot' => '#a69944',
5879 'map-marker-alt' => '#aaa',
5880 'mrp' => '#a69944',
5881 'product' => '#a69944',
5882 'service' => '#a69944',
5883 'inventory' => '#a69944',
5884 'stock' => '#a69944',
5885 'movement' => '#a69944',
5886 'other' => '#ddd',
5887 'world' => '#986c6a',
5888 'partnership' => '#6c6aa8',
5889 'playdisabled' => '#ccc',
5890 'printer' => '#444',
5891 'projectpub' => '#986c6a',
5892 'resize' => '#444',
5893 'rss' => '#cba',
5894 //'shipment'=>'#a69944',
5895 'search-plus' => '#808080',
5896 'security' => '#999',
5897 'square' => '#888',
5898 'stop-circle' => '#888',
5899 'stats' => '#444',
5900 'superadmin' => '#600',
5901 'switch_off' => '#999',
5902 'technic' => '#999',
5903 'tick' => '#282',
5904 'timespent' => '#555',
5905 'uncheck' => '#800',
5906 'uparrow' => '#555',
5907 'user-cog' => '#999',
5908 'country' => '#aaa',
5909 'globe-americas' => '#aaa',
5910 'region' => '#aaa',
5911 'state' => '#aaa',
5912 'website' => '#304',
5913 'workstation' => '#a69944'
5914 );
5915 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5916 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5917 }
5918
5919 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5920 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5921 $morestyle = '';
5922 $reg = array();
5923 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5924 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5925 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5926 }
5927 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5928 $morestyle = $reg[1];
5929 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5930 }
5931 $moreatt = trim($moreatt);
5932
5933 $enabledisablehtml = '<span class="' . $fa . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5934 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5935 $enabledisablehtml .= '</span>';
5936
5937 return $enabledisablehtml;
5938 }
5939
5940 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5941 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/' . $theme; // If the theme does not have the same name as the module
5942 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5943 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_RES') . '/theme/' . getDolGlobalString('MAIN_OVERWRITE_THEME_RES'); // To allow an external module to overwrite image resources whatever is activated theme
5944 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5945 $path = $theme . '/theme/' . $theme; // If the theme have the same name as the module
5946 }
5947
5948 // If we ask an image into $url/$mymodule/img (instead of default path)
5949 $regs = array();
5950 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5951 $picto = $regs[1];
5952 $path = $regs[2]; // $path is $mymodule
5953 }
5954
5955 // Clean parameters
5956 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5957 $picto .= '.png';
5958 }
5959 // If alt path are defined, define url where img file is, according to physical path
5960 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5961 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5962 if ($type == 'main') {
5963 continue;
5964 }
5965 // This consumes a lot of time, that's why enabling alternative dir like "custom" dir should be avoid
5966 if (file_exists($dirroot . '/' . $path . '/img/' . $picto) && !empty($conf->file->dol_url_root)) {
5967 $url = DOL_URL_ROOT . $conf->file->dol_url_root[$type];
5968 break;
5969 }
5970 }
5971
5972 // $url is '' or '/custom', $path is current theme or
5973 $fullpathpicto = $url . '/' . $path . '/img/' . $picto;
5974 }
5975
5976 if ($srconly) {
5977 return $fullpathpicto;
5978 }
5979
5980 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5981 return '<img src="' . $fullpathpicto . '"' . ($notitle ? '' : ' alt="' . dolPrintHTMLForAttribute($alt, 0, $allowothertags) . '"') . (($notitle || empty($titlealt)) ? '' : ' title="' . dolPrintHTMLForAttribute($titlealt, 0, $allowothertags) . '"') . ($moreatt ? ' ' . $moreatt . ($morecss ? ' class="' . $morecss . '"' : '') : ' class="inline-block' . ($morecss ? ' ' . $morecss : '') . '"') . '>'; // Alt is used for accessibility, title for popup
5982}
5983
5991function getImgPictoConv($mode = 'fa')
5992{
5993 global $conf;
5994
5995 if (empty($mode) || $mode == 'fa') {
5996 // Array when the fa picto key is different than the Dolibarr picto key.
5997 $arrayconvpictotofa = array(
5998 'account' => 'university',
5999 'accounting_account' => 'clipboard-list',
6000 'accountline' => 'receipt',
6001 'accountancy' => 'search-dollar',
6002 'action' => 'calendar-alt',
6003 'add' => 'plus-circle',
6004 'address' => 'address-book',
6005 'ai' => 'magic',
6006 'admin' => 'star',
6007 'asset' => 'money-check-alt',
6008 'autofill' => 'fill',
6009 'back' => 'arrow-left',
6010 'bank_account' => 'university',
6011 'bill' => 'file-invoice-dollar',
6012 'billa' => 'file-excel',
6013 'billr' => 'file-invoice-dollar',
6014 'billd' => 'file-medical',
6015 'blockedlog' => 'file-archive',
6016 'bookcal' => 'calendar-check',
6017 'supplier_invoice' => 'file-invoice-dollar',
6018 'supplier_invoicea' => 'file-excel',
6019 'supplier_invoicer' => 'file-invoice-dollar',
6020 'supplier_invoiced' => 'file-medical',
6021 'bom' => 'shapes',
6022 'card' => 'address-card',
6023 'chart' => 'chart-line',
6024 'company' => 'building',
6025 'contact' => 'address-book',
6026 'contract' => 'suitcase',
6027 'collab' => 'people-arrows',
6028 'conversation' => 'comments',
6029 'country' => 'globe-americas',
6030 'cron' => 'business-time',
6031 'cross' => 'times',
6032 'chevron-double-left' => 'angle-double-left',
6033 'chevron-double-right' => 'angle-double-right',
6034 'chevron-double-down' => 'angle-double-down',
6035 'chevron-double-top' => 'angle-double-up',
6036 'donation' => 'gift',
6037 'dynamicprice' => 'hand-holding-usd',
6038 'setup' => 'cog',
6039 'companies' => 'building',
6040 'products' => 'cube',
6041 'commercial' => 'suitcase',
6042 'invoicing' => 'coins',
6043 'accounting' => 'search-dollar',
6044 'category' => 'tag',
6045 'dollyrevert' => 'dolly',
6046 'file-o' => 'file',
6047 'generate' => 'plus-square',
6048 'hrm' => 'user-tie',
6049 'incoterm' => 'truck-loading',
6050 'margin' => 'calculator',
6051 'members' => 'user-friends',
6052 'ticket' => 'ticket-alt',
6053 'globe' => 'external-link-alt',
6054 'lot' => 'barcode',
6055 'email' => 'at',
6056 'establishment' => 'building',
6057 'edit' => 'pencil-alt',
6058 'entity' => 'globe',
6059 'graph' => 'chart-line',
6060 'grip_title' => 'arrows-alt',
6061 'grip' => 'arrows-alt',
6062 'help' => 'question-circle',
6063 'generic' => 'file',
6064 'holiday' => 'umbrella-beach',
6065 'info' => 'info-circle',
6066 'info_black' => 'info-circle',
6067 'inventory' => 'boxes',
6068 'intracommreport' => 'globe-europe',
6069 'jobprofile' => 'cogs',
6070 'knowledgemanagement' => 'ticket-alt',
6071 'label' => 'layer-group',
6072 'layout' => 'columns',
6073 'line' => 'bars',
6074 'loan' => 'money-bill-alt',
6075 'member' => 'user-alt',
6076 'meeting' => 'chalkboard-teacher',
6077 'mrp' => 'cubes',
6078 'next' => 'arrow-alt-circle-right',
6079 'trip' => 'wallet',
6080 'expensereport' => 'wallet',
6081 'group' => 'users',
6082 'movement' => 'people-carry',
6083 'sign-out' => 'sign-out-alt',
6084 'superadmin' => 'star',
6085 'switch_off' => 'toggle-off',
6086 'switch_off_grey' => 'toggle-off',
6087 'switch_off_warning' => 'toggle-off',
6088 'switch_off_red' => 'toggle-off',
6089 'switch_on' => 'toggle-on',
6090 'switch_on_grey' => 'toggle-on',
6091 'switch_on_warning' => 'toggle-on',
6092 'switch_on_red' => 'toggle-on',
6093 'check' => 'check',
6094 'bookmark' => 'star',
6095 'bank' => 'university',
6096 'close_title' => 'times',
6097 'delete' => 'trash',
6098 'filter' => 'filter',
6099 'list-alt' => 'list-alt',
6100 'calendarlist' => 'bars',
6101 'calendar' => 'calendar-alt',
6102 'calendarmonth' => 'calendar-alt',
6103 'calendarweek' => 'calendar-week',
6104 'calendarday' => 'calendar-day',
6105 'calendarperuser' => 'table',
6106 'calendarpertype' => 'table',
6107 'intervention' => 'ambulance',
6108 'invoice' => 'file-invoice-dollar',
6109 'order' => 'file-invoice',
6110 'error' => 'exclamation-triangle',
6111 'warning' => 'exclamation-triangle',
6112 'other' => 'square',
6113 'playdisabled' => 'play',
6114 'pdf' => 'file-pdf',
6115 'poll' => 'check-double',
6116 'pos' => 'cash-register',
6117 'preview' => 'binoculars',
6118 'project' => 'project-diagram',
6119 'projectpub' => 'project-diagram',
6120 'projecttask' => 'tasks',
6121 'propal' => 'file-signature',
6122 'proposal' => 'file-signature',
6123 'partnership' => 'handshake',
6124 'payment' => 'money-check-alt',
6125 'payment_vat' => 'money-check-alt',
6126 'pictoconfirm' => 'check-square',
6127 'phoning' => 'phone',
6128 'phoning_mobile' => 'mobile-alt',
6129 'phoning_fax' => 'fax',
6130 'previous' => 'arrow-alt-circle-left',
6131 'printer' => 'print',
6132 'product' => 'cube',
6133 'puce' => 'angle-right',
6134 'recent' => 'check-square',
6135 'reception' => 'dolly',
6136 'recruitmentjobposition' => 'id-card-alt',
6137 'recruitmentcandidature' => 'id-badge',
6138 'resize' => 'crop',
6139 'supplier_order' => 'dol-order_supplier',
6140 'supplier_proposal' => 'file-signature',
6141 'refresh' => 'redo',
6142 'region' => 'map-marked',
6143 'replacement' => 'exchange-alt',
6144 'resource' => 'laptop-house',
6145 'recurring' => 'history',
6146 'service' => 'concierge-bell',
6147 'skill' => 'shapes',
6148 'state' => 'map-marked-alt',
6149 'security' => 'key',
6150 'salary' => 'wallet',
6151 'shipment' => 'dolly',
6152 'stock' => 'box-open',
6153 'stats' => 'chart-bar',
6154 'split' => 'code-branch',
6155 'status' => 'stop-circle',
6156 'stripe' => 'stripe-s',
6157 'supplier' => 'building',
6158 'technic' => 'cogs',
6159 'tick' => 'check',
6160 'timespent' => 'clock',
6161 'title_setup' => 'tools',
6162 'title_accountancy' => 'money-check-alt',
6163 'title_bank' => 'university',
6164 'title_hrm' => 'umbrella-beach',
6165 'title_agenda' => 'calendar-alt',
6166 'uncheck' => 'times',
6167 'uparrow' => 'share',
6168 'url' => 'external-link-alt',
6169 'vat' => 'money-check-alt',
6170 'vcard' => 'arrow-alt-circle-down',
6171 'jabber' => 'comment',
6172 'website' => 'globe-americas',
6173 'workstation' => 'pallet',
6174 'webhook' => 'bullseye',
6175 'world' => 'globe',
6176 'private' => 'user-lock',
6177 'conferenceorbooth' => 'chalkboard-teacher',
6178 'eventorganization' => 'project-diagram',
6179 'webportal' => 'door-open'
6180 );
6181
6182 if ($conf->currency == 'EUR') {
6183 $arrayconvpictotofa['currency'] = 'euro-sign';
6184 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
6185 } else {
6186 $arrayconvpictotofa['currency'] = 'dollar-sign';
6187 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
6188 }
6189 } else {
6190 $arrayconvpictotofa = array();
6191 }
6192
6193 return $arrayconvpictotofa;
6194}
6195
6196
6211function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
6212{
6213 if (strpos($picto, '^') === 0) {
6214 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6215 } else {
6216 return img_picto($titlealt, 'object_' . $picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6217 }
6218}
6219
6231function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
6232{
6233 global $conf;
6234
6235 if (is_numeric($picto)) {
6236 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
6237 //$picto = $leveltopicto[$picto];
6238 return '<i class="fa fa-weather-level' . $picto . '"></i>';
6239 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6240 $picto .= '.png';
6241 }
6242
6243 $path = DOL_URL_ROOT . '/theme/' . $conf->theme . '/img/weather/' . $picto;
6244
6245 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
6246}
6247
6259function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
6260{
6261 global $conf;
6262
6263 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6264 $picto .= '.png';
6265 }
6266
6267 if ($pictoisfullpath) {
6268 $path = $picto;
6269 } else {
6270 $path = DOL_URL_ROOT . '/theme/common/' . $picto;
6271
6272 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
6273 $themepath = DOL_DOCUMENT_ROOT . '/theme/' . $conf->theme . '/img/' . $picto;
6274
6275 if (file_exists($themepath)) {
6276 $path = $themepath;
6277 }
6278 }
6279 }
6280
6281 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
6282}
6283
6297function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
6298{
6299 global $langs;
6300
6301 if (empty($titlealt) || $titlealt == 'default') {
6302 if ($numaction == '-1' || $numaction == 'ST_NO') {
6303 $numaction = -1;
6304 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
6305 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
6306 $numaction = 0;
6307 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
6308 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
6309 $numaction = 1;
6310 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
6311 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
6312 $numaction = 2;
6313 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
6314 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
6315 $numaction = 3;
6316 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
6317 } else {
6318 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus ' . $numaction);
6319 $numaction = 0;
6320 }
6321 }
6322 if (!is_numeric($numaction)) {
6323 $numaction = 0;
6324 }
6325
6326 return img_picto($titlealt, (empty($picto) ? 'stcomm' . $numaction . '.png' : $picto), $moreatt);
6327}
6328
6336function img_edit_add($titlealt = 'default', $other = '')
6337{
6338 global $langs;
6339
6340 if ($titlealt == 'default') {
6341 $titlealt = $langs->trans('Add');
6342 }
6343
6344 return img_picto($titlealt, 'edit_add.png', $other);
6345}
6353function img_edit_remove($titlealt = 'default', $other = '')
6354{
6355 global $langs;
6356
6357 if ($titlealt == 'default') {
6358 $titlealt = $langs->trans('Remove');
6359 }
6360
6361 return img_picto($titlealt, 'edit_remove.png', $other);
6362}
6363
6372function img_edit($titlealt = 'default', $float = 0, $other = '')
6373{
6374 global $langs;
6375
6376 if ($titlealt == 'default') {
6377 $titlealt = $langs->trans('Modify');
6378 }
6379
6380 return img_picto($titlealt, 'edit', ($float ? 'style="float: ' . ($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right') . '"' : "") . ($other ? ' ' . $other : ''));
6381}
6382
6391function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
6392{
6393 global $langs;
6394
6395 if ($titlealt == 'default') {
6396 $titlealt = $langs->trans('View');
6397 }
6398
6399 $moreatt = ($float ? 'style="float: right" ' : '') . $other;
6400
6401 return img_picto($titlealt, 'eye', $moreatt);
6402}
6403
6412function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
6413{
6414 global $langs;
6415
6416 if ($titlealt == 'default') {
6417 $titlealt = $langs->trans('Delete');
6418 }
6419
6420 return img_picto($titlealt, 'delete', $other, 0, 0, 0, '', $morecss);
6421}
6422
6430function img_printer($titlealt = "default", $other = '')
6431{
6432 global $langs;
6433 if ($titlealt == "default") {
6434 $titlealt = $langs->trans("Print");
6435 }
6436 return img_picto($titlealt, 'printer', $other);
6437}
6438
6446function img_split($titlealt = 'default', $other = 'class="pictosplit"')
6447{
6448 global $langs;
6449
6450 if ($titlealt == 'default') {
6451 $titlealt = $langs->trans('Split');
6452 }
6453
6454 return img_picto($titlealt, 'split', $other);
6455}
6456
6464function img_help($usehelpcursor = 1, $usealttitle = 1)
6465{
6466 global $langs;
6467
6468 if ($usealttitle) {
6469 if (is_string($usealttitle)) {
6470 $usealttitle = dol_escape_htmltag($usealttitle);
6471 } else {
6472 $usealttitle = $langs->trans('Info');
6473 }
6474 }
6475
6476 return img_picto($usealttitle, 'info', 'style="vertical-align: middle;' . ($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')) . '"');
6477}
6478
6485function img_info($titlealt = 'default')
6486{
6487 global $langs;
6488
6489 if ($titlealt == 'default') {
6490 $titlealt = $langs->trans('Informations');
6491 }
6492
6493 return img_picto($titlealt, 'info', 'style="vertical-align: middle;"');
6494}
6495
6504function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6505{
6506 global $langs;
6507
6508 if ($titlealt == 'default') {
6509 $titlealt = $langs->trans('Warning');
6510 }
6511
6512 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6513 return img_picto($titlealt, 'warning', 'class="' . $morecss . '"' . ($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' ' . $moreatt) : ''));
6514}
6515
6522function img_error($titlealt = 'default')
6523{
6524 global $langs;
6525
6526 if ($titlealt == 'default') {
6527 $titlealt = $langs->trans('Error');
6528 }
6529
6530 return img_picto($titlealt, 'error');
6531}
6532
6540function img_next($titlealt = 'default', $moreatt = '')
6541{
6542 global $langs;
6543
6544 if ($titlealt == 'default') {
6545 $titlealt = $langs->trans('Next');
6546 }
6547
6548 //return img_picto($titlealt, 'next.png', $moreatt);
6549 return '<span class="fa fa-chevron-right paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6550}
6551
6559function img_previous($titlealt = 'default', $moreatt = '')
6560{
6561 global $langs;
6562
6563 if ($titlealt == 'default') {
6564 $titlealt = $langs->trans('Previous');
6565 }
6566
6567 //return img_picto($titlealt, 'previous.png', $moreatt);
6568 return '<span class="fa fa-chevron-left paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6569}
6570
6579function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6580{
6581 global $langs;
6582
6583 if ($titlealt == 'default') {
6584 $titlealt = $langs->trans('Down');
6585 }
6586
6587 return img_picto($titlealt, ($selected ? '1downarrow_selected' : '1downarrow'), 'class="imgdown' . ($moreclass ? " " . $moreclass : "") . '"');
6588}
6589
6598function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6599{
6600 global $langs;
6601
6602 if ($titlealt == 'default') {
6603 $titlealt = $langs->trans('Up');
6604 }
6605
6606 return img_picto($titlealt, ($selected ? '1uparrow_selected' : '1uparrow'), 'class="imgup' . ($moreclass ? " " . $moreclass : "") . '"');
6607}
6608
6617function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6618{
6619 global $langs;
6620
6621 if ($titlealt == 'default') {
6622 $titlealt = $langs->trans('Left');
6623 }
6624
6625 return img_picto($titlealt, ($selected ? '1leftarrow_selected' : '1leftarrow'), $moreatt);
6626}
6627
6636function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6637{
6638 global $langs;
6639
6640 if ($titlealt == 'default') {
6641 $titlealt = $langs->trans('Right');
6642 }
6643
6644 return img_picto($titlealt, ($selected ? '1rightarrow_selected' : '1rightarrow'), $moreatt);
6645}
6646
6654function img_allow($allow, $titlealt = 'default')
6655{
6656 global $langs;
6657
6658 if ($titlealt == 'default') {
6659 $titlealt = $langs->trans('Active');
6660 }
6661
6662 if ($allow == 1) {
6663 return img_picto($titlealt, 'tick');
6664 }
6665
6666 return '-';
6667}
6668
6676function img_credit_card($brand, $morecss = 'fa-2x inline-block valignmiddle')
6677{
6678 if (is_null($morecss)) {
6679 $morecss = 'fa-2x';
6680 }
6681
6682 if ($brand == 'visa' || $brand == 'Visa') {
6683 $brand = 'cc-visa';
6684 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6685 $brand = 'cc-mastercard';
6686 } elseif ($brand == 'amex' || $brand == 'American Express') {
6687 $brand = 'cc-amex';
6688 } elseif ($brand == 'discover' || $brand == 'Discover') {
6689 $brand = 'cc-discover';
6690 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6691 $brand = 'cc-jcb';
6692 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6693 $brand = 'cc-diners-club';
6694 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6695 $brand = 'credit-card';
6696 }
6697
6698 return '<span class="fa fa-' . $brand . ' fa-fw' . ($morecss ? ' ' . $morecss : '') . '"></span>';
6699}
6700
6709function img_mime($file, $titlealt = '', $morecss = '')
6710{
6711 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
6712
6713 $mimetype = dol_mimetype($file, '', 1);
6714 //$mimeimg = dol_mimetype($file, '', 2);
6715 $mimefa = dol_mimetype($file, '', 4);
6716
6717 if (empty($titlealt)) {
6718 $titlealt = 'Mime type: ' . $mimetype;
6719 }
6720
6721 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6722 return '<i class="fa fa-' . $mimefa . ' ' . (preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ') . ($morecss ? ' ' . $morecss : '') . '"' . ($titlealt ? ' title="' . dolPrintHTMLForAttribute($titlealt) . '"' : '') . '></i>';
6723}
6724
6725
6733function img_search($titlealt = 'default', $other = '')
6734{
6735 global $langs;
6736
6737 if ($titlealt == 'default') {
6738 $titlealt = $langs->trans('Search');
6739 }
6740
6741 $img = img_picto($titlealt, 'search', $other, 0, 1);
6742
6743 $input = '<input type="image" class="liste_titre" name="button_search" src="' . $img . '" ';
6744 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6745
6746 return $input;
6747}
6748
6756function img_searchclear($titlealt = 'default', $other = '')
6757{
6758 global $langs;
6759
6760 if ($titlealt == 'default') {
6761 $titlealt = $langs->trans('Search');
6762 }
6763
6764 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6765
6766 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="' . $img . '" ';
6767 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6768
6769 return $input;
6770}
6771
6784function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6785{
6786 global $conf, $langs;
6787
6788 if ($infoonimgalt) {
6789 $result = img_picto($text, 'info', 'class="' . ($morecss ? ' ' . $morecss : '') . '"');
6790 } else {
6791 if (empty($conf->use_javascript_ajax)) {
6792 $textfordropdown = '';
6793 }
6794
6795 $class = (empty($admin) ? 'undefined' : ((string) $admin == '1' ? 'info' : $admin));
6796 $fa = 'info-circle';
6797 if ($picto == 'warning') {
6798 $fa = 'exclamation-triangle';
6799 }
6800 $result = ($nodiv ? '' : '<div class="wordbreak ' . $class . ($morecss ? ' ' . $morecss : '') . ($textfordropdown ? ' hidden' : '') . '">');
6801 $result .= img_picto((string) $admin ? $langs->trans('InfoAdmin') : $langs->trans('Note'), $fa);
6802 $result .= ' ';
6803 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6804 $result .= ($nodiv ? '' : '</div>');
6805
6806 if ($textfordropdown) {
6807 $tmpresult = '<span class="' . $class . 'text opacitymedium cursorpointer">' . $langs->trans($textfordropdown) . ' ' . img_picto($langs->trans($textfordropdown), '1downarrow') . '</span>';
6808 $tmpresult .= '<script nonce="' . getNonce() . '" type="text/javascript">
6809 jQuery(document).ready(function() {
6810 jQuery(".' . $class . 'text").click(function() {
6811 console.log("toggle text");
6812 jQuery(".' . $class . '").toggle();
6813 });
6814 });
6815 </script>';
6816
6817 $result = $tmpresult . $result;
6818 }
6819 }
6820
6821 return $result;
6822}
6823
6824
6836function dol_print_error($db = null, $error = '', $errors = null)
6837{
6838 global $conf, $langs, $user, $argv;
6839 global $dolibarr_main_prod;
6840
6841 $out = '';
6842 $syslog = '';
6843
6844 // If error occurs before the $lang object was loaded
6845 if (!$langs) {
6846 require_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
6847 $langs = new Translate('', $conf);
6848 $langs->load("main");
6849 }
6850
6851 // Load translation files required by the error messages
6852 $langs->loadLangs(array('main', 'errors'));
6853
6854 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6855 $out .= $langs->trans("DolibarrHasDetectedError") . ".<br>\n";
6856 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6857 $out .= "You use an experimental or develop level of features, so please do NOT report any bugs or vulnerability, except if problem is confirmed after moving option MAIN_FEATURES_LEVEL back to 0.<br>\n";
6858 }
6859 $out .= $langs->trans("InformationToHelpDiagnose") . ":<br>\n";
6860
6861 $out .= "<b>" . $langs->trans("Date") . ":</b> " . dol_print_date(time(), 'dayhourlog') . "<br>\n";
6862 $out .= "<b>" . $langs->trans("Dolibarr") . ":</b> " . DOL_VERSION . " - https://www.dolibarr.org<br>\n";
6863 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6864 $out .= "<b>" . $langs->trans("LevelOfFeature") . ":</b> " . getDolGlobalInt('MAIN_FEATURES_LEVEL') . "<br>\n";
6865 }
6866 if ($user instanceof User) {
6867 $out .= "<b>" . $langs->trans("Login") . ":</b> " . $user->login . "<br>\n";
6868 }
6869 if (function_exists("phpversion")) {
6870 $out .= "<b>" . $langs->trans("PHP") . ":</b> " . phpversion() . "<br>\n";
6871 }
6872 $out .= "<b>" . $langs->trans("Server") . ":</b> " . (isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '') . "<br>\n";
6873 if (function_exists("php_uname")) {
6874 $out .= "<b>" . $langs->trans("OS") . ":</b> " . php_uname() . "<br>\n";
6875 }
6876 $out .= "<b>" . $langs->trans("UserAgent") . ":</b> " . (isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '') . "<br>\n";
6877 $out .= "<br>\n";
6878 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6879 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6880 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6881 $out .= "<br>\n";
6882 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6883 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6884 } else { // Mode CLI
6885 $out .= '> ' . $langs->transnoentities("ErrorInternalErrorDetected") . ":\n" . $argv[0] . "\n";
6886 $syslog .= "pid=" . dol_getmypid();
6887 }
6888
6889 if (!empty($conf->modules)) {
6890 $out .= "<b>" . $langs->trans("Modules") . ":</b> " . implode(', ', $conf->modules) . "<br>\n";
6891 }
6892
6893 if (is_object($db)) {
6894 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6895 $out .= "<b>" . $langs->trans("DatabaseTypeManager") . ":</b> " . $db->type . "<br>\n";
6896 $lastqueryerror = $db->lastqueryerror();
6897 if (!utf8_check($lastqueryerror)) {
6898 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6899 }
6900 $out .= "<b>" . $langs->trans("RequestLastAccessInError") . ":</b> " . ($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6901 $out .= "<b>" . $langs->trans("ReturnCodeLastAccessInError") . ":</b> " . ($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6902 $out .= "<b>" . $langs->trans("InformationLastAccessInError") . ":</b> " . ($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6903 $out .= "<br>\n";
6904 } else { // Mode CLI
6905 // No dol_escape_htmltag for output, we are in CLI mode
6906 $out .= '> ' . $langs->transnoentities("DatabaseTypeManager") . ":\n" . $db->type . "\n";
6907 $out .= '> ' . $langs->transnoentities("RequestLastAccessInError") . ":\n" . ($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6908 $out .= '> ' . $langs->transnoentities("ReturnCodeLastAccessInError") . ":\n" . ($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6909 $out .= '> ' . $langs->transnoentities("InformationLastAccessInError") . ":\n" . ($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6910 }
6911 $syslog .= ", sql=" . $db->lastquery();
6912 $syslog .= ", db_error=" . $db->lasterror();
6913 }
6914
6915 if ($error || $errors) {
6916 // Merge all into $errors array
6917 if (is_array($error) && is_array($errors)) {
6918 $errors = array_merge($error, $errors);
6919 } elseif (is_array($error)) { // deprecated, use second parameters
6920 $errors = $error;
6921 } elseif (is_array($errors) && !empty($error)) {
6922 $errors = array_merge(array($error), $errors);
6923 } elseif (!empty($error)) {
6924 $errors = array_merge(array($error), array($errors));
6925 }
6926
6927 $langs->load("errors");
6928
6929 foreach ($errors as $msg) {
6930 if (empty($msg)) {
6931 continue;
6932 }
6933 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6934 $out .= "<b>" . $langs->trans("Message") . ":</b> " . dol_escape_htmltag($msg) . "<br>\n";
6935 } else { // Mode CLI
6936 $out .= '> ' . $langs->transnoentities("Message") . ":\n" . $msg . "\n";
6937 }
6938 $syslog .= ", msg=" . $msg;
6939 }
6940 }
6941 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6942 xdebug_print_function_stack();
6943 $out .= '<b>XDebug information:</b>' . "<br>\n";
6944 $out .= 'File: ' . xdebug_call_file() . "<br>\n";
6945 $out .= 'Line: ' . xdebug_call_line() . "<br>\n";
6946 $out .= 'Function: ' . xdebug_call_function() . "<br>\n";
6947 $out .= "<br>\n";
6948 }
6949
6950 // Return a http header with error code if possible
6951 if (!headers_sent()) {
6952 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6953 top_httphead();
6954 }
6955 //http_response_code(500); // If we use 500, message is not output with some command line tools
6956 http_response_code(202); // If we use 202, this is not really an error message, but this allow to output message on command line tools
6957 }
6958
6959 if (empty($dolibarr_main_prod)) {
6960 print $out;
6961 } else {
6962 if (empty($langs->defaultlang)) {
6963 $langs->setDefaultLang();
6964 }
6965 $langs->loadLangs(array("main", "errors")); // Reload main because language may have been set only on previous line so we have to reload files we need.
6966 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6967 print 'This website or feature is currently temporarily not available or failed after a technical error.<br><br>This may be due to a maintenance operation. Current status of operation (' . dol_print_date(dol_now(), 'dayhourrfc') . ') are on next line...<br><br>' . "\n";
6968 print $langs->trans("DolibarrHasDetectedError") . '. ';
6969 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6970 if (!defined("MAIN_CORE_ERROR")) {
6971 define("MAIN_CORE_ERROR", 1);
6972 }
6973 }
6974
6975 dol_syslog("Error " . $syslog, LOG_ERR);
6976}
6977
6988function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6989{
6990 global $langs;
6991
6992 if (empty($email)) {
6993 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6994 }
6995
6996 $langs->load("errors");
6997 $now = dol_now();
6998
6999 print '<br><div class="center login_main_message"><div class="' . $morecss . '">';
7000 print $langs->trans("ErrorContactEMail", $email, $prefixcode . '-' . dol_print_date($now, '%Y%m%d%H%M%S'));
7001 if ($errormessage) {
7002 print '<br><br>' . $errormessage;
7003 }
7004 if (is_array($errormessages) && count($errormessages)) {
7005 foreach ($errormessages as $mesgtoshow) {
7006 print '<br><br>' . $mesgtoshow;
7007 }
7008 }
7009 print '</div></div>';
7010}
7011
7028function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
7029{
7030 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
7031}
7032
7051function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
7052{
7053 global $langs, $form;
7054 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
7055
7056 if ($moreattrib == 'class="right"') {
7057 $prefix .= 'right '; // For backward compatibility
7058 }
7059
7060 $tooltip = (string) $tooltip; // In case $tooltip is null
7061
7062 $sortorder = strtoupper((string) $sortorder);
7063 $out = '';
7064 $sortimg = '';
7065
7066 $tag = 'th';
7067 if ($thead == 2) {
7068 $tag = 'div';
7069 }
7070
7071 $tmpsortfield = explode(',', (string) $sortfield);
7072 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
7073 $tmpfield = explode(',', $field);
7074 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
7075
7076 if (strpos((string) $tooltip, ':') !== false) {
7077 $tmptooltip = explode(':', (string) $tooltip);
7078 } else {
7079 $tmptooltip = array($tooltip);
7080 }
7081
7082 $wrapcolumntitle = (empty($forcenowrapcolumntitle) || (!empty($tmptooltip[2]) && $tmptooltip[2] == '-1'));
7083
7084 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle) {
7085 $prefix = 'wrapcolumntitle ' . $prefix;
7086 }
7087
7088 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
7089 // If field is used as sort criteria we use a specific css class liste_titre_sel
7090 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
7091 $liste_titre = 'liste_titre';
7092 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
7093 $liste_titre = 'liste_titre_sel';
7094 }
7095
7096 $tagstart = '<' . $tag . ' class="' . $prefix . $liste_titre . '" ' . $moreattrib;
7097 //$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:&;]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
7098 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle && !dol_textishtml($name)) ? ' title="' . dolPrintHTMLForAttribute($langs->trans($name)) . '"' : '';
7099 $tagstart .= '>';
7100
7101 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7102 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7103 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7104 $options = preg_replace('/&+/i', '&', $options);
7105 if (!preg_match('/^&/', $options)) {
7106 $options = '&' . $options;
7107 }
7108
7109 $sortordertouseinlink = '';
7110 if ($field1 != $sortfield1) { // We are on another field than current sorted field
7111 if (preg_match('/^DESC/i', $sortorder)) {
7112 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7113 } else { // We reverse the var $sortordertouseinlink
7114 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7115 }
7116 } else { // We are on field that is the first current sorting criteria
7117 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
7118 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7119 } else {
7120 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7121 }
7122 }
7123 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
7124 $out .= '<a class="reposition" href="' . dolBuildUrl($file, ['sortfield' => $field, 'sortorder' => $sortordertouseinlink, 'begin' => $begin]) . $options . '"';
7125 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
7126 $out .= '>';
7127 }
7128 if ($tooltip && $tmptooltip[0]) {
7129 // You can also use 'TranslationString:[keyfortooltiponclick]:[tooltipdirection]' for a tooltip on click or to change tooltip position.
7130 $out .= $form->textwithpicto($langs->trans((string) $name), $langs->trans((string) $tmptooltip[0]), (empty($tmptooltip[2]) ? '1' : $tmptooltip[2]), 'help', ((!empty($tmptooltip[2]) && $tmptooltip[2] == '-1') ? 'paddingrightonly' : ''), 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_' . str_replace('.', '_', $field) . '_' . $tmptooltip[1]));
7131 } else {
7132 $out .= $langs->trans((string) $name);
7133 }
7134
7135 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7136 $out .= '</a>';
7137 }
7138
7139 if (empty($thead) && $field) { // If this is a sort field
7140 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7141 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7142 $options = preg_replace('/&+/i', '&', $options);
7143 if (!preg_match('/^&/', $options)) {
7144 $options = '&' . $options;
7145 }
7146
7147 if (!$sortorder || ($field1 != $sortfield1)) {
7148 // Nothing
7149 } else {
7150 if (preg_match('/^DESC/', $sortorder)) {
7151 $sortimg .= '<span class="nowrap">' . img_up("Z-A", 0, 'paddingright') . '</span>';
7152 }
7153 if (preg_match('/^ASC/', $sortorder)) {
7154 $sortimg .= '<span class="nowrap">' . img_down("A-Z", 0, 'paddingright') . '</span>';
7155 }
7156 }
7157 }
7158
7159 $tagend = '</' . $tag . '>';
7160
7161 $out = $tagstart . $sortimg . $out . $tagend;
7162
7163 return $out;
7164}
7165
7174function print_titre($title)
7175{
7176 dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
7177
7178 print '<div class="titre">' . $title . '</div>';
7179}
7180
7192function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
7193{
7194 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
7195}
7196
7211function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '', $morecssonpicto = 'widthpictotitle')
7212{
7213 $return = '';
7214
7215 if ($picto == 'setup') {
7216 $picto = 'generic';
7217 }
7218
7219 $return .= "\n";
7220 $return .= '<table ' . ($id ? 'id="' . $id . '" ' : '') . 'class="centpercent notopnoleftnoright table-fiche-title' . ($morecssontable ? ' ' . $morecssontable : '') . '">'; // margin bottom must be same than into print_barre_list
7221 $return .= '<tr class="toptitle">';
7222 if ($picto) {
7223 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">' . img_picto('', $picto, 'class="valignmiddle pictotitle'.($morecssonpicto ? ' '.$morecssonpicto : '').'"', $pictoisfullpath) . '</td>';
7224 }
7225 $return .= '<td class="nobordernopadding valignmiddle col-title">';
7226 $return .= '<div class="titre inline-block">';
7227 $return .= '<span class="inline-block valignmiddle">' . $title . '</span>'; // $title is already HTML sanitized content
7228 $return .= '</div>';
7229 $return .= '</td>';
7230 if (dol_strlen($morehtmlcenter)) {
7231 $return .= '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7232 }
7233 if (dol_strlen($morehtmlright)) {
7234 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">' . $morehtmlright . '</td>';
7235 }
7236 $return .= '</tr></table>' . "\n";
7237
7238 return $return;
7239}
7240
7264function print_barre_liste($title, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $selectlimitsuffix = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
7265{
7266 global $conf, $langs;
7267
7268 $savlimit = $limit;
7269 $savtotalnboflines = $totalnboflines;
7270 if (is_numeric($totalnboflines)) {
7271 $totalnboflines = abs($totalnboflines);
7272 }
7273
7274 // Detect if there is a subtitle
7275 $subtitle = '';
7276 $tmparray = preg_split('/<br>/i', $title, 2);
7277 if (!empty($tmparray[1])) {
7278 $title = $tmparray[0];
7279 $subtitle = $tmparray[1];
7280 }
7281
7282 $page = (int) $page;
7283
7284 if ($picto == 'setup') {
7285 $picto = 'title_setup';
7286 }
7287 if (($conf->browser->name == 'ie') && $picto == 'generic') {
7288 $picto = 'title.gif';
7289 }
7290 if ($limit < 0) {
7291 $limit = $conf->liste_limit;
7292 }
7293
7294 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
7295 $nextpage = 1;
7296 } else {
7297 $nextpage = 0;
7298 }
7299 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
7300
7301 print "\n";
7302 print "<!-- Begin print_barre_liste -->\n";
7303 print '<table class="centpercent notopnoleftnoright table-fiche-title' . ($morecss ? ' ' . $morecss : '') . '">';
7304 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
7305
7306 // Left
7307
7308 if ($picto && $title) {
7309 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
7310 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
7311 print '</td>';
7312 }
7313
7314 print '<td class="nobordernopadding valignmiddle col-title">';
7315 print '<div class="titre inline-block">';
7316 print '<span class="inline-block valignmiddle print-barre-liste">' . $title . '</span>'; // $title may contains HTML like a combo list from page consumption.php, so we do not use dolPrintLabel here()
7317 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
7318 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="' . $langs->trans("NbRecordQualified") . '">(' . $totalnboflines . ')</span>';
7319 }
7320 print '</div>';
7321 if (!empty($subtitle)) {
7322 print '<br><div class="subtitle inline-block hideonsmartphone">' . $subtitle . '</div>';
7323 }
7324 print '</td>';
7325
7326 // Center
7327 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
7328 print '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7329 }
7330
7331 // Right
7332 print '<td class="nobordernopadding valignmiddle right col-right">';
7333 print '<input type="hidden" name="pageplusoneold" value="' . ((int) $page + 1) . '">';
7334 $query = [];
7335 parse_str($options, $query);
7336 if ($sortfield) {
7337 $query += ['sortfield' => $sortfield];
7338 }
7339 if ($sortorder) {
7340 $query += ['sortorder' => $sortorder];
7341 }
7342
7343 $options = '&' . http_build_query($query);
7344 if ($page) {
7345 $query = array_merge($query, ['page' => $page]);
7346 }
7347 // Show navigation bar
7348 $pagelist = '';
7349 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
7350 if ($totalnboflines) { // If we know total nb of lines
7351 // Define nb of extra page links before and after selected page + ... + first or last
7352 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
7353
7354 if ($limit > 0) {
7355 $nbpages = ceil($totalnboflines / $limit);
7356 } else {
7357 $nbpages = 1;
7358 }
7359 $cpt = ($page - $maxnbofpage);
7360 if ($cpt < 0) {
7361 $cpt = 0;
7362 }
7363
7364 if ($cpt >= 1) {
7365 if (empty($pagenavastextinput)) {
7366 $query['page'] = 0;
7367 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">1</a></li>';
7368 if ($cpt > 2) {
7369 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7370 } elseif ($cpt == 2) {
7371 $query['page'] = 0;
7372 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">2</a></li>';
7373 }
7374 }
7375 }
7376
7377 do {
7378 if ($pagenavastextinput) {
7379 if ($cpt == $page) {
7380 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="' . ($totalnboflines > 100 ? 'width40' : 'width25') . ' center pageplusone heightofcombo" name="pageplusone" value="' . ($page + 1) . '"></li>';
7381 $pagelist .= '/';
7382 }
7383 } else {
7384 if ($cpt == $page) {
7385 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . '</span></li>';
7386 } else {
7387 $query['page'] = $cpt;
7388 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($cpt + 1) . '</a></li>';
7389 }
7390 }
7391 $cpt++;
7392 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
7393
7394 if (empty($pagenavastextinput)) {
7395 if ($cpt < $nbpages) {
7396 if ($cpt < $nbpages - 2) {
7397 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7398 } elseif ($cpt == $nbpages - 2) {
7399 $query['page'] = ($nbpages - 2);
7400 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($nbpages - 1) . '</a></li>';
7401 }
7402 $query['page'] = ($nbpages - 1);
7403 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7404 }
7405 } else {
7406 $query['page'] = ($nbpages - 1);
7407 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7408 }
7409 } else {
7410 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . "</li>";
7411 }
7412 }
7413
7414 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
7415 print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $selectlimitsuffix, $morehtmlrightbeforearrow, $hidenavigation); // output the div and ul for previous/last completed with page numbers into $pagelist
7416 }
7417
7418 // js to autoselect page field on focus
7419 if ($pagenavastextinput) {
7420 print ajax_autoselect('.pageplusone');
7421 }
7422
7423 print '</td>';
7424 print '</tr>';
7425
7426 print "</table>\n";
7427
7428 // Center
7429 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
7430 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">' . $morehtmlcenter . '</div>';
7431 }
7432
7433 print "<!-- End title -->\n\n";
7434}
7435
7452function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
7453{
7454 global $conf, $langs;
7455
7456 print '<div class="pagination"><ul>';
7457 if ($beforearrows) {
7458 print '<li class="paginationbeforearrows">';
7459 print $beforearrows;
7460 print '</li>';
7461 }
7462
7463 if (empty($hidenavigation)) {
7464 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
7465 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
7466 $pagesizechoices .= ',5000:5000';
7467 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
7468 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
7469 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
7470 //$pagesizechoices .= ',2:2';
7471 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
7472 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
7473 }
7474
7475 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7476 print '<li class="pagination">';
7477 print '<input onfocus="this.value=null;" onchange="this.blur();" class="flat selectlimit nopadding maxwidth75 right pageplusone" id="limit" name="limit" list="limitlist" title="' . dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")) . '" value="' . $limit . '">';
7478 print '<datalist id="limitlist">';
7479 } else {
7480 print '<li class="paginationcombolimit valignmiddle">';
7481 print '<select id="limit' . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix) . '" name="limit" class="flat selectlimit nopadding maxwidth75 center' . (is_numeric($selectlimitsuffix) ? '' : ' ' . $selectlimitsuffix) . '" title="' . dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")) . '">';
7482 }
7483 $tmpchoice = explode(',', $pagesizechoices);
7484 $tmpkey = $limit . ':' . $limit;
7485 if (!in_array($tmpkey, $tmpchoice)) {
7486 $tmpchoice[$tmpkey] = $tmpkey;
7487 }
7488 $tmpkey = $conf->liste_limit . ':' . $conf->liste_limit;
7489 if (!in_array($tmpkey, $tmpchoice)) {
7490 $tmpchoice[$tmpkey] = $tmpkey;
7491 }
7492 asort($tmpchoice, SORT_NUMERIC);
7493 foreach ($tmpchoice as $val) {
7494 $selected = '';
7495 $tmp = explode(':', $val);
7496 $key = $tmp[0];
7497 $val = $tmp[1];
7498 if ($key != '' && $val != '') {
7499 if ((int) $key == (int) $limit) {
7500 $selected = ' selected="selected"';
7501 }
7502 print '<option name="' . $key . '"' . $selected . '>' . dol_escape_htmltag($val) . '</option>' . "\n";
7503 }
7504 }
7505 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7506 print '</datalist>';
7507 } else {
7508 print '</select>';
7509 print ajax_combobox("limit" . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
7510 //print ajax_combobox("limit");
7511 }
7512
7513 if ($conf->use_javascript_ajax) {
7514 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
7515 <script>
7516 jQuery(document).ready(function () {
7517 jQuery(".selectlimit").change(function() {
7518 console.log("We change limit so we submit the form");
7519 $(this).parents(\'form:first\').submit();
7520 });
7521 });
7522 </script>
7523 ';
7524 }
7525 print '</li>';
7526 }
7527 if ($page > 0) {
7528 print '<li class="pagination paginationpage paginationpageleft"><a class="paginationprevious reposition" href="' . $file . '?page=' . ($page - 1) . $options . '"><i class="fa fa-chevron-left" title="' . dol_escape_htmltag($langs->trans("Previous")) . '"></i></a></li>';
7529 }
7530 if ($betweenarrows) {
7531 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7532 print $betweenarrows;
7533 print '<!--</div>-->';
7534 }
7535 if ($nextpage > 0) {
7536 print '<li class="pagination paginationpage paginationpageright"><a class="paginationnext reposition" href="' . $file . '?page=' . ($page + 1) . $options . '"><i class="fa fa-chevron-right" title="' . dol_escape_htmltag($langs->trans("Next")) . '"></i></a></li>';
7537 }
7538 if ($afterarrows) {
7539 print '<li class="paginationafterarrows">';
7540 print $afterarrows;
7541 print '</li>';
7542 }
7543 }
7544 print '</ul></div>' . "\n";
7545}
7546
7547
7559function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7560{
7561 $morelabel = '';
7562
7563 if (preg_match('/%/', $rate)) {
7564 $rate = str_replace('%', '', $rate);
7565 $addpercent = true;
7566 }
7567 $reg = array();
7568 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7569 $morelabel = ' (' . $reg[1] . ')';
7570 $rate = preg_replace('/\s*' . preg_quote($morelabel, '/') . '/', '', $rate);
7571 $morelabel = ' ' . ($html ? '<span class="opacitymedium">' : '') . '(' . $reg[1] . ')' . ($html ? '</span>' : '');
7572 }
7573 if (preg_match('/\*/', $rate)) {
7574 $rate = str_replace('*', '', $rate);
7575 $info_bits |= 1;
7576 }
7577
7578 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7579 if (!preg_match('/\//', $rate)) {
7580 $ret = price($rate, 0, '', 0, 0) . ($addpercent ? '%' : '');
7581 } else {
7582 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7583 $ret = $rate . ($addpercent ? '%' : '');
7584 }
7585 if (($info_bits & 1) && $usestarfornpr >= 0) {
7586 $ret .= ' *';
7587 }
7588 $ret .= $morelabel;
7589 return $ret;
7590}
7591
7592
7608function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7609{
7610 global $langs, $conf;
7611
7612 // Clean parameters
7613 if (empty($amount)) {
7614 $amount = 0; // To have a numeric value if amount not defined or = ''
7615 }
7616 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7617 if ($rounding == -1) {
7618 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7619 }
7620 $nbdecimal = $rounding;
7621
7622 if ($outlangs === 'none') {
7623 // Use international separators
7624 $dec = '.';
7625 $thousand = '';
7626 } else {
7627 // Output separators by default (french)
7628 $dec = ',';
7629 $thousand = ' ';
7630
7631 // If $outlangs not forced, we use use language
7632 if (!($outlangs instanceof Translate)) {
7633 $outlangs = $langs;
7634 }
7635
7636 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7637 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7638 }
7639 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7640 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7641 }
7642 if ($thousand == 'None') {
7643 $thousand = '';
7644 } elseif ($thousand == 'Space') {
7645 $thousand = ' ';
7646 }
7647 }
7648 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7649
7650 //print "amount=".$amount."-";
7651 $amount = str_replace(',', '.', $amount); // should be useless
7652 //print $amount."-";
7653 $data = explode('.', $amount);
7654 $decpart = isset($data[1]) ? $data[1] : '';
7655 $decpart = preg_replace('/0+$/i', '', $decpart); // Remove 0 at end of decimal part
7656 //print "decpart=".$decpart."<br>";
7657 $end = '';
7658
7659 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7660 if (dol_strlen($decpart) > $nbdecimal) {
7661 $nbdecimal = dol_strlen($decpart);
7662 }
7663
7664 // If nbdecimal is higher than max to show
7665 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7666 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7667 $nbdecimal = $nbdecimalmaxshown;
7668 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7669 // If output is truncated, we show ...
7670 $end = '...';
7671 }
7672 }
7673
7674 // If force rounding
7675 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7676 if ($forcerounding === 'MU') {
7677 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7678 } elseif ($forcerounding === 'MT') {
7679 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7680 } elseif ($forcerounding >= 0) {
7681 $nbdecimal = (int) $forcerounding;
7682 }
7683 }
7684
7685 // Format number
7686 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7687 // Add symbol of currency if requested
7688 $cursymbolbefore = $cursymbolafter = '';
7689 if ($currency_code && is_object($outlangs)) {
7690 if ($currency_code == 'auto') {
7691 $currency_code = $conf->currency;
7692 }
7693
7694 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7695 $listoflanguagesbefore = array('nl_NL');
7696 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7697 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7698 } else {
7699 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7700 $cursymbolafter .= ($tmpcur == $currency_code ? ' ' . $tmpcur : $tmpcur);
7701 }
7702 }
7703 $output = $cursymbolbefore . $output . $end . ($cursymbolafter ? ' ' : '') . $cursymbolafter;
7704 if ($form) {
7705 $output = preg_replace('/\s/', '&nbsp;', $output);
7706 $output = preg_replace('/\'/', '&#039;', $output);
7707 }
7708
7709 return $output;
7710}
7711
7737function price2num($amount, $rounding = '', $option = 0)
7738{
7739 global $langs;
7740
7741 // Clean parameters
7742 if (is_null($amount)) {
7743 $amount = '';
7744 }
7745
7746 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7747 // Numbers must be '1234.56'
7748 // Decimal delimiter for PHP and database SQL requests must be '.'
7749 $dec = ',';
7750 $thousand = ' ';
7751 if (is_null($langs)) { // $langs is not defined, we use english values.
7752 $dec = '.';
7753 $thousand = ',';
7754 } else {
7755 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7756 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7757 }
7758 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7759 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7760 }
7761 }
7762 if ($thousand == 'None') {
7763 $thousand = '';
7764 } elseif ($thousand == 'Space') {
7765 $thousand = ' ';
7766 }
7767 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7768
7769 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7770 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7771 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7772 if (!is_numeric($amount)) {
7773 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7774 }
7775
7776 if ($option == 2 && $thousand == '.' && preg_match('/\.(\d\d\d)$/', (string) $amount)) { // It means the . is used as a thousand separator and string come from input data, so 1.123 is 1123
7777 $amount = str_replace($thousand, '', $amount);
7778 }
7779
7780 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7781 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7782 // So if number was already a good number, it is converted into local Dolibarr setup.
7783 if (is_numeric($amount)) {
7784 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7785 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7786 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7787 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7788 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7789 }
7790 //print "QQ".$amount."<br>\n";
7791
7792 // Now make replaceents (the main goal of function)
7793
7794 if ($thousand != ',' && $thousand != '.') {
7795 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7796
7797 // REGEX: Find the integral and decimal parts.
7798 //
7799 // We require that the decimal point only appears once in $amount.
7800 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7801 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7802 // It covers two cases:
7803 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7804 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7805 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7806 $matches = array();
7807 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7808 $intPart = $matches['int'];
7809 $decPart = $matches['dec'];
7810
7811 // Remove all commas and dots from intPart
7812 $intPart = str_replace(['.', ','], '', $intPart);
7813
7814 // Combine intPart and decPart with a dot
7815 $amount = $intPart . $dec . $decPart;
7816 }
7817 }
7818
7819 $amount = str_replace(' ', '', $amount); // To avoid spaces
7820 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7821 $amount = str_replace($dec, '.', $amount);
7822
7823 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7824 }
7825 //print ' XX'.$amount.' '.$rounding;
7826
7827 // Now, $amount is a real PHP float number. We make a rounding if required.
7828 if ($rounding) {
7829 $nbofdectoround = '';
7830 if ($rounding == 'MU') {
7831 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7832 } elseif ($rounding == 'MT') {
7833 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7834 } elseif ($rounding == 'MS') {
7835 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK', 5);
7836 } elseif ($rounding == 'CU') {
7837 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7838 } elseif ($rounding == 'CT') {
7839 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7840 } elseif (is_numeric($rounding)) {
7841 $nbofdectoround = (int) $rounding;
7842 }
7843
7844 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7845 if (dol_strlen($nbofdectoround)) {
7846 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7847 } else {
7848 return 'ErrorBadParameterProvidedToFunction';
7849 }
7850 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7851
7852 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7853 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7854 if (is_numeric($amount)) {
7855 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7856 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7857 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7858 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7859 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7860 }
7861 //print "TT".$amount.'<br>';
7862
7863 // Always make replace because each math function (like round) replace
7864 // with local values and we want a number that has a SQL string format x.y
7865 if ($thousand != ',' && $thousand != '.') {
7866 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7867 }
7868
7869 $amount = str_replace(' ', '', $amount); // To avoid spaces
7870 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7871 $amount = str_replace($dec, '.', $amount);
7872
7873 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7874 }
7875
7876 return $amount;
7877}
7878
7891function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7892{
7893 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
7894
7895 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7896 $dimension *= 1000000;
7897 $unit -= 6;
7898 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7899 $dimension *= 1000;
7900 $unit -= 3;
7901 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7902 $dimension /= 1000000;
7903 $unit += 6;
7904 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7905 $dimension /= 1000;
7906 $unit += 3;
7907 }
7908 // Special case when we want output unit into pound or ounce
7909 /* TODO
7910 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7911 {
7912 $dimension = // convert dimension from standard unit into ounce or pound
7913 $unit = $forceunitoutput;
7914 }
7915 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7916 {
7917 $dimension = // convert dimension from standard unit into ounce or pound
7918 $unit = $forceunitoutput;
7919 }*/
7920
7921 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7922 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7923 $ret .= ' ' . measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
7924
7925 return $ret;
7926}
7927
7928
7941function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7942{
7943 global $db, $conf, $mysoc;
7944
7945 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7946 $thirdparty_seller = $mysoc;
7947 }
7948
7949 dol_syslog("get_localtax tva=" . $vatrate . " local=" . $local . " thirdparty_buyer id=" . (is_object($thirdparty_buyer) ? $thirdparty_buyer->id : '') . "/country_code=" . (is_object($thirdparty_buyer) ? $thirdparty_buyer->country_code : '') . " thirdparty_seller id=" . $thirdparty_seller->id . "/country_code=" . $thirdparty_seller->country_code . " thirdparty_seller localtax1_assuj=" . $thirdparty_seller->localtax1_assuj . " thirdparty_seller localtax2_assuj=" . $thirdparty_seller->localtax2_assuj);
7950
7951 $vatratecleaned = $vatrate;
7952 $reg = array();
7953 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7954 $vatratecleaned = trim($reg[1]);
7955 $vatratecode = $reg[2];
7956 }
7957
7958 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7959 {
7960 return 0;
7961 }*/
7962
7963 // Some test to guess with no need to make database access
7964 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7965 if ($local == 1) {
7966 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7967 return 0;
7968 }
7969 if ($thirdparty_seller->id == $mysoc->id) {
7970 if (!$thirdparty_buyer->localtax1_assuj) {
7971 return 0;
7972 }
7973 } else {
7974 if (!$thirdparty_seller->localtax1_assuj) {
7975 return 0;
7976 }
7977 }
7978 }
7979
7980 if ($local == 2) {
7981 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7982 if (!$mysoc->localtax2_assuj) {
7983 return 0; // If main vat is 0, IRPF may be different than 0.
7984 }
7985 if ($thirdparty_seller->id == $mysoc->id) {
7986 if (!$thirdparty_buyer->localtax2_assuj) {
7987 return 0;
7988 }
7989 } else {
7990 if (!$thirdparty_seller->localtax2_assuj) {
7991 return 0;
7992 }
7993 }
7994 }
7995 } else {
7996 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7997 return 0;
7998 }
7999 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
8000 return 0;
8001 }
8002 }
8003
8004 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
8005 if (in_array($mysoc->country_code, array('ES'))) {
8006 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
8007 }
8008
8009 // Search local taxes
8010 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
8011 if ($local == 1) {
8012 if ($thirdparty_seller != $mysoc) {
8013 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
8014 return $thirdparty_seller->localtax1_value;
8015 }
8016 } else { // i am the seller
8017 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
8018 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
8019 }
8020 }
8021 }
8022 if ($local == 2) {
8023 if ($thirdparty_seller != $mysoc) {
8024 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
8025 // TODO We should also return value defined on thirdparty only if defined
8026 return $thirdparty_seller->localtax2_value;
8027 }
8028 } else { // i am the seller
8029 if (in_array($mysoc->country_code, array('ES'))) {
8030 return $thirdparty_buyer->localtax2_value;
8031 } else {
8032 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
8033 }
8034 }
8035 }
8036 }
8037
8038 // By default, search value of local tax on line of common tax
8039 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
8040 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8041 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdparty_seller->country_code) . "'";
8042 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8043 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8044 if (!empty($vatratecode)) {
8045 $sql .= " AND t.code ='" . $db->escape($vatratecode) . "'"; // If we have the code, we use it in priority
8046 } else {
8047 $sql .= " AND t.recuperableonly = '" . $db->escape((string) $vatnpr) . "'";
8048 }
8049
8050 $resql = $db->query($sql);
8051
8052 if ($resql) {
8053 $obj = $db->fetch_object($resql);
8054 if ($obj) {
8055 if ($local == 1) {
8056 return $obj->localtax1;
8057 } elseif ($local == 2) {
8058 return $obj->localtax2;
8059 }
8060 }
8061 }
8062
8063 return 0;
8064}
8065
8066
8075function isOnlyOneLocalTax($local)
8076{
8077 $tax = get_localtax_by_third($local);
8078
8079 $valors = explode(":", $tax);
8080
8081 if (count($valors) > 1) {
8082 return false;
8083 } else {
8084 return true;
8085 }
8086}
8087
8094function get_localtax_by_third($local)
8095{
8096 global $db, $mysoc;
8097
8098 $sql = " SELECT t.localtax" . $local . " as localtax";
8099 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t INNER JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = t.fk_pays";
8100 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.active = 1 AND t.entity IN (" . getEntity('c_tva') . ") AND t.taux = (";
8101 $sql .= "SELECT MAX(tt.taux) FROM " . MAIN_DB_PREFIX . "c_tva as tt INNER JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = tt.fk_pays";
8102 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.entity IN (" . getEntity('c_tva') . ") AND tt.active = 1)";
8103 $sql .= " AND t.localtax" . $local . "_type <> '0'";
8104 $sql .= " ORDER BY t.rowid DESC";
8105
8106 $resql = $db->query($sql);
8107 if ($resql) {
8108 $obj = $db->fetch_object($resql);
8109 if ($obj) {
8110 return $obj->localtax;
8111 } else {
8112 return '0';
8113 }
8114 }
8115
8116 return 'Error';
8117}
8118
8119
8131function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
8132{
8133 global $db;
8134
8135 dol_syslog("getTaxesFromId vat id or rate = " . $vatrate);
8136
8137 // Search local taxes
8138 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
8139 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
8140 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8141 if ($firstparamisid) {
8142 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8143 } else {
8144 $vatratecleaned = $vatrate;
8145 $vatratecode = '';
8146 $reg = array();
8147 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
8148 $vatratecleaned = $reg[1];
8149 $vatratecode = $reg[2];
8150 }
8151
8152 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8153 /*if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // vat in spain use the buyer country ??
8154 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
8155 $sql .= " WHERE t.fk_pays = c.rowid";
8156 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8157 $sql .= " AND c.code = '" . $db->escape($buyer->country_code) . "'";
8158 } else {
8159 $sql .= " AND c.code = '" . $db->escape($seller->country_code) . "'";
8160 }
8161 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8162 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8163 if ($vatratecode) {
8164 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8165 }
8166 }
8167
8168 $resql = $db->query($sql);
8169 if ($resql) {
8170 $obj = $db->fetch_object($resql);
8171 if ($obj) {
8172 return array(
8173 'rowid' => $obj->rowid,
8174 'code' => $obj->code,
8175 'rate' => $obj->rate,
8176 'localtax1' => $obj->localtax1,
8177 'localtax1_type' => $obj->localtax1_type,
8178 'localtax2' => $obj->localtax2,
8179 'localtax2_type' => $obj->localtax2_type,
8180 'npr' => $obj->npr,
8181 'accountancy_code_sell' => $obj->accountancy_code_sell,
8182 'accountancy_code_buy' => $obj->accountancy_code_buy
8183 );
8184 } else {
8185 return array();
8186 }
8187 } else {
8188 dol_print_error($db);
8189 }
8190
8191 return array();
8192}
8193
8210function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
8211{
8212 global $db, $mysoc;
8213
8214 dol_syslog("getLocalTaxesFromRate vatrate=" . $vatrate . " local=" . $local);
8215
8216 // Search local taxes
8217 $sql = "SELECT t.taux as rate, t.code, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
8218 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8219 if ($firstparamisid) {
8220 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8221 } else {
8222 $vatratecleaned = $vatrate;
8223 $vatratecode = '';
8224 $reg = array();
8225 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
8226 $vatratecleaned = $reg[1];
8227 $vatratecode = $reg[2];
8228 }
8229
8230 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8231 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
8232 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
8233 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'"; // local tax in spain use the buyer country ??
8234 } else {
8235 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
8236 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'";
8237 }
8238 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8239 if ($vatratecode) {
8240 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8241 }
8242 }
8243
8244 $resql = $db->query($sql);
8245 if ($resql) {
8246 $obj = $db->fetch_object($resql);
8247
8248 if ($obj) {
8249 $vateratestring = $obj->rate . ($obj->code ? ' (' . $obj->code . ')' : '');
8250
8251 if ($local == 1) {
8252 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8253 } elseif ($local == 2) {
8254 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8255 } else {
8256 return array($obj->localtax1_type, get_localtax($vateratestring, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vateratestring, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8257 }
8258 }
8259 }
8260
8261 return array();
8262}
8263
8274function get_product_vat_for_country($idprod, $thirdpartytouseforcountry, $idprodfournprice = 0)
8275{
8276 global $db, $mysoc;
8277
8278 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8279
8280 $ret = 0;
8281 $found = 0;
8282
8283 if ($idprod > 0) {
8284 // Load product
8285 $product = new Product($db);
8286 $product->fetch($idprod);
8287
8288 if (($mysoc->country_code == $thirdpartytouseforcountry->country_code)
8289 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouseforcountry->country_code, array('FR', 'MC')))
8290 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouseforcountry->country_code, array('MQ', 'GP')))
8291 ) {
8292 // If country of thirdparty to consider is ours
8293 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
8294 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
8295 if ($result > 0) {
8296 $ret = $product->vatrate_supplier;
8297 if ($product->default_vat_code_supplier) {
8298 $ret .= ' (' . $product->default_vat_code_supplier . ')';
8299 }
8300 $found = 1;
8301 }
8302 }
8303 if (!$found) {
8304 $ret = $product->tva_tx; // Default sales vat of product
8305 if ($product->default_vat_code) {
8306 $ret .= ' (' . $product->default_vat_code . ')';
8307 }
8308 $found = 1;
8309 }
8310 } else {
8311 // TODO Read default product vat according to product and an other countrycode.
8312 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8313 }
8314 }
8315
8316 if (!$found) {
8317 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8318 // If vat of product for the country not found or not defined, we return the first rate found (sorting on use_default, then on higher vat of country).
8319 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
8320 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8321 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8322 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8323 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8324 $sql .= $db->plimit(1);
8325
8326 $resql = $db->query($sql);
8327 if ($resql) {
8328 $obj = $db->fetch_object($resql);
8329 if ($obj) {
8330 $ret = $obj->vat_rate;
8331 if ($obj->default_vat_code) {
8332 $ret .= ' (' . $obj->default_vat_code . ')';
8333 }
8334 }
8335 $db->free($resql);
8336 } else {
8337 dol_print_error($db);
8338 }
8339 } else {
8340 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
8341 // '1.23'
8342 // or '1.23 (CODE)'
8343 $defaulttx = '';
8344 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8345 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8346 }
8347 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8348 $defaultcode = $reg[1];
8349 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8350 }*/
8351
8352 $ret = $defaulttx;
8353 }
8354 }
8355
8356 dol_syslog("get_product_vat_for_country: ret=" . $ret);
8357
8358 return $ret;
8359}
8360
8370function get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
8371{
8372 global $db, $mysoc;
8373
8374 if (!class_exists('Product')) {
8375 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8376 }
8377
8378 $ret = 0;
8379 $found = 0;
8380
8381 if ($idprod > 0) {
8382 // Load product
8383 $product = new Product($db);
8384 $result = $product->fetch($idprod);
8385
8386 if ($mysoc->country_code == $thirdpartytouseforcountry->country_code) { // If selling country is ours
8387 /* Not defined yet, so we don't use this
8388 if ($local==1) $ret=$product->localtax1_tx;
8389 elseif ($local==2) $ret=$product->localtax2_tx;
8390 $found=1;
8391 */
8392 } else {
8393 // TODO Read default product vat according to product and another countrycode.
8394 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8395 }
8396 }
8397
8398 if (!$found) {
8399 // If vat of product for the country not found or not defined, we return higher vat of country.
8400 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
8401 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8402 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8403 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8404 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
8405 $sql .= $db->plimit(1);
8406
8407 $resql = $db->query($sql);
8408 if ($resql) {
8409 $obj = $db->fetch_object($resql);
8410 if ($obj) {
8411 if ($local == 1) {
8412 $ret = $obj->localtax1;
8413 } elseif ($local == 2) {
8414 $ret = $obj->localtax2;
8415 }
8416 }
8417 } else {
8418 dol_print_error($db);
8419 }
8420 }
8421
8422 dol_syslog("get_product_localtax_for_country: ret=" . $ret);
8423 return $ret;
8424}
8425
8444function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8445{
8446 global $mysoc, $db, $hookmanager;
8447
8448 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8449
8450 // Note: possible values for tva_assuj are 0/1 or franchise/reel
8451 $seller_use_vat = ((is_numeric($thirdparty_seller->tva_assuj) && !$thirdparty_seller->tva_assuj) || (!is_numeric($thirdparty_seller->tva_assuj) && $thirdparty_seller->tva_assuj == 'franchise')) ? 0 : 1;
8452
8453 if (empty($thirdparty_seller->country_code)) {
8454 $thirdparty_seller->country_code = $mysoc->country_code;
8455 }
8456 $seller_country_code = $thirdparty_seller->country_code;
8457 $seller_in_cee = isInEEC($thirdparty_seller);
8458
8459 if (empty($thirdparty_buyer->country_code)) {
8460 $thirdparty_buyer->country_code = $mysoc->country_code;
8461 }
8462 $buyer_country_code = $thirdparty_buyer->country_code;
8463 $buyer_in_cee = isInEEC($thirdparty_buyer);
8464
8465 dol_syslog("get_default_tva: seller use vat=" . $seller_use_vat . ", seller country=" . $seller_country_code . ", seller in cee=" . ((string) (int) $seller_in_cee) . ", buyer vat number=" . $thirdparty_buyer->tva_intra . " buyer country=" . $buyer_country_code . ", buyer state=" . $thirdparty_buyer->state_id . " buyer in cee=" . ((string) (int) $buyer_in_cee) . ", idprod=" . $idprod . ", idprodfournprice=" . $idprodfournprice . ", SERVICE_ARE_ECOMMERCE_200238EC=" . getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC'));
8466
8467 $vatvalue = 0;
8468 $vatrule = '';
8469
8470 // If services are eServices according to EU Council Directive 2002/38/EC (http://ec.europa.eu/taxation_customs/taxation/vat/traders/e-commerce/article_1610_en.htm)
8471 // we use the buyer VAT.
8472 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8473 if ($seller_in_cee && $buyer_in_cee) {
8474 $isacompany = $thirdparty_buyer->isACompany();
8475 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8476 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8477 if (!isValidVATID($thirdparty_buyer)) {
8478 $isacompany = 0;
8479 }
8480 }
8481
8482 if (!$isacompany) {
8483 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
8484 $vatrule = 'VATRULE 0';
8485 }
8486 }
8487 }
8488
8489 // If seller does not use VAT, default VAT is 0. End of rule.
8490 if (empty($vatrule) && !$seller_use_vat) {
8491 //print 'VATRULE 1';
8492 // TODO get the VAT Code of exemption asked into setup if country isInEEC (from an array list of possible
8493 // values like VATEX-EU-132-*, VATEX-FR-FRANCHISE, VATEX-EU-AE...
8494 // When we had recorded it, we also added a corresponding entry into table of vat code if it does not exists yet.
8495 // Here we test if entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-132-xx)'
8496 // If not, we add it and we return '0 (VATEX-EU-132-xx)'
8497 $vatvalue = 0;
8498 $vatrule = 'VATRULE 1';
8499 }
8500
8501 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
8502 if (empty($vatrule) && !empty($thirdparty_buyer->state_id)) {
8503 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
8504 $sql .= " FROM " . $db->prefix() . "c_tva as t";
8505 $sql .= " INNER JOIN " . $db->prefix() . "c_departements as d ON t.fk_department_buyer = d.rowid";
8506 $sql .= " WHERE d.rowid = " . ((int) $thirdparty_buyer->state_id);
8507 $sql .= " AND t.active > 0";
8508 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
8509 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8510
8511 $res = $db->query($sql);
8512 if ($res) {
8513 if ($db->num_rows($res)) {
8514 $obj = $db->fetch_object($res);
8515
8516 $vatvalue = $obj->vat_default_rate . ' (' . $obj->vat_default_code . ')';
8517 $vatrule = 'VATRULE 2';
8518 }
8519 $db->free($res);
8520 }
8521 }
8522
8523 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
8524 if (empty($vatrule) && (
8525 ($seller_country_code == $buyer_country_code)
8526 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
8527 || (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.
8528 )) { // Warning ->country_code not always defined
8529 //print 'VATRULE 3';
8530 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8531
8532 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
8533 // Special case for india.
8534 //print 'VATRULE 3b';
8535 $reg = array();
8536 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
8537 // we must revert the C+S into I
8538 $tmpvat = str_replace("C+S", "I", $tmpvat);
8539 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8540 // we must revert the I into C+S
8541 $tmpvat = str_replace("I", "C+S", $tmpvat);
8542 }
8543 }
8544
8545 $vatvalue = $tmpvat;
8546 $vatrule = 'VATRULE 3b';
8547 }
8548
8549 // 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.
8550 // 'VATRULE 4' - Not supported
8551
8552 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8553 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8554 if (empty($vatrule) && ($seller_in_cee && $buyer_in_cee)) {
8555 $isacompany = $thirdparty_buyer->isACompany();
8556 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8557 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8558 if (!isValidVATID($thirdparty_buyer)) {
8559 $isacompany = 0;
8560 }
8561 }
8562
8563 if (!$isacompany) {
8564 //print 'VATRULE 5';
8565 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8566 $vatrule = 'VATRULE 5';
8567 } else {
8568 //print 'VATRULE 6';
8569 // TODO This is the case of VAT exemption 'VATEX-EU-IC'
8570 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-IC)'
8571 // If not, we add it and we return '0 (VATEX-EU-IC)'
8572 $vatvalue = 0;
8573 $vatrule = 'VATRULE 6';
8574 }
8575 }
8576
8577 // 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
8578 // 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
8579 if (empty($vatrule) && getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8580 $isacompany = $thirdparty_buyer->isACompany();
8581 if (!$isacompany) {
8582 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8583 $vatrule = 'VATRULE extra';
8584 //print 'VATRULE extra';
8585 }
8586 }
8587
8588 // Otherwise the VAT proposed by default=0. End of rule.
8589 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8590 //print 'VATRULE 7';
8591 // TODO This is the case of VAT exemption 'VATEX-EU-G'
8592 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-xxx)'
8593 // If not, we add it and we return '0 (VATEX-xxx)'
8594
8595 // Allow an external module to bypass the calculation of prices
8596 $parameters = array('vatvalue' => $vatvalue, 'vatrule' => $vatrule);
8597 $tmpobject = null;
8598 $tmpaction = '';
8599 // @phan-suppress-next-line PhanPluginConstantVariableNull
8600 $reshook = $hookmanager->executeHooks('get_default_tva', $parameters, $tmpobject, $tmpaction); // @phan-suppress-current-line PhanPluginConstantVariableNull
8601 if ($reshook > 0 && !empty($hookmanager->resArray['vatvalue'])) {
8602 $vatvalue = $hookmanager->resArray['vatvalue'];
8603 $vatrule = $hookmanager->resArray['vatrule']; // For information
8604 }
8605
8606 return $vatvalue;
8607}
8608
8609
8620function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8621{
8622 global $db;
8623
8624 if ($idprodfournprice > 0) {
8625 if (!class_exists('ProductFournisseur')) {
8626 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
8627 }
8628 $prodprice = new ProductFournisseur($db);
8629 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8630 return $prodprice->fourn_tva_npr;
8631 } elseif ($idprod > 0) {
8632 if (!class_exists('Product')) {
8633 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8634 }
8635 $prod = new Product($db);
8636 $prod->fetch($idprod);
8637 return $prod->tva_npr;
8638 }
8639
8640 return 0;
8641}
8642
8656function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8657{
8658 global $mysoc;
8659
8660 if (!is_object($thirdparty_seller)) {
8661 return -1;
8662 }
8663 if (!is_object($thirdparty_buyer)) {
8664 return -1;
8665 }
8666
8667 if (empty($thirdparty_seller->country_code)) {
8668 $thirdparty_seller->country_code = $mysoc->country_code;
8669 }
8670 $seller_country_code = $thirdparty_seller->country_code;
8671 //$seller_in_cee = isInEEC($thirdparty_seller);
8672
8673 if (empty($thirdparty_buyer->country_code)) {
8674 $thirdparty_buyer->country_code = $mysoc->country_code;
8675 }
8676 $buyer_country_code = $thirdparty_buyer->country_code;
8677 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8678
8679 if ($local == 1) { // Localtax 1
8680 if ($mysoc->country_code == 'ES') {
8681 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8682 return 0;
8683 }
8684 } else {
8685 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8686 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8687 return 0;
8688 }
8689 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8690 return 0;
8691 }
8692 }
8693 } elseif ($local == 2) { //I Localtax 2
8694 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8695 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8696 return 0;
8697 }
8698 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8699 return 0;
8700 }
8701 }
8702
8703 if ($seller_country_code == $buyer_country_code) {
8704 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8705 }
8706
8707 return 0;
8708}
8709
8718function yn($yesno, $format = 1, $color = 0)
8719{
8720 global $langs;
8721
8722 $result = 'unknown';
8723 $classname = '';
8724 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'
8725 $result = $langs->trans('yes');
8726 if ($format == 1 || $format == 3) {
8727 $result = $langs->trans("Yes");
8728 }
8729 if ($format == 2) {
8730 $result = '<input type="checkbox" value="1" checked disabled>';
8731 }
8732 if ($format == 3) {
8733 $result = '<input type="checkbox" value="1" checked disabled> ' . $result;
8734 }
8735 if ($format == 4 || !is_numeric($format)) {
8736 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8737 }
8738
8739 $classname = 'ok';
8740 } else {
8741 $result = $langs->trans("no");
8742 if ($format == 1 || $format == 3) {
8743 $result = $langs->trans("No");
8744 }
8745 if ($format == 2) {
8746 $result = '<input type="checkbox" value="0" disabled>';
8747 }
8748 if ($format == 3) {
8749 $result = '<input type="checkbox" value="0" disabled> ' . $result;
8750 }
8751 if ($format == 4 || !is_numeric($format)) {
8752 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8753 }
8754
8755 if ($color == 2) {
8756 $classname = 'ok';
8757 } else {
8758 $classname = 'error';
8759 }
8760 }
8761 if ($color) {
8762 return '<span class="' . $classname . '">' . $result . '</span>';
8763 }
8764 return $result;
8765}
8766
8785function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8786{
8787 if (empty($modulepart) && is_object($object)) {
8788 if (!empty($object->module)) {
8789 $modulepart = $object->module;
8790 } elseif (!empty($object->element)) {
8791 $modulepart = $object->element;
8792 }
8793 }
8794
8795 $path = '';
8796
8797 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8798 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8799 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8800 $arrayforoldpath['product'] = 2;
8801 }
8802
8803 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8804 $level = $arrayforoldpath[$modulepart];
8805 }
8806 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8807 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8808 if (empty($num) && is_object($object)) {
8809 $num = $object->id;
8810 }
8811 if (empty($alpha)) {
8812 $num = preg_replace('/([^0-9])/i', '', $num);
8813 } else {
8814 $num = preg_replace('/^.*\-/i', '', $num);
8815 }
8816 $num = substr("000" . $num, -$level);
8817 if ($level == 1) {
8818 $path = substr($num, 0, 1);
8819 }
8820 if ($level == 2) {
8821 $path = substr($num, 1, 1) . '/' . substr($num, 0, 1);
8822 }
8823 if ($level == 3) {
8824 $path = substr($num, 2, 1) . '/' . substr($num, 1, 1) . '/' . substr($num, 0, 1);
8825 }
8826 } else {
8827 // We will enhance here a common way of forging path for document storage.
8828 // In a future, we may distribute directories on several levels depending on setup and object.
8829 // Here, $object->id, $object->ref and $modulepart are required.
8830 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceof Societe) {
8831 // 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
8832 $path = dol_sanitizeFileName((string) $object->id);
8833 } else {
8834 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8835 }
8836 }
8837
8838 if (empty($withoutslash) && !empty($path)) {
8839 $path .= '/';
8840 }
8841
8842 return $path;
8843}
8844
8853function dol_mkdir($dir, $dataroot = '', $newmask = '')
8854{
8855 dol_syslog("functions.lib::dol_mkdir: dir=" . $dir, LOG_INFO);
8856
8857 $dir = dol_sanitizePathName($dir, '_', 0);
8858
8859 $dir_osencoded = dol_osencode($dir);
8860 if (@is_dir($dir_osencoded)) {
8861 return 0;
8862 }
8863
8864 $nberr = 0;
8865 $nbcreated = 0;
8866
8867 $ccdir = '';
8868 if (!empty($dataroot)) {
8869 // Remove data root from loop
8870 $dir = str_replace($dataroot . '/', '', $dir);
8871 $ccdir = $dataroot . '/';
8872 }
8873
8874 $cdir = explode("/", $dir);
8875 $num = count($cdir);
8876 for ($i = 0; $i < $num; $i++) {
8877 if ($i > 0) {
8878 $ccdir .= '/' . $cdir[$i];
8879 } else {
8880 $ccdir .= $cdir[$i];
8881 }
8882 $regs = array();
8883 if (preg_match("/^.:$/", $ccdir, $regs)) {
8884 continue; // If the Windows path is incomplete, continue with next directory
8885 }
8886
8887 // Attention, is_dir() can fail event if the directory exists
8888 // (i.e. according the open_basedir configuration)
8889 if ($ccdir) {
8890 $ccdir_osencoded = dol_osencode($ccdir);
8891 if (!@is_dir($ccdir_osencoded)) {
8892 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8893
8894 umask(0);
8895 $dirmaskdec = octdec((string) $newmask);
8896 if (empty($newmask)) {
8897 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8898 }
8899 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8900 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8901 // If the is_dir has returned a false information, we arrive here
8902 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '" . $ccdir . "' (no permission to write into parent or directory already exists).", LOG_WARNING);
8903 $nberr++;
8904 } else {
8905 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' created", LOG_DEBUG);
8906 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8907 $nbcreated++;
8908 }
8909 } else {
8910 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8911 }
8912 }
8913 }
8914 return ($nberr ? -$nberr : $nbcreated);
8915}
8916
8917
8925function dolChmod($filepath, $newmask = '')
8926{
8927 if (!empty($newmask)) {
8928 @chmod($filepath, octdec($newmask));
8929 } elseif (getDolGlobalString('MAIN_UMASK')) {
8930 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8931 }
8932}
8933
8934
8940function picto_required()
8941{
8942 return '<span class="fieldrequired">*</span>';
8943}
8944
8945
8962function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8963{
8964 if (is_null($stringtoclean)) {
8965 return '';
8966 }
8967
8968 if ($removelinefeed == 2) {
8969 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8970 }
8971 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8972
8973 // 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)
8974 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8975
8976 $temp = str_replace('< ', '__ltspace__', $temp);
8977 $temp = str_replace('<:', '__lttwopoints__', $temp);
8978
8979 if ($strip_tags) {
8980 $temp = strip_tags($temp);
8981 } else {
8982 // 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).
8983 $pattern = "/<[^<>]+>/";
8984 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8985 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8986 // pass 2 - $temp after pass 2: 0000-021
8987 $tempbis = $temp;
8988 do {
8989 $temp = $tempbis;
8990 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8991 $tempbis = preg_replace($pattern, '', $tempbis);
8992 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8993 } while ($tempbis != $temp);
8994
8995 $temp = $tempbis;
8996
8997 // 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).
8998 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8999 }
9000
9001 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
9002
9003 // Remove also carriage returns
9004 if ($removelinefeed == 1) {
9005 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
9006 }
9007
9008 // And double spaces
9009 if ($removedoublespaces) {
9010 while (strpos($temp, " ") !== false) {
9011 $temp = str_replace(" ", " ", $temp);
9012 }
9013 }
9014
9015 $temp = str_replace('__ltspace__', '< ', $temp);
9016 $temp = str_replace('__lttwopoints__', '<:', $temp);
9017
9018 return trim($temp);
9019}
9020
9040function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
9041{
9042 if (empty($allowed_tags)) {
9043 $allowed_tags = array(
9044 // HTML 4
9045 "html",
9046 "head",
9047 "body",
9048 "article",
9049 "a",
9050 "abbr",
9051 "b",
9052 "blockquote",
9053 "br",
9054 "cite",
9055 "div",
9056 "dl",
9057 "dd",
9058 "dt",
9059 "em",
9060 "font",
9061 "img",
9062 "ins",
9063 "hr",
9064 "i",
9065 "li",
9066 "ol",
9067 "p",
9068 "q",
9069 "s",
9070 "span",
9071 "strike",
9072 "strong",
9073 "title",
9074 "table",
9075 "tr",
9076 "th",
9077 "td",
9078 "u",
9079 "ul",
9080 "sup",
9081 "sub",
9082 "blockquote",
9083 "pre",
9084 "h1",
9085 "h2",
9086 "h3",
9087 "h4",
9088 "h5",
9089 "h6",
9090
9091 // HTML 5
9092 "footer",
9093 "header",
9094 "menu",
9095 "menuitem",
9096 "nav",
9097 "section"
9098 );
9099 }
9100 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
9101 if ($allowiframe) {
9102 if (!in_array('iframe', $allowed_tags)) {
9103 $allowed_tags[] = "iframe";
9104 }
9105 }
9106 if ($allowlink) {
9107 if (!in_array('link', $allowed_tags)) {
9108 $allowed_tags[] = "link";
9109 }
9110 if (!in_array('meta', $allowed_tags)) {
9111 $allowed_tags[] = "meta";
9112 }
9113 }
9114 if ($allowscript) {
9115 if (!in_array('script', $allowed_tags)) {
9116 $allowed_tags[] = "script";
9117 }
9118 }
9119 if ($allowstyle) {
9120 if (!in_array('style', $allowed_tags)) {
9121 $allowed_tags[] = "style";
9122 }
9123 }
9124
9125 $allowed_tags_string = implode("><", $allowed_tags);
9126 $allowed_tags_string = '<' . $allowed_tags_string . '>';
9127
9128 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
9129
9130 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
9131
9132 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
9133 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
9134
9135 if ($allowphp) {
9136 $allowed_tags[] = "commentphp";
9137 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
9138 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
9139 }
9140
9141 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
9142 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
9143
9144 // Remove all HTML tags
9145 $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
9146
9147 if ($cleanalsosomestyles) { // Clean for remaining html tags
9148 $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
9149 }
9150 if ($removeclassattribute) { // Clean for remaining html tags
9151 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
9152 }
9153
9154 // Remove 'javascript:' that we should not find into a text
9155 // 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)).
9156 if ($cleanalsojavascript) {
9157 $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);
9158 }
9159
9160 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
9161
9162 if ($allowphp) {
9163 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
9164 }
9165
9166 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
9167
9168
9169 return $temp;
9170}
9171
9172
9185function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
9186{
9187 if (is_null($allowed_attributes)) {
9188 $allowed_attributes = array(
9189 // HTML 4
9190 "allow",
9191 "allowfullscreen",
9192 "alt",
9193 "async",
9194 "class",
9195 "contenteditable",
9196 "crossorigin",
9197 "data-html",
9198 "frameborder",
9199 "height",
9200 "href",
9201 "id",
9202 "name",
9203 "property",
9204 "rel",
9205 "src",
9206 "style",
9207 "target",
9208 "title",
9209 "type",
9210 "width",
9211
9212 // HTML5
9213 "footer",
9214 "header",
9215 "menu",
9216 "menuitem",
9217 "nav",
9218 "section"
9219 );
9220 }
9221 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
9222 if (!in_array("content", $allowed_attributes)) {
9223 $allowed_attributes[] = "content";
9224 }
9225 if (!in_array("http-equiv", $allowed_attributes)) {
9226 $allowed_attributes[] = "http-equiv";
9227 }
9228
9229 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
9230 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
9231 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . $stringtoclean . '</body></html>';
9232
9233 // Warning: loadHTML does not support HTML5 on old libxml versions.
9234 $dom = new DOMDocument('', 'UTF-8');
9235 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
9236 $savwarning = error_reporting();
9237 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
9238 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
9239 error_reporting($savwarning);
9240
9241 if ($dom instanceof DOMDocument) {
9242 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
9243 $el = $els->item($i);
9244 if (!$el instanceof DOMElement) {
9245 continue;
9246 }
9247 $attrs = $el->attributes;
9248 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
9249 //var_dump($attrs->item($ii));
9250 if (!empty($attrs->item($ii)->name)) {
9251 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
9252 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
9253 $els->item($i)->removeAttribute($attrs->item($ii)->name);
9254 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
9255 // If attribute is 'style'
9256 $valuetoclean = $attrs->item($ii)->value;
9257
9258 if (isset($valuetoclean)) {
9259 do {
9260 $oldvaluetoclean = $valuetoclean;
9261 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
9262 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
9263 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
9264 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
9265 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
9266 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
9267 }
9268
9269 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
9270 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
9271 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
9272 } while ($oldvaluetoclean != $valuetoclean);
9273 }
9274
9275 $attrs->item($ii)->value = $valuetoclean;
9276 }
9277 }
9278 }
9279 }
9280 }
9281
9282 $dom->encoding = 'UTF-8';
9283
9284 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
9285 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
9286
9287 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
9288 $return = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]*' . preg_quote('></head><body>', '/') . '/', '', $return);
9289 $return = preg_replace('/' . preg_quote('</body></html>', '/') . '$/', '', trim($return));
9290
9291 return trim($return);
9292 } else {
9293 return $stringtoclean;
9294 }
9295}
9296
9308function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
9309{
9310 $temp = $stringtoclean;
9311 foreach ($disallowed_tags as $tagtoremove) {
9312 $temp = preg_replace('/<\/?' . $tagtoremove . '>/', '', $temp);
9313 $temp = preg_replace('/<\/?' . $tagtoremove . '\s+[^>]*>/', '', $temp);
9314 }
9315
9316 if ($cleanalsosomestyles) {
9317 $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
9318 }
9319
9320 return $temp;
9321}
9322
9323
9333function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
9334{
9335 if ($nboflines == 1) {
9336 if (dol_textishtml($text)) {
9337 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
9338 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
9339 } else {
9340 if (isset($text)) {
9341 $firstline = preg_replace('/[\n\r].*/', '', $text);
9342 } else {
9343 $firstline = '';
9344 }
9345 }
9346 return $firstline . (isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
9347 } else {
9348 $ishtml = 0;
9349 if (dol_textishtml($text)) {
9350 $text = preg_replace('/\n/', '', $text);
9351 $ishtml = 1;
9352 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9353 } else {
9354 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9355 }
9356
9357 $text = strtr($text, $repTable);
9358 if ($charset == 'UTF-8') {
9359 $pattern = '/(<br[^>]*>)/Uu';
9360 } else {
9361 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9362 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9363 }
9364 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9365
9366 $firstline = '';
9367 $i = 0;
9368 $countline = 0;
9369 $lastaddediscontent = 1;
9370 while ($countline < $nboflines && isset($a[$i])) {
9371 if (preg_match('/<br[^>]*>/', $a[$i])) {
9372 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
9373 $firstline .= ($ishtml ? "<br>\n" : "\n");
9374 // Is it a br for a new line of after a printed line ?
9375 if (!$lastaddediscontent) {
9376 $countline++;
9377 }
9378 $lastaddediscontent = 0;
9379 }
9380 } else {
9381 $firstline .= $a[$i];
9382 $lastaddediscontent = 1;
9383 $countline++;
9384 }
9385 $i++;
9386 }
9387
9388 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
9389 //unset($a);
9390 $ret = $firstline . ($adddots ? '...' : '');
9391 //exit;
9392 return $ret;
9393 }
9394}
9395
9396
9408function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
9409{
9410 if (is_null($stringtoencode)) {
9411 return '';
9412 }
9413
9414 if (!$nl2brmode) {
9415 return nl2br($stringtoencode, $forxml);
9416 } else {
9417 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
9418 return $ret;
9419 }
9420}
9421
9431function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
9432{
9433 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
9434 // TODO using sandbox on inline html content is not possible yet with current browsers
9435 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
9436 //$s .= $stringtoencode;
9437 //$s .= '</body></html></iframe>';
9438 return $stringtoencode;
9439 } else {
9440 $out = $stringtoencode;
9441
9442 // First clean HTML content
9443 do {
9444 $oldstringtoclean = $out;
9445
9446 $outishtml = 0;
9447 if (dol_textishtml($out)) {
9448 $outishtml = 1;
9449 }
9450
9451 // HTML sanitizer by DOMDocument
9452 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
9453 try {
9454 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
9455 if (LIBXML_VERSION < 20900) {
9456 // Avoid load of external entities (security problem).
9457 // Required only if LIBXML_VERSION < 20900
9458 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
9459 libxml_disable_entity_loader(true);
9460 }
9461
9462 $dom = new DOMDocument();
9463 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
9464 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
9465 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
9466 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
9467 // I don't know what the xml encoding is the trick for
9468 if ($outishtml) {
9469 //$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>';
9470 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">' . $out . '</div></body></html>';
9471 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
9472 } else {
9473 //$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>';
9474 $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>';
9475 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
9476 }
9477
9478 // Note: <a href="https://__[aaa]__/aaa.html"> is transformed into <a href="https://__[aaa]__/aaa.html">
9479 // We don't want that, so we protect __[xxx]__ by replacing [ and ] before loadHTML and restore them after saveHTML
9480 $out = preg_replace_callback(
9481 '/__\[([0-9a-zA-Z_]+)\]__/',
9486 function ($m) {
9487 return '__BRACKETSTART' . $m[1] . 'BRACKETEND__';
9488 },
9489 $out
9490 );
9491
9492 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
9493
9494 $dom->encoding = 'UTF-8';
9495
9496 $out = trim($dom->saveHTML());
9497
9498 // Restore [ and ] that were protected before loadHTML
9499 $out = preg_replace_callback(
9500 '/__BRACKETSTART([0-9a-zA-Z_]+)BRACKETEND__/',
9505 function ($m) {
9506 return '__[' . $m[1] . ']__';
9507 },
9508 $out
9509 );
9510
9511 // Remove the trick added to solve pb with text in utf8 and text without parent tag
9512 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
9513 $out = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]+' . preg_quote('></head><body><div class="tricktoremove">', '/') . '/', '', $out);
9514 $out = preg_replace('/' . preg_quote('</div></body></html>', '/') . '$/', '', trim($out));
9515 //$out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
9516 //$out = preg_replace('/<\/div>$/', '', $out);
9517
9518 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
9519 $out = str_replace('<br>', '', $out);
9520 }
9521 } catch (Exception $e) {
9522 // If error, invalid HTML string with no way to clean it
9523 //print $e->getMessage();
9524 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9525 }
9526 }
9527
9528 // HTML sanitizer by Tidy
9529 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
9530 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
9531 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
9532 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
9533 try {
9534 //var_dump($out);
9535
9536 // Try cleaning using tidy
9537 if (extension_loaded('tidy') && class_exists("tidy")) {
9538 //print "aaa".$out."\n";
9539
9540 // See options at https://tidy.sourceforge.net/docs/quickref.html
9541 $config = array(
9542 'clean' => false,
9543 // 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;
9544 'quote-marks' => false,
9545 'doctype' => 'strict',
9546 'show-body-only' => true,
9547 "indent-attributes" => false,
9548 "vertical-space" => false,
9549 //'ident' => false, // Not always supported
9550 "wrap" => 0,
9551 'preserve-entities' => true
9552 // HTML5 tags
9553 //'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',
9554 //'new-blocklevel-tags' => 'footer header section menu menuitem'
9555 //'new-empty-tags' => 'command embed keygen source track wbr',
9556 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
9557 );
9558
9559 // Tidy
9560 $tidy = new tidy();
9561 $out = $tidy->repairString($out, $config, 'utf8');
9562
9563 //print "xxx".$out;exit;
9564 }
9565
9566 //var_dump($out);
9567 } catch (Exception $e) {
9568 // If error, invalid HTML string with no way to clean it
9569 //print $e->getMessage();
9570 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9571 }
9572 }
9573
9574 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
9575 // TODO $out = preg_replace('/[\x{2000}-\x{200D}\x{FEFF}]/u', ' ', $out);
9576 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
9577
9578 // Clean some html entities that are useless so text is cleaner
9579 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
9580
9581 // Ckeditor uses the numeric entity for apostrophe, so we force it to
9582 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
9583 $out = preg_replace('/&#39;/i', '&apos;', $out);
9584
9585 // 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).
9586 // 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
9587 // using a non conventionnal way to be encoded, to not have them sanitized just after)
9588 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
9589 $out = preg_replace_callback(
9590 '/&#(x?[0-9][0-9a-f]+;?)/i',
9595 static function ($m) {
9596 return realCharForNumericEntities($m);
9597 },
9598 $out
9599 );
9600 }
9601
9602 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
9603 $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'.
9604
9605 // Keep only some html tags and remove also some 'javascript:' strings
9606 if ($check == 'restricthtmlallowlinkscript') {
9607 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
9608 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
9609 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
9610 } elseif ($check == 'restricthtmlallowiframe') {
9611 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
9612 } else {
9613 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
9614 }
9615
9616 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
9617 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
9619 }
9620
9621 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
9622 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
9623 $out = preg_replace('/&apos;/i', "&#39;", $out);
9624
9625 // Now remove js
9626 // 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
9627 $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)>
9628 $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);
9629 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
9630 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
9631 $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);
9632 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
9633 // More not into the previous list
9634 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
9635 } while ($oldstringtoclean != $out);
9636
9637 // Check the limit of external links that are automatically executed in a Rich text content. We count:
9638 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
9639 // 'url(' to avoid inline style like background: url(http...
9640 // '<link' to avoid <link href="http...">
9641 $reg = array();
9642 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
9643 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
9644 $nblinks = count($reg[0]);
9645 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
9646 $out = 'ErrorTooManyLinksIntoHTMLString';
9647 }
9648
9649 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
9650 if ($nblinks > 0) {
9651 $out = 'ErrorHTMLLinksNotAllowed';
9652 }
9653 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
9654 // Refuse any links except it they are to the wrapper document.php or viewimage.php
9655 $nblinks = 0;
9656
9657 // Loop on each url in src= and url(
9658 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
9659
9661
9662 $matches = array();
9663 if (preg_match_all($pattern, $out, $matches)) {
9664 // URLs are into $matches[1] or $matches[2]
9665 $urls = array();
9666 foreach ($matches[1] as $tmpval) {
9667 if (!empty($tmpval)) {
9668 $urls[] = $tmpval;
9669 }
9670 }
9671 foreach ($matches[2] as $tmpval) {
9672 if (!empty($tmpval)) {
9673 $urls[] = $tmpval;
9674 }
9675 }
9676
9677 // Show URLs
9678 $firstexturl = '';
9679 $secondexturl = '';
9680 foreach ($urls as $url) {
9681 $urlok = 0;
9682 $parsedurl = parse_url($url);
9683 if (!empty($parsedurl)) {
9684 if (preg_match('/'.preg_quote($dolibarr_main_url_root, '/').'/', $url)
9685 //&& preg_match('/(document|viewimage)\.php$/', $parsedurl['path']) && preg_match('/modulepart=(media|mycompany)/', $parsedurl['query'])
9686 ) {
9687 $urlok = 1;
9688 }
9689 }
9690 if (!$urlok) {
9691 $nblinks++;
9692 if (empty($firstexturl)) {
9693 $firstexturl = $url;
9694 } elseif (empty($secondexturl)) {
9695 $secondexturl = $url;
9696 }
9697 //echo "Found url = ".$url . "\n";
9698 }
9699 }
9700 if ($nblinks > 0) {
9701 $out = 'ErrorHTMLExternalLinksNotAllowed (Example: '.$firstexturl.($secondexturl ? ' '.$secondexturl : '').')';
9702 }
9703 }
9704 }
9705
9706 return $out;
9707 }
9708}
9709
9730function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9731{
9732 if (is_null($stringtoencode)) {
9733 return '';
9734 }
9735
9736 $newstring = $stringtoencode;
9737 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9738 $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.
9739 if ($removelasteolbr) {
9740 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9741 }
9742 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9743 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9744 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9745 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9746 } else {
9747 if ($removelasteolbr) {
9748 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9749 }
9750 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9751 }
9752 // Other substitutions that htmlentities does not do
9753 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9754 return $newstring;
9755}
9756
9764function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9765{
9766 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9767 $ret = preg_replace('/' . "\r\n" . '<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9768 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\r\n" . '/i', "\r\n", $ret);
9769 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\n" . '/i', "\n", $ret);
9770 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9771 return $ret;
9772}
9773
9780function dol_htmlcleanlastbr($stringtodecode)
9781{
9782 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9783 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|' . "\n" . '|' . "\r" . ')+$/i', "", $ret);
9784 return $ret;
9785}
9786
9796function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9797{
9798 $newstring = $a;
9799 if ($keepsomeentities) {
9800 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9801 }
9802 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9803 if ($keepsomeentities) {
9804 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9805 }
9806 return $newstring;
9807}
9808
9820function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9821{
9822 return htmlentities($string, $flags, $encoding, $double_encode);
9823}
9824
9836function dol_string_is_good_iso($s, $clean = 0)
9837{
9838 $len = dol_strlen($s);
9839 $out = '';
9840 $ok = 1;
9841 for ($scursor = 0; $scursor < $len; $scursor++) {
9842 $ordchar = ord($s[$scursor]);
9843 //print $scursor.'-'.$ordchar.'<br>';
9844 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9845 $ok = 0;
9846 break;
9847 } elseif ($ordchar > 126 && $ordchar < 160) {
9848 $ok = 0;
9849 break;
9850 } elseif ($clean) {
9851 $out .= $s[$scursor];
9852 }
9853 }
9854 if ($clean) {
9855 return $out;
9856 }
9857 return $ok;
9858}
9859
9868function dol_nboflines($s, $maxchar = 0)
9869{
9870 if ($s == '') {
9871 return 0;
9872 }
9873 $arraystring = explode("\n", $s);
9874 $nb = count($arraystring);
9875
9876 return $nb;
9877}
9878
9879
9889function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9890{
9891 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9892 if (dol_textishtml($text)) {
9893 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9894 }
9895
9896 $text = strtr($text, $repTable);
9897 if ($charset == 'UTF-8') {
9898 $pattern = '/(<br[^>]*>)/Uu';
9899 } else {
9900 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9901 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9902 }
9903 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9904
9905 $nblines = (int) floor((count($a) + 1) / 2);
9906 // count possible auto line breaks
9907 if ($maxlinesize) {
9908 foreach ($a as $line) {
9909 if (dol_strlen($line) > $maxlinesize) {
9910 //$line_dec = html_entity_decode(strip_tags($line));
9911 $line_dec = html_entity_decode($line);
9912 if (dol_strlen($line_dec) > $maxlinesize) {
9913 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9914 $nblines += substr_count($line_dec, '\n');
9915 }
9916 }
9917 }
9918 }
9919
9920 unset($a);
9921 return $nblines;
9922}
9923
9932function dol_textishtml($msg, $option = 0)
9933{
9934 if (is_null($msg)) {
9935 return false;
9936 }
9937
9938 if ($option == 1) {
9939 if (preg_match('/<(html|link|script)/i', $msg)) {
9940 return true;
9941 } elseif (preg_match('/<body/i', $msg)) {
9942 return true;
9943 } elseif (preg_match('/<\/textarea/i', $msg)) {
9944 return true;
9945 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9946 return true;
9947 } elseif (preg_match('/<br/i', $msg)) {
9948 return true;
9949 }
9950 return false;
9951 } else {
9952 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9953 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9954 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9955 return true;
9956 } elseif (preg_match('/<\/textarea/i', $msg)) {
9957 return true;
9958 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9959 return true;
9960 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9961 return true;
9962 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9963 return true;
9964 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9965 return true;
9966 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9967 return true; // must accept <img src="http://example.com/aaa.png" />
9968 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9969 return true; // must accept <a href="http://example.com/aaa.png" />
9970 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9971 return true;
9972 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9973 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9974 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9975 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9976 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9977 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
9978 return true; // Html entities numbers in hexa
9979 }
9980
9981 return false;
9982 }
9983}
9984
9999function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
10000{
10001 if (!empty($invert)) {
10002 $tmp = $text1;
10003 $text1 = $text2;
10004 $text2 = $tmp;
10005 }
10006
10007 $ret = '';
10008 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
10009 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
10010 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
10011 return $ret;
10012}
10013
10022function dol_concat($text1, $text2)
10023{
10024 return $text1.$text2;
10025}
10026
10040function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
10041{
10042 global $db, $conf, $mysoc, $user, $extrafields;
10043
10044 $substitutionarray = array();
10045
10046 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
10047 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
10048 // this will include signature content first and then replace var found into content of signature
10049 //var_dump($onlykey);
10050 $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()
10051 $usersignature = $user->signature;
10052 $substitutionarray = array_merge($substitutionarray, array(
10053 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
10054 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
10055 ));
10056
10057 if (is_object($user) && ($user instanceof User)) {
10058 $substitutionarray = array_merge($substitutionarray, array(
10059 '__USER_ID__' => (string) $user->id,
10060 '__USER_LOGIN__' => (string) $user->login,
10061 '__USER_EMAIL__' => (string) $user->email,
10062 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
10063 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
10064 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
10065 '__USER_FAX__' => (string) $user->office_fax,
10066 '__USER_LASTNAME__' => (string) $user->lastname,
10067 '__USER_FIRSTNAME__' => (string) $user->firstname,
10068 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
10069 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
10070 '__USER_JOB__' => (string) $user->job,
10071 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
10072 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
10073 ));
10074 }
10075 }
10076 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
10077 $substitutionarray = array_merge($substitutionarray, array(
10078 '__MYCOMPANY_NAME__' => $mysoc->name,
10079 '__MYCOMPANY_EMAIL__' => $mysoc->email,
10080 '__MYCOMPANY_URL__' => $mysoc->url,
10081 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
10082 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
10083 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
10084 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
10085 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
10086 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
10087 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
10088 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
10089 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
10090 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
10091 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
10092 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
10093 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
10094 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
10095 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
10096 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
10097 '__MYCOMPANY_VATNUMBER__' => $mysoc->tva_intra,
10098 '__MYCOMPANY_ZIP__' => $mysoc->zip,
10099 '__MYCOMPANY_TOWN__' => $mysoc->town,
10100 '__MYCOMPANY_STATE__' => $mysoc->state,
10101 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
10102 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
10103 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
10104 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
10105 ));
10106 }
10107
10108 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
10109 if ($onlykey) {
10110 $substitutionarray['__ID__'] = '__ID__';
10111 $substitutionarray['__REF__'] = '__REF__';
10112 $substitutionarray['__NEWREF__'] = '__NEWREF__';
10113 $substitutionarray['__LABEL__'] = '__LABEL__';
10114 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
10115 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
10116 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
10117 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
10118 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
10119
10120 if (isModEnabled("societe")) { // Most objects are concerned
10121 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
10122 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
10123 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
10124 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
10125 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
10126 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
10127 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
10128 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
10129 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
10130 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
10131 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
10132 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
10133 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
10134 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
10135 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
10136 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
10137 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
10138 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
10139 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
10140 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
10141 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
10142 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
10143 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
10144 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
10145 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
10146 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
10147 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
10148 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
10149 }
10150 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
10151 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
10152 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
10153 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
10154 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
10155 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
10156 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
10157 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
10158 }
10159 // add substitution variables for ticket
10160 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
10161 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
10162 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
10163 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
10164 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
10165 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
10166 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
10167 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
10168 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
10169 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
10170 }
10171
10172 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
10173 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
10174 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
10175 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
10176 }
10177 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
10178 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
10179 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
10180 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
10181 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
10182 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
10183 }
10184 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
10185 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
10186 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
10187 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
10188 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
10189 }
10190 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
10191 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
10192 }
10193 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
10194 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
10195 }
10196 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
10197 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
10198 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
10199 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
10200 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
10201 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
10202 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
10203
10204 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
10205 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
10206 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
10207 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
10208 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
10209
10210 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
10211 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
10212 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
10213 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
10214 }
10215 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
10216 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
10217 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
10218 }
10219 } else {
10220 '@phan-var-force Adherent|Delivery $object';
10222 $substitutionarray['__ID__'] = $object->id;
10223 $substitutionarray['__REF__'] = $object->ref;
10224 $substitutionarray['__NEWREF__'] = $object->newref;
10225 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
10226 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10227 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10228 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
10229 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
10230
10231 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
10232 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
10233 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
10234
10235 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
10236 $date_delivery = null;
10237 if (property_exists($object, 'date_delivery')) {
10238 $date_delivery = $object->date_delivery;
10239 } elseif (property_exists($object, 'delivery_date')) {
10240 $date_delivery = $object->delivery_date;
10241 }
10242 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10243 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
10244 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
10245 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
10246 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
10247 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
10248 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
10249 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
10250 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
10251
10252 // For backward compatibility (deprecated)
10253 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10254 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10255
10256 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10257 $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 : '')) : '');
10258 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
10259
10260 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
10261 '@phan-var-force Adherent $object';
10263 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
10264
10265 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
10266 if (method_exists($object, 'getCivilityLabel')) {
10267 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
10268 }
10269 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
10270 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
10271 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
10272 if (method_exists($object, 'getFullName')) {
10273 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
10274 }
10275 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
10276 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
10277 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
10278 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
10279 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
10280 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
10281 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
10282 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
10283 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
10284 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
10285 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
10286 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
10287 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
10288 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
10289 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
10290 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
10291
10292 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
10293 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
10294 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
10295 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
10296 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
10297 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
10298 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
10299 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
10300 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
10301 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
10302 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
10303 }
10304
10305 if (is_object($object) && $object->element == 'societe') {
10307 '@phan-var-force Societe $object';
10308 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
10309 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
10310 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
10311 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
10312 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
10313 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
10314 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
10315 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
10316 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
10317 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
10318 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
10319 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
10320 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
10321 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
10322 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
10323 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ?: '');
10324 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
10325 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
10326 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
10327 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
10328 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
10329 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
10330 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
10331 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
10332 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
10333 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
10334 } elseif (is_object($object) && is_object($object->thirdparty)) {
10335 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
10336 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
10337 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
10338 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
10339 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
10340 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
10341 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
10342 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
10343 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
10344 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
10345 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
10346 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
10347 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
10348 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ?: '');
10349 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
10350 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
10351 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
10352 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
10353 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
10354 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
10355 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
10356 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
10357 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
10358 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
10359 }
10360
10361 if (is_object($object) && $object->element == 'recruitmentcandidature') {
10362 '@phan-var-force RecruitmentCandidature $object';
10364 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
10365 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10366 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10367 }
10368 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
10369 '@phan-var-force ConferenceOrBoothAttendee $object';
10371 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
10372 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10373 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10374 }
10375
10376 if (is_object($object) && $object->element == 'project') {
10377 '@phan-var-force Project $object';
10379 $substitutionarray['__PROJECT_ID__'] = $object->id;
10380 $substitutionarray['__PROJECT_REF__'] = $object->ref;
10381 $substitutionarray['__PROJECT_NAME__'] = $object->title;
10382 } elseif (is_object($object)) {
10383 $project = null;
10384 if (!empty($object->project)) {
10385 $project = $object->project;
10386 }
10387 if (!is_null($project) && is_object($project)) {
10388 $substitutionarray['__PROJECT_ID__'] = $project->id;
10389 $substitutionarray['__PROJECT_REF__'] = $project->ref;
10390 $substitutionarray['__PROJECT_NAME__'] = $project->title;
10391 } else {
10392 // can substitute variables for project : uses lazy load in "make_substitutions" method
10393 $project_id = 0;
10394 if (!empty($object->fk_project) && $object->fk_project > 0) {
10395 $project_id = $object->fk_project;
10396 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
10397 $project_id = $object->fk_project;
10398 }
10399 if ($project_id > 0) {
10400 // path:class:method:id
10401 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10402 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10403 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10404 }
10405 }
10406 }
10407
10408 if (is_object($object) && $object->element == 'facture') {
10409 '@phan-var-force Facture $object';
10411 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
10412 }
10413 if (is_object($object) && $object->element == 'shipping') {
10414 '@phan-var-force Expedition $object';
10416 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
10417 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
10418 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
10419 }
10420 if (is_object($object) && $object->element == 'reception') {
10421 '@phan-var-force Reception $object';
10423 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
10424 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
10425 }
10426
10427 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
10428 '@phan-var-force Contrat $object';
10430 $dateplannedstart = '';
10431 $datenextexpiration = '';
10432 foreach ($object->lines as $line) {
10433 if ($line->date_start > $dateplannedstart) {
10434 $dateplannedstart = $line->date_start;
10435 }
10436 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
10437 $datenextexpiration = $line->date_end;
10438 }
10439 }
10440 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
10441 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
10442 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
10443
10444 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
10445 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
10446 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
10447 }
10448 // add substitution variables for ticket
10449 if (is_object($object) && $object->element == 'ticket') {
10450 '@phan-var-force Ticket $object';
10452 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
10453 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
10454 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
10455 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
10456 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
10457 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
10458 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
10459 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
10460 $userstat = new User($db);
10461 if ($object->fk_user_assign > 0) {
10462 $userstat->fetch($object->fk_user_assign);
10463 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10464 }
10465
10466 if ($object->fk_user_create > 0) {
10467 $userstat->fetch($object->fk_user_create);
10468 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10469 }
10470 }
10471
10472 // Create dynamic tags for __EXTRAFIELD_FIELD__
10473 if ($object->table_element && $object->id > 0) {
10474 if (!is_object($extrafields)) {
10475 $extrafields = new ExtraFields($db);
10476 }
10477 $extrafields->fetch_name_optionals_label($object->table_element, true);
10478
10479 if ($object->fetch_optionals() > 0) {
10480 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
10481 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
10482 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
10483 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_date($object->array_options['options_' . $key], 'day');
10484 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = dol_print_date($object->array_options['options_' . $key], 'day', 'tzserver', $outputlangs);
10485 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = dol_print_date($object->array_options['options_' . $key], 'dayrfc');
10486 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
10487 $datetime = $object->array_options['options_' . $key];
10488 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
10489 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
10490 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
10491 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
10492 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
10493 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_phone($object->array_options['options_' . $key]);
10494 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
10495 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
10496 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATED__'] = price($object->array_options['options_' . $key]); // For compatibility
10497 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATTED__'] = price($object->array_options['options_' . $key]);
10498 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
10499 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = !empty($object->array_options['options_' . $key]) ? $object->array_options['options_' . $key] : '';
10500 }
10501 }
10502 }
10503 }
10504 }
10505
10506 // Complete substitution array with the url to make online payment
10507 if (empty($substitutionarray['__REF__'])) {
10508 $paymenturl = '';
10509 } else {
10510 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
10511 require_once DOL_DOCUMENT_ROOT . '/core/lib/payments.lib.php';
10512 $outputlangs->loadLangs(array('paypal', 'other'));
10513
10514 $amounttouse = 0;
10515 $typeforonlinepayment = 'free';
10516 if (is_object($object) && $object->element == 'commande') {
10517 $typeforonlinepayment = 'order';
10518 }
10519 if (is_object($object) && $object->element == 'facture') {
10520 $typeforonlinepayment = 'invoice';
10521 }
10522 if (is_object($object) && $object->element == 'member') {
10523 $typeforonlinepayment = 'member';
10524 if (!empty($object->last_subscription_amount)) {
10525 $amounttouse = $object->last_subscription_amount;
10526 }
10527 }
10528 if (is_object($object) && $object->element == 'contrat') {
10529 $typeforonlinepayment = 'contract';
10530 }
10531 if (is_object($object) && $object->element == 'fichinter') {
10532 $typeforonlinepayment = 'ficheinter';
10533 }
10534
10535 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], (float) $amounttouse);
10536 $paymenturl = $url;
10537 }
10538
10539 if ($object->id > 0) {
10540 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
10541 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
10542
10543 // Show structured communication
10544 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
10545 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions_be.lib.php';
10546 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
10547 }
10548
10549 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
10550 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10551 } else {
10552 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
10553 }
10554 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
10555 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
10556 } else {
10557 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
10558 }
10559 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
10560 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
10561 } else {
10562 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
10563 }
10564 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
10565 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
10566 } else {
10567 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
10568 }
10569 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
10570 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
10571 } else {
10572 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
10573 }
10574 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
10575 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10576 } else {
10577 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
10578 }
10579
10580 if (is_object($object) && $object->element == 'propal') {
10581 '@phan-var-force Propal $object';
10583 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/comm/propal/card.php?id=" . $object->id;
10584 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10585 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
10586 }
10587 if (is_object($object) && $object->element == 'commande') {
10588 '@phan-var-force Commande $object';
10590 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT . "/commande/card.php?id=" . $object->id;
10591 }
10592 if (is_object($object) && $object->element == 'facture') {
10593 '@phan-var-force Facture $object';
10595 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT . "/compta/facture/card.php?id=" . $object->id;
10596 }
10597 if (is_object($object) && $object->element == 'contrat') {
10598 '@phan-var-force Contrat $object';
10600 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT . "/contrat/card.php?id=" . $object->id;
10601 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10602 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
10603 }
10604 if (is_object($object) && $object->element == 'fichinter') {
10605 '@phan-var-force Fichinter $object';
10607 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT . "/fichinter/card.php?id=" . $object->id;
10608 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10609 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
10610 }
10611 if (is_object($object) && $object->element == 'supplier_proposal') {
10612 '@phan-var-force SupplierProposal $object';
10614 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/supplier_proposal/card.php?id=" . $object->id;
10615 }
10616 if (is_object($object) && $object->element == 'invoice_supplier') {
10617 '@phan-var-force FactureFournisseur $object';
10619 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT . "/fourn/facture/card.php?id=" . $object->id;
10620 }
10621 if (is_object($object) && $object->element == 'payment_supplier') {
10622 '@phan-var-force PaiementFourn $object';
10624 //print_r($object);
10625 $liste_factures = [];
10626 $total = 0;
10627
10628 $sql = 'SELECT f.ref,f.multicurrency_code as f_mccode, pf.*
10629 FROM '.MAIN_DB_PREFIX.'paiementfourn_facturefourn as pf
10630 JOIN '.MAIN_DB_PREFIX.'facture_fourn as f ON pf.fk_facturefourn = f.rowid
10631 WHERE pf.fk_paiementfourn = '.((int) $object->id);
10632
10633 $resql = $db->query($sql);
10634 if ($resql) {
10635 while ($objp = $db->fetch_object($resql)) {
10636 $liste_factures[] = ' - '.$outputlangs->trans('Invoice').' '. $objp->ref.' '.$outputlangs->trans('AmountPayed').' '.price($objp->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $objp->multicurrency_code);
10637 }
10638 }
10639 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_LIST__'] = implode("\n", $liste_factures);
10640 ;
10641 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_TOTAL__'] = price($object->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $object->multicurrency_code ? $object->multicurrency_code : $conf->currency);
10642 }
10643 if (is_object($object) && $object->element == 'shipping') {
10644 '@phan-var-force Expedition $object';
10646 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT . "/expedition/card.php?id=" . $object->id;
10647 }
10648 }
10649
10650 if (is_object($object) && $object->element == 'action') {
10651 '@phan-var-force ActionComm $object';
10653 $substitutionarray['__EVENT_LABEL__'] = $object->label;
10654 $substitutionarray['__EVENT_DESCRIPTION__'] = $object->note;
10655 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action" . $object->type_code);
10656 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
10657 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
10658 $substitutionarray['__EVENT_DATE_TZUSER__'] = dol_print_date($object->datep, 'day', 'tzuserrel', $outputlangs);
10659 $substitutionarray['__EVENT_TIME_TZUSER__'] = dol_print_date($object->datep, 'hour', 'tzuserrel', $outputlangs);
10660 }
10661 }
10662 }
10663
10664 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
10665 '@phan-var-force Facture|FactureRec $object';
10667 include_once DOL_DOCUMENT_ROOT . '/core/lib/functionsnumtoword.lib.php';
10668
10669 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
10670 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
10671 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
10672 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
10673
10674 $already_payed_all = 0;
10675 if (is_object($object) && ($object instanceof Facture)) {
10676 $already_payed_all = $object->totalpaid + $object->totaldeposits + $object->totalcreditnotes;
10677 }
10678
10679 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
10680 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
10681 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
10682
10683 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
10684 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
10685 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
10686
10687 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
10688
10689 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10690 $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)) : '';
10691 $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)) : '';
10692
10693 $mysocuselocaltax1 = false;
10694 $mysocuselocaltax2 = false;
10695 if ($mysoc instanceof Societe && !empty($mysoc->country_code)) {
10696 $tmparray = $mysoc->useLocalTax(-1);
10697 $mysocuselocaltax1 = $tmparray[1];
10698 $mysocuselocaltax2 = $tmparray[2];
10699 }
10700
10701 // Local taxes
10702 if ($onlykey != 2 || $mysocuselocaltax1) {
10703 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
10704 }
10705 if ($onlykey != 2 || $mysocuselocaltax2) {
10706 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
10707 }
10708
10709 // Amount keys formatted in a currency
10710 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10711 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10712 $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) : '';
10713 $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)) : '';
10714 if ($onlykey != 2 || $mysocuselocaltax1) {
10715 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10716 }
10717 if ($onlykey != 2 || $mysocuselocaltax2) {
10718 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10719 }
10720 // Amount keys formatted in a currency (with the typo error for backward compatibility)
10721 if ($onlykey != 2) {
10722 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
10723 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
10724 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
10725 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
10726 if ($mysocuselocaltax1) {
10727 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
10728 }
10729 if ($mysoc->useLocalTax2) {
10730 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
10731 }
10732 }
10733
10734 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
10735 $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) : '';
10736 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
10737 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
10738 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
10739 // TODO Add other keys for foreign multicurrency
10740
10741 // For backward compatibility
10742 if ($onlykey != 2) {
10743 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
10744 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
10745 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10746 }
10747 }
10748
10749
10750 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10751 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
10752
10753 $now = dol_now();
10754
10755 $tmp = dol_getdate($now, true);
10756 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10757 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10758 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10759 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10760
10761 $daytext = $outputlangs->trans('Day' . $tmp['wday']);
10762
10763 $substitutionarray = array_merge($substitutionarray, array(
10764 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10765 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10766 '__DAY__' => (string) $tmp['mday'],
10767 '__DAY_TEXT__' => $daytext, // Monday
10768 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10769 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10770 '__MONTH__' => (string) $tmp['mon'],
10771 '__MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp['mon'])),
10772 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp['mon'])),
10773 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp['mon'])),
10774 '__YEAR__' => (string) $tmp['year'],
10775 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10776 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10777 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10778 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10779 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp3['month'])),
10780 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp3['month'])),
10781 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp3['month'])),
10782 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10783 '__NEXT_DAY__' => (string) $tmp4['day'],
10784 '__NEXT_MONTH__' => (string) $tmp5['month'],
10785 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp5['month'])),
10786 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp5['month'])),
10787 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp5['month'])),
10788 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10789 ));
10790 }
10791
10792 if (isModEnabled('multicompany')) {
10793 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10794 }
10795 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10796 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10797 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10798 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey') . ' (load also language file before)';
10799 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10800 }
10801
10802 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10803
10804 return $substitutionarray;
10805}
10806
10823function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10824{
10825 global $db, $langs;
10826
10827 if (!is_array($substitutionarray)) {
10828 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10829 }
10830
10831 if (empty($outputlangs)) {
10832 $outputlangs = $langs;
10833 }
10834
10835 // Is initial text HTML or simple text ?
10836 $msgishtml = 0;
10837 if (dol_textishtml($text, 1)) {
10838 $msgishtml = 1;
10839 }
10840
10841 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10842 if (is_object($outputlangs)) {
10843 $reg = array();
10844 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10845 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10846 $tmp = explode('|', $reg[1]);
10847 if (!empty($tmp[1])) {
10848 $outputlangs->load($tmp[1]);
10849 }
10850
10851 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10852
10853 if (empty($converttextinhtmlifnecessary)) {
10854 // convert $newval into HTML is necessary
10855 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10856 } else {
10857 if (! $msgishtml) {
10858 $valueishtml = dol_textishtml($value, 1);
10859 //var_dump("valueishtml=".$valueishtml);
10860
10861 if ($valueishtml) {
10862 $text = dol_htmlentitiesbr($text);
10863 $msgishtml = 1;
10864 }
10865 } else {
10866 $value = dol_nl2br((string) $value);
10867 }
10868
10869 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $value, $text);
10870 }
10871 }
10872 }
10873
10874 // Make substitution for constant keys.
10875 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10876 $reg = array();
10877 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10878 $originalkeyfound = $reg[1];
10879 $keyfound = preg_replace('/\|urlencode$/', '', $originalkeyfound);
10880
10881 if (isASecretKey($keyfound)) {
10882 $value = '*****forbidden*****';
10883 } else {
10884 $value = getDolGlobalString($keyfound);
10885 // Execute some functions on value of substitution key
10886 if (preg_match('/\|urlencode$/', $originalkeyfound)) {
10887 $value = urlencode($value);
10888 }
10889 }
10890
10891 if (empty($converttextinhtmlifnecessary)) {
10892 // convert $newval into HTML is necessary
10893 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10894 } else {
10895 if (! $msgishtml) {
10896 $valueishtml = dol_textishtml($value, 1);
10897
10898 if ($valueishtml) {
10899 $text = dol_htmlentitiesbr($text);
10900 $msgishtml = 1;
10901 }
10902 } else {
10903 $value = dol_nl2br((string) $value);
10904 }
10905
10906 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $value, $text);
10907 }
10908 }
10909
10910 // Make substitution for array $substitutionarray
10911 foreach ($substitutionarray as $key => $value) {
10912 if (!isset($value)) {
10913 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10914 }
10915
10916 if (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN') && ($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__')) {
10917 $value = ''; // Protection
10918 }
10919
10920 if (empty($converttextinhtmlifnecessary)) {
10921 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10922 } else {
10923 if (! $msgishtml) {
10924 $valueishtml = dol_textishtml($value, 1);
10925
10926 if ($valueishtml) {
10927 $text = dol_htmlentitiesbr($text);
10928 $msgishtml = 1;
10929 }
10930 } else {
10931 $value = dol_nl2br((string) $value);
10932 }
10933 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10934 }
10935 }
10936
10937 // TODO Implement the lazyload substitution
10938 /*
10939 add a loop to scan $substitutionarray:
10940 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.
10941 If no, we don't need to make replacement, so we do nothing.
10942 If yes, we can make the substitution:
10943
10944 include_once $path;
10945 $tmpobj = new $class($db);
10946 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10947 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10948 */
10949 $memory_object_list = array();
10950 foreach ($substitutionarray as $key => $value) {
10951 $lazy_load_arr = array();
10952 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10953 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10954 $key_to_substitute = $lazy_load_arr[1];
10955 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10956 $param_arr = explode(':', (string) $value);
10957 // path:class:method:id
10958 if (count($param_arr) == 4) {
10959 $path = $param_arr[0];
10960 $class = $param_arr[1];
10961 $method = $param_arr[2];
10962 $id = (int) $param_arr[3];
10963
10964 // load class file and init object list in memory
10965 if (!isset($memory_object_list[$class])) {
10966 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10967 require_once DOL_DOCUMENT_ROOT . $path;
10968 if (class_exists($class)) {
10969 $memory_object_list[$class] = array(
10970 'list' => array(),
10971 );
10972 }
10973 }
10974 }
10975
10976 // fetch object and set substitution
10977 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
10978 if (method_exists($class, $method)) {
10979 if (!isset($memory_object_list[$class]['list'][$id])) {
10980 $tmpobj = new $class($db);
10981 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10982 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
10983 $memory_object_list[$class]['list'][$id] = $tmpobj;
10984 } else {
10985 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10986 $tmpobj = $memory_object_list[$class]['list'][$id];
10987 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10988 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
10989 }
10990
10991 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
10992 }
10993 }
10994 }
10995 }
10996 }
10997 }
10998 }
10999
11000 return $text;
11001}
11002
11015function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
11016{
11017 global $conf, $user;
11018
11019 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
11020
11021 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
11022
11023 // Check if there is external substitution to do, requested by plugins
11024 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
11025
11026 foreach ($dirsubstitutions as $reldir) {
11027 $dir = dol_buildpath($reldir, 0);
11028
11029 // Check if directory exists
11030 if (!dol_is_dir($dir)) {
11031 continue;
11032 }
11033
11034 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
11035 foreach ($substitfiles as $substitfile) {
11036 $reg = array();
11037 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
11038 $module = $reg[1];
11039
11040 dol_syslog("Library " . $substitfile['name'] . " found into " . $dir);
11041 // Include the user's functions file
11042 require_once $dir . $substitfile['name'];
11043 // Call the user's function, and only if it is defined
11044 $function_name = $module . "_" . $callfunc;
11045 if (function_exists($function_name)) {
11046 $function_name($substitutionarray, $outputlangs, $object, $parameters);
11047 }
11048 }
11049 }
11050 }
11051 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
11052 // to list all tags in odt template
11053 $tags = '';
11054 foreach ($substitutionarray as $key => $value) {
11055 $tags .= '{' . $key . '} => ' . $value . "\n";
11056 }
11057 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
11058 }
11059}
11060
11070function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
11071{
11072 print get_date_range($date_start, $date_end, $format, $outputlangs);
11073}
11074
11085function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
11086{
11087 global $langs;
11088
11089 $out = '';
11090
11091 if (!is_object($outputlangs)) {
11092 $outputlangs = $langs;
11093 }
11094
11095 if ($date_start && $date_end) {
11096 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11097 }
11098 if ($date_start && !$date_end) {
11099 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11100 }
11101 if (!$date_start && $date_end) {
11102 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11103 }
11104
11105 return $out;
11106}
11107
11116function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
11117{
11118 $ret = '';
11119 // If order not defined, we use the setup
11120 if ($nameorder < 0) {
11121 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
11122 }
11123 if ($nameorder == 1) {
11124 $ret .= $firstname;
11125 if ($firstname && $lastname) {
11126 $ret .= ' ';
11127 }
11128 $ret .= $lastname;
11129 } elseif ($nameorder == 2 || $nameorder == 3) {
11130 $ret .= $firstname;
11131 if (empty($ret) && $nameorder == 3) {
11132 $ret .= $lastname;
11133 }
11134 } else { // 0, 4 or 5
11135 $ret .= $lastname;
11136 if (empty($ret) && $nameorder == 5) {
11137 $ret .= $firstname;
11138 }
11139 if ($nameorder == 0) {
11140 if ($firstname && $lastname) {
11141 $ret .= ' ';
11142 }
11143 $ret .= $firstname;
11144 }
11145 }
11146 return $ret;
11147}
11148
11149
11162function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
11163{
11164 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
11165 if (!is_array($mesgs)) {
11166 $mesgs = trim((string) $mesgs);
11167 // If mesgs is a not an empty string
11168 if ($mesgs) {
11169 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
11170 return;
11171 }
11172 if ($attop) {
11173 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11174 } else {
11175 $_SESSION['dol_events'][$style][] = $mesgs;
11176 }
11177 }
11178 } else {
11179 // If mesgs is an array
11180 foreach ($mesgs as $mesg) {
11181 $mesg = trim((string) $mesg);
11182 if ($mesg) {
11183 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
11184 return;
11185 }
11186 if ($attop) {
11187 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11188 } else {
11189 $_SESSION['dol_events'][$style][] = $mesg;
11190 }
11191 }
11192 }
11193 }
11194}
11195
11209function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
11210{
11211 if (empty($mesg) && empty($mesgs)) {
11212 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
11213 } else {
11214 if ($messagekey) {
11215 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
11216 // TODO
11217 $mesg .= '';
11218 }
11219 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE" . $messagekey])) {
11220 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
11221 dol_print_error(null, 'Bad parameter style=' . $style . ' for setEventMessages');
11222 }
11223 if (empty($mesgs)) {
11224 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
11225 } else {
11226 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
11227 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
11228 }
11229 setEventMessage($mesgs, $style, $noduplicate, $attop);
11230 }
11231 }
11232 }
11233}
11234
11244function dol_htmloutput_events($disabledoutputofmessages = 0)
11245{
11246 // Show mesgs
11247 if (isset($_SESSION['dol_events']['mesgs'])) {
11248 if (empty($disabledoutputofmessages)) {
11249 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
11250 }
11251 unset($_SESSION['dol_events']['mesgs']);
11252 }
11253 // Show errors
11254 if (isset($_SESSION['dol_events']['errors'])) {
11255 if (empty($disabledoutputofmessages)) {
11256 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
11257 }
11258 unset($_SESSION['dol_events']['errors']);
11259 }
11260
11261 // Show warnings
11262 if (isset($_SESSION['dol_events']['warnings'])) {
11263 if (empty($disabledoutputofmessages)) {
11264 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
11265 }
11266 unset($_SESSION['dol_events']['warnings']);
11267 }
11268}
11269
11284function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
11285{
11286 global $conf, $langs;
11287
11288 $ret = 0;
11289 $return = '';
11290 $out = '';
11291 $divstart = $divend = '';
11292
11293 // If inline message with no format, we add it.
11294 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
11295 $divstart = '<div class="' . $style . ' clearboth">';
11296 $divend = '</div>';
11297 }
11298
11299 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
11300 $langs->load("errors");
11301 $out .= $divstart;
11302 if (is_array($mesgarray) && count($mesgarray)) {
11303 foreach ($mesgarray as $message) {
11304 $ret++;
11305 $out .= $langs->trans($message);
11306 if ($ret < count($mesgarray)) {
11307 $out .= "<br>\n";
11308 }
11309 }
11310 }
11311 if ($mesgstring) {
11312 $ret++;
11313 $out .= $langs->trans($mesgstring);
11314 }
11315 $out .= $divend;
11316 }
11317
11318 if ($out) {
11319 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
11320 $return = '<script nonce="' . getNonce() . '">
11321 $(document).ready(function() {
11322 /* jnotify(message, preset of message type, keepmessage) */
11323 $.jnotify("' . dol_escape_js($out) . '", "' . ($style == "ok" ? 3000 : $style) . '", ' . ($style == "ok" ? "false" : "true") . ',{ remove: function (){} } );
11324 });
11325 </script>';
11326 } else {
11327 $return = $out;
11328 }
11329 }
11330
11331 return $return;
11332}
11333
11345function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11346{
11347 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11348}
11349
11363function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
11364{
11365 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
11366 return;
11367 }
11368
11369 $iserror = 0;
11370 $iswarning = 0;
11371 if (is_array($mesgarray)) {
11372 foreach ($mesgarray as $val) {
11373 if ($val && preg_match('/class="error"/i', $val)) {
11374 $iserror++;
11375 break;
11376 }
11377 if ($val && preg_match('/class="warning"/i', $val)) {
11378 $iswarning++;
11379 break;
11380 }
11381 }
11382 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
11383 $iserror++;
11384 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
11385 $iswarning++;
11386 }
11387 if ($style == 'error' || $style == 'errors') {
11388 $iserror++;
11389 }
11390 if ($style == 'warning' || $style == 'warnings') {
11391 $iswarning++;
11392 }
11393
11394 if ($iserror || $iswarning) {
11395 // Remove div from texts
11396 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
11397 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
11398 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
11399 // Remove div from texts array
11400 if (is_array($mesgarray)) {
11401 $newmesgarray = array();
11402 foreach ($mesgarray as $val) {
11403 if (is_string($val)) {
11404 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
11405 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
11406 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
11407 $newmesgarray[] = $tmpmesgstring;
11408 } else {
11409 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
11410 }
11411 }
11412 $mesgarray = $newmesgarray;
11413 }
11414 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
11415 } else {
11416 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
11417 }
11418}
11419
11431function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11432{
11433 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11434}
11435
11456function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
11457{
11458 // Clean parameters
11459 $order = strtolower($order);
11460
11461 if (is_array($array)) {
11462 $sizearray = count($array);
11463 if ($sizearray > 0) {
11464 // Build a temp array with sorting key as value
11465 $temp = array();
11466 foreach (array_keys($array) as $key) {
11467 $tmpmultikey = explode(',', $index);
11468 $newindex = $tmpmultikey[0];
11469 if (is_object($array[$key])) {
11470 $temp[$key] = empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex;
11471 // Add other keys
11472 if (!empty($tmpmultikey[1])) {
11473 $newindex = $tmpmultikey[1];
11474 $temp[$key] .= '__' . (empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex);
11475 }
11476 } else {
11477 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11478 $temp[$key] = empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex];
11479 // Add other keys
11480 if (!empty($tmpmultikey[1])) {
11481 $newindex = $tmpmultikey[1];
11482 // @phan-suppress-next-line PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11483 $temp[$key] .= '__' . (empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex]);
11484 }
11485 }
11486 if ($natsort == -1) {
11487 $temp[$key] = '___' . $temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
11488 }
11489 }
11490 if (empty($natsort) || $natsort == -1) {
11491 if ($order == 'asc') {
11492 asort($temp);
11493 } else {
11494 arsort($temp);
11495 }
11496 } else {
11497 if ($case_sensitive) {
11498 natsort($temp);
11499 } else {
11500 natcasesort($temp); // natecasesort is not sensible to case
11501 }
11502 if ($order != 'asc') {
11503 $temp = array_reverse($temp, true);
11504 }
11505 }
11506
11507 $sorted = array();
11508
11509 foreach (array_keys($temp) as $key) {
11510 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
11511 }
11512
11513 return $sorted;
11514 }
11515 }
11516 return $array;
11517}
11518
11519
11527function utf8_check($str)
11528{
11529 $str = (string) $str; // Sometimes string is an int.
11530
11531 // We must use here a binary strlen function (so not dol_strlen)
11532 $strLength = strlen($str);
11533 for ($i = 0; $i < $strLength; $i++) {
11534 if (ord($str[$i]) < 0x80) {
11535 continue; // 0bbbbbbb
11536 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
11537 $n = 1; // 110bbbbb
11538 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
11539 $n = 2; // 1110bbbb
11540 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
11541 $n = 3; // 11110bbb
11542 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
11543 $n = 4; // 111110bb
11544 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
11545 $n = 5; // 1111110b
11546 } else {
11547 return false; // Does not match any model
11548 }
11549 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
11550 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
11551 return false;
11552 }
11553 }
11554 }
11555 return true;
11556}
11557
11565function utf8_valid($str)
11566{
11567 /* 2 other methods to test if string is utf8
11568 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
11569 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
11570 */
11571 return preg_match('//u', $str) ? true : false;
11572}
11573
11574
11581function ascii_check($str)
11582{
11583 if (function_exists('mb_check_encoding')) {
11584 //if (mb_detect_encoding($str, 'ASCII', true) return false;
11585 if (!mb_check_encoding($str, 'ASCII')) {
11586 return false;
11587 }
11588 } else {
11589 if (preg_match('/[^\x00-\x7f]/', $str)) {
11590 return false; // Contains a byte > 7f
11591 }
11592 }
11593
11594 return true;
11595}
11596
11597
11605function dol_osencode($str)
11606{
11607 $tmp = ini_get("unicode.filesystem_encoding");
11608 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
11609 $tmp = 'iso-8859-1'; // By default for windows
11610 }
11611 if (empty($tmp)) {
11612 $tmp = 'utf-8'; // By default for other
11613 }
11614 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
11615 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
11616 }
11617
11618 if ($tmp == 'iso-8859-1') {
11619 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
11620 }
11621 return $str;
11622}
11623
11624
11640function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
11641{
11642 global $conf;
11643
11644 // If key empty
11645 if ($key == '') {
11646 return 0;
11647 }
11648
11649 // Check in cache
11650 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
11651 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
11652 }
11653
11654 dol_syslog('dol_getIdFromCode (value for field ' . $fieldid . ' from key ' . $key . ' not found into cache)', LOG_DEBUG);
11655
11656 $sql = "SELECT " . $fieldid . " as valuetoget";
11657 $sql .= " FROM " . MAIN_DB_PREFIX . $tablename;
11658 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
11659 $sql .= " WHERE " . $fieldkey . " = " . ((int) $key);
11660 } else {
11661 $sql .= " WHERE " . $fieldkey . " = '" . $db->escape($key) . "'";
11662 }
11663 if (!empty($entityfilter)) {
11664 $sql .= " AND entity IN (" . getEntity($tablename) . ")";
11665 }
11666 if ($filters) {
11667 $sql .= $filters;
11668 }
11669
11670 $resql = $db->query($sql);
11671 if ($resql) {
11672 $obj = $db->fetch_object($resql);
11673 $valuetoget = '';
11674 if ($obj) {
11675 $valuetoget = $obj->valuetoget;
11676 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
11677 } else {
11678 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
11679 }
11680 $db->free($resql);
11681
11682 return $valuetoget;
11683 } else {
11684 return -1;
11685 }
11686}
11687
11697function isStringVarMatching($var, $regextext, $matchrule = 1)
11698{
11699 // Tolerate callers (custom modules, older code) that already pass a full regex with delimiters
11700 // like '/^(aaa|bbb)/' instead of the bare body. Without this, the function would build
11701 // '/^/^(aaa|bbb)//' which trips preg_match() with 'Unknown modifier ^'.
11702 $regextext = preg_replace('#^/\^?#', '', (string) $regextext);
11703 $regextext = preg_replace('#\$?/[imsxuADSUXJ]*$#', '', $regextext);
11704
11705 if ($matchrule == 1) {
11706 if ($var == 'mainmenu') {
11707 global $mainmenu;
11708 return (preg_match('/^' . $regextext . '/', $mainmenu));
11709 } elseif ($var == 'leftmenu') {
11710 global $leftmenu;
11711 return (preg_match('/^' . $regextext . '/', $leftmenu));
11712 } else {
11713 return 'This variable is not accessible with dol_eval';
11714 }
11715 } else {
11716 return 'This value '.$matchrule.' for param $matchrule is not yet implemented';
11717 }
11718}
11719
11720
11730function verifCond($strToEvaluate, $onlysimplestring = '1')
11731{
11732 //print $strToEvaluate."<br>\n";
11733 $rights = true;
11734 if (isset($strToEvaluate) && $strToEvaluate !== '') {
11735 //var_dump($strToEvaluate);
11736 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
11737 $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
11738 //var_dump($strToEvaluate, $rep);
11739 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
11740 //var_dump($rights);
11741 }
11742 return $rights;
11743}
11744
11759function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11760{
11761 if ($returnvalue != 1) {
11762 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
11763 }
11764
11765 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
11766 return dol_eval_new($s);
11767 } else {
11768 return dol_eval_standard($s, $hideerrors, $onlysimplestring);
11769 }
11770}
11771
11781function dol_eval_new($s)
11782{
11783 // Only this global variables can be read by eval function and returned to caller
11784 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11785 $db, $langs, $user, $website, $websitepage,
11786 $action, $mainmenu, $leftmenu,
11787 $mysoc,
11788 $objectoffield, // To allow the use of $objectoffield in computed fields
11789
11790 // Old variables used
11791 $object,
11792 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11793
11794 // PHP < 7.4.0
11795 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11796 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11797
11798 // PHP < 8.0.0
11799 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11800 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11801 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11802 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11803 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11804
11805 // PHP < 8.1.0
11806 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11807 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11808 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11809 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11810
11811 // PHP < 8.4.0
11812 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11813 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11814 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11815
11816 $prohibited_token_ids = [
11817 /*
11818 * Prohibited int tokens
11819 */
11820
11821 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11822 'T_ABSTRACT',
11823 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
11824 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
11825 'T_ATTRIBUTE',
11826 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11827 'T_BAD_CHARACTER',
11828 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11829 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11830 'T_CALLABLE',
11831 'T_CATCH',
11832 'T_CLASS',
11833 'T_CLOSE_TAG',
11834 'T_CONST',
11835 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11836 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11837 'T_DECLARE',
11838 'T_DIR',
11839 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11840 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11841 'T_ECHO',
11842 'T_ENDDECLARE',
11843 'T_ENUM',
11844 'T_EVAL',
11845 'T_EXIT',
11846 'T_EXTENDS',
11847 // 'T_FOR', 'T_FOREACH',
11848 'T_FILE',
11849 'T_FINAL',
11850 'T_FINALLY',
11851 'T_FN',
11852 'T_FUNCTION',
11853 'T_FUNC_C',
11854 'T_GLOBAL',
11855 'T_GOTO',
11856 'T_HALT_COMPILER',
11857 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11858 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11859 'T_IMPLEMENTS',
11860 'T_INCLUDE',
11861 'T_INCLUDE_ONCE',
11862 'T_INSTEADOF',
11863 'T_INTERFACE',
11864 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11865 'T_LINE',
11866 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11867 'T_METHOD_C',
11868 // 'T_NEW',
11869 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11870 'T_NAMESPACE',
11871 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11872 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11873 'T_OPEN_TAG',
11874 'T_OPEN_TAG_WITH_ECHO',
11875 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11876 'T_PRINT',
11877 'T_PRIVATE',
11878 'T_PROTECTED',
11879 'T_PUBLIC',
11880 // 'T_PROPERTY_C',
11881 'T_READONLY',
11882 'T_REQUIRE',
11883 'T_REQUIRE_ONCE',
11884 'T_RETURN',
11885 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11886 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11887 'T_STATIC',
11888 'T_THROW',
11889 'T_TRAIT',
11890 'T_TRAIT_C',
11891 'T_TRY',
11892 'T_UNSET',
11893 'T_UNSET_CAST',
11894 'T_USE',
11895 // 'T_VARIABLE',
11896 'T_VAR',
11897 // 'T_WHILE', 'T_WHITESPACE',
11898 // 'T_XOR_EQUAL',
11899 // 'T_YIELD', 'T_YIELD_FROM',
11900
11901 /*
11902 * Prohibited string tokens
11903 */
11904 ';',
11905 '`',
11906 ];
11907
11908 $prohibited_variables = [
11909 '$_COOKIE',
11910 '$_ENV',
11911 '$_FILES',
11912 '$GLOBALS',
11913 '$_GET',
11914 '$_POST',
11915 '$_REQUEST',
11916 '$_SERVER',
11917 '$_SESSION',
11918 ];
11919
11920 $prohibited_functions = [
11921 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11922 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11923 'override_function',
11924 'session_id',
11925 'session_create_id',
11926 'session_regenerate_id',
11927 'call_user_func',
11928 'call_user_func_array', // PREVENT calling forbidden functions
11929 'exec',
11930 'passthru',
11931 'shell_exec',
11932 'system',
11933 'proc_open',
11934 'popen',
11935 'dol_eval',
11936 'dol_eval_new',
11937 'dol_eval_standard',
11938 'dol_contctdesc',
11939 'executeCLI',
11940 'verifCond',
11941 'GETPOST', // Native Dolibarr functions
11942 'create_function',
11943 'assert',
11944 'mb_ereg_replace',
11945 'mb_eregi_replace', // function with eval capabilities
11946 'dol_compress_dir',
11947 'dol_decode',
11948 'dol_delete_file',
11949 'dol_delete_dir',
11950 'dol_delete_dir_recursive',
11951 'dol_copy',
11952 'archiveOrBackupFile', // more dolibarr functions
11953 'fopen',
11954 'file_put_contents',
11955 'fputs',
11956 'fputscsv',
11957 'fwrite',
11958 'fpassthru',
11959 'mkdir',
11960 'rmdir',
11961 'symlink',
11962 'touch',
11963 'unlink',
11964 'umask', // PHP functions related to file operations
11965 'invoke',
11966 'invokeArgs', // Method of ReflectionFunction to execute a function
11967 'filter_input',
11968 'filter_input_array',
11969 'GETPOST', // PREVENT CODE INJECTION
11970 ];
11971
11972 $prohibited_token_arrangements = [
11973 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11974 ' T_VARIABLE ( ',
11975 ' " ( ',
11976 ' \' ( ',
11977 ' T_CONSTANT_ENCAPSED_STRING ( ',
11978 ' ) ( ',
11979 ];
11980
11981 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
11982
11983 $tokens_arrangement = ' ';
11984
11985 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
11986 if (is_array($tokens[$i])) {
11987 $token_id = $tokens[$i][0];
11988 $token_value = $tokens[$i][1];
11989 $token_name = token_name($tokens[$i][0]);
11990 } else {
11991 $token_id = $tokens[$i];
11992 $token_value = $tokens[$i];
11993 $token_name = $tokens[$i];
11994 }
11995
11996 // Ignore whitespaces
11997 if (T_WHITESPACE === $token_id) {
11998 continue;
11999 }
12000
12001 // Keep history to check arrangements
12002 $tokens_arrangement .= "{$token_name} ";
12003
12004 // Prohibited Variables
12005 if (
12006 T_VARIABLE === $token_id
12007 && in_array($token_value, $prohibited_variables, true)
12008 ) {
12009 return "« {$token_value} » is prohibited in « {$s} »";
12010 }
12011
12012 // Prohibited Functions
12013 if (
12014 T_STRING === $token_id
12015 && in_array($token_value, $prohibited_functions, true)
12016 ) {
12017 return "« {$token_value} » is prohibited in « {$s} »";
12018 }
12019 }
12020
12021 // Prohibited Token IDs
12022 $maxi = count($prohibited_token_ids);
12023 for ($i = 0; $i < $maxi; ++$i) {
12024 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
12025 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
12026 }
12027 }
12028
12029 // Prohibited token arrangements
12030 $maxi = count($prohibited_token_arrangements);
12031 for ($i = 0; $i < $maxi; ++$i) {
12032 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
12033 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
12034 }
12035 }
12036
12037 // Return result
12038 try {
12039 return @eval("return {$s};") ?? '';
12040 } catch (Throwable $ex) {
12041 return "Exception during evaluation: " . $s . " - " . $ex->getMessage();
12042 }
12043}
12044
12058function dol_eval_standard($s, $hideerrors = 1, $onlysimplestring = '1')
12059{
12060 // Only this global variables can be read by eval function and returned to caller
12061 // The less we have, the better it is.
12062
12063 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()
12064 global $db, $langs, $user, $website, $websitepage;
12065 global $action, $mainmenu, $leftmenu;
12066 global $mysoc;
12067 global $objectoffield; // To allow the use of $objectoffield in computed fields
12068 global $object;
12069
12070 // Old variables (deprecated)
12071 if (getDolGlobalString('MAIN_ALLOW_OLD_VAR_OBJ_IN_DOL_EVAL')) {
12072 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $objectoffield
12073 }
12074
12075 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
12076 if ($onlysimplestring == '0') { // '0' is deprecated, we process it as the more secured '1'
12077 $onlysimplestring = '1';
12078 }
12079 if (!in_array($onlysimplestring, array('1', '2'))) {
12080 return "Bad call of dol_eval. Parameter onlysimplestring must be '1' or '2'.";
12081 }
12082 if (!is_scalar($s)) {
12083 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
12084 }
12085
12086 try {
12087 global $dolibarr_main_restrict_eval_methods;
12088
12089 // Set $dolibarr_main_restrict_eval_methods_array
12090 if (!isset($dolibarr_main_restrict_eval_methods)) {
12091 $dolibarr_main_restrict_eval_methods = 'getDolGlobalString, getDolGlobalInt, getDolCurrency, getDolEntity, getDolDBType, fetchNoCompute, hasRight, isAdmin, isModEnabled, isStringVarMatching, abs, min, max, round, dol_now, preg_match';
12092 }
12093 //print '$dolibarr_main_restrict_eval_methods = '.$dolibarr_main_restrict_eval_methods."\n";
12094 $dolibarr_main_restrict_eval_methods_array = explode(',', str_replace(" ", "", $dolibarr_main_restrict_eval_methods));
12095
12096 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
12097 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
12098 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$objectoffield->canvas == "patient@cabinetmed"'
12099 // 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"
12100
12101 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
12102 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
12103 if ($onlysimplestring == '2') {
12104 $specialcharsallowed .= '<[]'; // Later we check that < has space before and after
12105 }
12106 global $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12107 if (!empty($dolibarr_main_allow_unsecured_special_chars_in_dol_eval)) {
12108 $specialcharsallowed .= (string) $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12109 }
12110 if (preg_match('/[^a-z0-9\s' . preg_quote($specialcharsallowed, '/') . ']/i', $s)) {
12111 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): ' . $s;
12112 }
12113
12114 // Check if we found a | without a space before and after
12115 /* Disabled to allow preg_match('/(AAA|BBB)/')
12116 $tmps = str_replace(' || ', '__XXX__', $s);
12117 if (strpos($tmps, '|') !== false) {
12118 return 'Bad string syntax to evaluate (The char | can be used only when duplicated || with a space before and after): ' . $s;
12119 }
12120 */
12121
12122 // Check if there is PHP comments (can be used to obfuscate code)
12123 if (strpos($s, '/*') !== false || strpos($s, '//') !== false) {
12124 return 'Bad string syntax to evaluate (The comment string /* and // are not allowed): ' . $s;
12125 }
12126
12127 // Check if we found a ? without a space before and after
12128 $tmps = str_replace(' ? ', '__XXX__', $s);
12129 if (strpos($tmps, '?') !== false) {
12130 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): ' . $s;
12131 }
12132
12133 // Check if there is a < or <= without spaces after
12134 if (preg_match('/<=?[^\s]/', $s)) {
12135 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found a < or <= without space after): ' . $s;
12136 }
12137
12138 // Check if there is dynamic call (first we use black list patterns)
12139 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
12140 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;
12141 }
12142
12143 if (empty($dolibarr_main_restrict_eval_methods)) {
12144 // If $dolibarr_main_restrict_eval_methods was set to '', we must check if we try dynamic call
12145
12146 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
12147 $savescheck = '';
12148 $scheck = $s;
12149 while ($scheck && $savescheck != $scheck) {
12150 $savescheck = $scheck;
12151 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
12152 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
12153 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
12154 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12155 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12156 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
12157 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
12158 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
12159 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
12160 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
12161 }
12162 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
12163
12164 // Now test if it remains 1 open parenthesis.
12165 if (strpos($scheck, '(') !== false) {
12166 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found call of a function or method without using the direct name of the function): ' . $s;
12167 }
12168 }
12169
12170 if (strpos($s, '`') !== false) {
12171 return 'Bad string syntax to evaluate (backtick char is forbidden): ' . $s;
12172 }
12173
12174 // Disallow also concat operator
12175 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) {
12176 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
12177 return 'Bad string syntax to evaluate (dot char is forbidden if not strictly between 2 numbers): ' . $s;
12178 }
12179 }
12180
12181 // We exclude string using a $ character that are not an expected global or temporary vars, so that are not:
12182 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $var....
12183 $savescheck = '';
12184 $scheck = $s;
12185 while ($scheck && $savescheck != $scheck) {
12186 $savescheck = $scheck;
12187 $scheck = preg_replace('/\$conf->[a-z\_]+->enabled/', '__VARCONFENABLED__', $scheck); // Remove this once $user->module->enabled has been replaced everywhere with isModEnabled.
12188 $scheck = preg_replace('/\$user->id/', '__VARUSERID__', $scheck);
12189 $scheck = preg_replace('/\$user->hasRight/', '__VARUSERHASRIGHT__', $scheck);
12190 $scheck = preg_replace('/\$user->rights/', '__VARUSERHASRIGHT__', $scheck); // Remove this once $user->rights->xxx is replaced everywhere with $user->hasRight()
12191 $scheck = preg_replace('/\$user->admin/', '__VARUSERISADMIN__', $scheck); // Remove this once $user->admin is replaced everywhere with $user->isAdmin()
12192 $scheck = preg_replace('/\‍(\$db\‍)/', '__VARDB__', $scheck);
12193 $scheck = preg_replace('/\$langs/', '__VARLANGSTRANS__', $scheck);
12194 $scheck = preg_replace('/\$mysoc/', '__VARMYSOC__', $scheck);
12195 $scheck = preg_replace('/\$action/', '__VARACTION__', $scheck);
12196 $scheck = preg_replace('/\$mainmenu/', '__VARMAINMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12197 $scheck = preg_replace('/\$leftmenu/', '__VARLEFTMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12198 $scheck = preg_replace('/\$websitepage/', '__VARWEBSITEPAGE__', $scheck);
12199 $scheck = preg_replace('/\$website/', '__VARWEBSITE__', $scheck);
12200 $scheck = preg_replace('/\$objectoffield/', '__VAROBJECTOFFIELD__', $scheck);
12201 $scheck = preg_replace('/\$object/', '__VAROBJECT__', $scheck);
12202 $scheck = preg_replace('/\$var/', '__VARVAR__', $scheck);
12203
12204 // deprecated (now we use $objecf->canvas or $objectoffield->canvas)
12205 $scheck = preg_replace('/\$soc->canvas/', '__VARSOCCANVAS__', $scheck);
12206 $scheck = preg_replace('/\$obj->canvas/', '__VAROBJCANVAS__', $scheck);
12207
12208 // Now test if it remains one '$'
12209 if (strpos($scheck, '$') !== false) {
12210 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);
12211 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;
12212 }
12213 }
12214
12215 // We block use of php exec or php file functions
12216 $forbiddenphpstrings = array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction', 'SplFileObject', 'SplTempFileObject');
12217
12218 if (empty($dolibarr_main_restrict_eval_methods)) { // If forced to ''
12219 // 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)
12220 // 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
12221 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
12222 $forbiddenphpfunctions = array();
12223 $forbiddenphpmethods = array();
12224
12225 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
12226 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
12227 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
12228
12229 $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"));
12230 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
12231 $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"));
12232 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
12233 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
12234
12235 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
12236
12237 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
12238 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
12239 $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"));
12240 $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", ));
12241 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("putenv", "dl", "apache_child_terminate", "apache_setenv"));
12242 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("posix_kill", "posix_setuid", "posix_setgid"));
12243 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
12244 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
12245 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
12246 $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
12247 $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"));
12248 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
12249 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) { // We disallow all function that allow to obfuscate the real name of a function
12250 // @phpcs:ignore
12251 $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
12252 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concat", "dol_concatdesc")); // native dolibarr functions
12253 }
12254 // Remove from blacklist the function that are into the whitelist
12255 /*foreach ($forbiddenphpfunctions as $key => $forbiddenphpfunction) {
12256 if (in_array($forbiddenphpfunction, $dolibarr_main_restrict_eval_methods_array)) {
12257 unset($forbiddenphpfunctions[$key]);
12258 }
12259 }*/
12260
12261 $forbiddenphpmethods = array_merge($forbiddenphpmethods, array('invoke', 'invokeArgs')); // Methods of ReflectionFunction to execute a function
12262 // Remove from blacklist the function that are into the whitelist
12263 /*foreach ($forbiddenphpmethods as $key => $forbiddenphpmethod) {
12264 if (in_array($forbiddenphpmethod, $dolibarr_main_restrict_eval_methods_array)) {
12265 unset($forbiddenphpmethods[$key]);
12266 }
12267 }*/
12268
12269 $forbiddenphpregex = 'global\s*\$';
12270 $forbiddenphpregex .= '|';
12271 $forbiddenphpregex .= '\b(' . implode('|', $forbiddenphpfunctions) . ')\b';
12272
12273 $forbiddenphpmethodsregex = '->(' . implode('|', $forbiddenphpmethods) . ')';
12274
12275 // Now scan all forbidden patterns
12276 do {
12277 $oldstringtoclean = $s;
12278 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12279 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12280 $s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12281 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12282 } while ($oldstringtoclean != $s);
12283
12284 if (strpos($s, '__forbiddenstring__') !== false) {
12285 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12286 return 'Bad string syntax to evaluate: ' . $s;
12287 }
12288 }
12289
12290 if (!empty($dolibarr_main_restrict_eval_methods)) {
12291 // Accept only white-listed allowed function and classes
12292 // TODO Get all pattern '/([\s\w]+)\‍(/', then check that $reg[1] is a defined class or a function into a given list
12293 $pattern = '/([\s\w\'\]\"]+)\‍(/';
12294
12295 $matches = array();
12296 preg_match_all($pattern, $s, $matches);
12297
12298 if (count($matches)) {
12299 foreach ($matches[1] as $m) {
12300 $m = trim($m);
12301 if (empty($m)) {
12302 continue;
12303 }
12304 $reg = array();
12305 if (!preg_match('/new ([A-Z][\w]+)/i', $m, $reg)) {
12306 if (!in_array($m, $dolibarr_main_restrict_eval_methods_array)) {
12307 if ($m != "'" && $m != '"') {
12308 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12309 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;
12310 }
12311 }
12312 } else {
12313 if (!class_exists($reg[1])) {
12314 dol_syslog('Bad string syntax to evaluate: Class "'.$reg[1].'" does not exist. ' . $s, LOG_WARNING);
12315 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" does not exist. ' . $s;
12316 }
12317 $parents = class_parents($reg[1]); // Get list of parent classes of class we want to check
12318 if (!in_array('CommonObject', $parents)) { // Only classes that inherit CommonObject are ok. This forbid dangerous classes like ReflectionFunction, SplFileObject, ...
12319 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);
12320 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" is not allowed because only classes extended CommonObject can be used in dynamic evaluation. ' . $s;
12321 }
12322 }
12323 }
12324 }
12325
12326 $forbiddenphpregex = 'global\s*\$';
12327 $forbiddenphpregex .= '|'; // or
12328 $forbiddenphpregex .= '}\s*\[';
12329 $forbiddenphpregex .= '|'; // or
12330 $forbiddenphpregex .= '\‍)\s*\‍(';
12331
12332 // Now scan all forbidden patterns
12333 do {
12334 $oldstringtoclean = $s;
12335 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12336 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12337 //$s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12338 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12339 } while ($oldstringtoclean != $s);
12340
12341 if (strpos($s, '__forbiddenstring__') !== false) {
12342 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12343 return 'Bad string syntax to evaluate: ' . $s;
12344 }
12345 }
12346
12347 //print $s."<br>\n";
12348 ob_start(); // An evaluation has no reason to output data
12349 $isObBufferActive = true;
12350 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
12351 $tmpo = ob_get_clean();
12352 $isObBufferActive = false;
12353 if ($tmpo) {
12354 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
12355 }
12356 return $tmps;
12357 } catch (Error $e) {
12358 if ($isObBufferActive) {
12359 // Clean up buffer which was left behind due to exception.
12360 $tmpo = ob_get_clean();
12361 $isObBufferActive = false;
12362 }
12363 $error = 'dol_eval try/catch error : ';
12364 $error .= $e->getMessage();
12365 dol_syslog($error, LOG_WARNING);
12366 return 'Exception during evaluation: ' . $s;
12367 }
12368}
12369
12377function dol_validElement($element)
12378{
12379 return (trim($element) != '');
12380}
12381
12390function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
12391{
12392 if (empty($codelang)) {
12393 return '';
12394 }
12395
12396 if ($codelang == 'auto') {
12397 return '<span class="fa fa-language"></span>';
12398 }
12399
12400 $langtocountryflag = array(
12401 'ar_AR' => '',
12402 'ca_ES' => 'catalonia',
12403 'da_DA' => 'dk',
12404 'fr_CA' => 'mq',
12405 'sv_SV' => 'se',
12406 'sw_SW' => 'unknown',
12407 'AQ' => 'unknown',
12408 'CW' => 'unknown',
12409 'IM' => 'unknown',
12410 'JE' => 'unknown',
12411 'MF' => 'unknown',
12412 'BL' => 'unknown',
12413 'SX' => 'unknown'
12414 );
12415
12416 if (isset($langtocountryflag[$codelang])) {
12417 $flagImage = $langtocountryflag[$codelang];
12418 } else {
12419 $tmparray = explode('_', $codelang);
12420 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
12421 }
12422
12423 $morecss = '';
12424 $reg = array();
12425 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
12426 $morecss = $reg[1];
12427 $moreatt = "";
12428 }
12429
12430 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
12431 return '<span class="flag-sprite ' . strtolower($flagImage) . ($morecss ? ' ' . $morecss : '') . '"' . ($moreatt ? ' ' . $moreatt : '') . (!$notitlealt ? ' title="' . $codelang . '"' : '') . '></span>';
12432}
12433
12441function getLanguageCodeFromCountryCode($countrycode)
12442{
12443 global $mysoc;
12444
12445 if (empty($countrycode)) {
12446 return null;
12447 }
12448
12449 if (strtoupper($countrycode) == 'MQ') {
12450 return 'fr_CA';
12451 }
12452 if (strtoupper($countrycode) == 'SE') {
12453 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
12454 }
12455 if (strtoupper($countrycode) == 'CH') {
12456 if ($mysoc->country_code == 'FR') {
12457 return 'fr_CH';
12458 }
12459 if ($mysoc->country_code == 'DE') {
12460 return 'de_CH';
12461 }
12462 if ($mysoc->country_code == 'IT') {
12463 return 'it_CH';
12464 }
12465 }
12466
12467 // Locale list taken from:
12468 // http://stackoverflow.com/questions/3191664/
12469 // list-of-all-locales-and-their-short-codes
12470 $locales = array(
12471 'af-ZA',
12472 'am-ET',
12473 'ar-AE',
12474 'ar-BH',
12475 'ar-DZ',
12476 'ar-EG',
12477 'ar-IQ',
12478 'ar-JO',
12479 'ar-KW',
12480 'ar-LB',
12481 'ar-LY',
12482 'ar-MA',
12483 'ar-OM',
12484 'ar-QA',
12485 'ar-SA',
12486 'ar-SY',
12487 'ar-TN',
12488 'ar-YE',
12489 //'as-IN', // Moved after en-IN
12490 'ba-RU',
12491 'be-BY',
12492 'bg-BG',
12493 'bn-BD',
12494 //'bn-IN', // Moved after en-IN
12495 'bo-CN',
12496 'br-FR',
12497 'ca-ES',
12498 'co-FR',
12499 'cs-CZ',
12500 'cy-GB',
12501 'da-DK',
12502 'de-AT',
12503 'de-CH',
12504 'de-DE',
12505 'de-LI',
12506 'de-LU',
12507 'dv-MV',
12508 'el-GR',
12509 'en-AU',
12510 'en-BZ',
12511 'en-CA',
12512 'en-GB',
12513 'en-IE',
12514 'en-IN',
12515 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
12516 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
12517 'en-JM',
12518 'en-MY',
12519 'en-NZ',
12520 'en-PH',
12521 'en-SG',
12522 'en-TT',
12523 'en-US',
12524 'en-ZA',
12525 'en-ZW',
12526 'es-AR',
12527 'es-BO',
12528 'es-CL',
12529 'es-CO',
12530 'es-CR',
12531 'es-DO',
12532 'es-EC',
12533 'es-ES',
12534 'es-GT',
12535 'es-HN',
12536 'es-MX',
12537 'es-NI',
12538 'es-PA',
12539 'es-PE',
12540 'es-PR',
12541 'es-PY',
12542 'es-SV',
12543 'es-US',
12544 'es-UY',
12545 'es-VE',
12546 'et-EE',
12547 'eu-ES',
12548 'fa-IR',
12549 'fi-FI',
12550 'fo-FO',
12551 'fr-BE',
12552 'fr-CA',
12553 'fr-CH',
12554 'fr-FR',
12555 'fr-LU',
12556 'fr-MC',
12557 'fy-NL',
12558 'ga-IE',
12559 'gd-GB',
12560 'gl-ES',
12561 'gu-IN',
12562 'he-IL',
12563 'hi-IN',
12564 'hr-BA',
12565 'hr-HR',
12566 'hu-HU',
12567 'hy-AM',
12568 'id-ID',
12569 'ig-NG',
12570 'ii-CN',
12571 'is-IS',
12572 'it-CH',
12573 'it-IT',
12574 'ja-JP',
12575 'ka-GE',
12576 'kk-KZ',
12577 'kl-GL',
12578 'km-KH',
12579 'kn-IN',
12580 'ko-KR',
12581 'ky-KG',
12582 'lb-LU',
12583 'lo-LA',
12584 'lt-LT',
12585 'lv-LV',
12586 'mi-NZ',
12587 'mk-MK',
12588 'ml-IN',
12589 'mn-MN',
12590 'mr-IN',
12591 'ms-BN',
12592 'ms-MY',
12593 'mt-MT',
12594 'nb-NO',
12595 'ne-NP',
12596 'nl-BE',
12597 'nl-NL',
12598 'nn-NO',
12599 'oc-FR',
12600 'or-IN',
12601 'pa-IN',
12602 'pl-PL',
12603 'ps-AF',
12604 'pt-BR',
12605 'pt-PT',
12606 'rm-CH',
12607 'ro-MD',
12608 'ro-RO',
12609 'ru-RU',
12610 'rw-RW',
12611 'sa-IN',
12612 'se-FI',
12613 'se-NO',
12614 'se-SE',
12615 'si-LK',
12616 'sk-SK',
12617 'sl-SI',
12618 'sq-AL',
12619 'sv-FI',
12620 'sv-SE',
12621 'sw-KE',
12622 'ta-IN',
12623 'te-IN',
12624 'th-TH',
12625 'tk-TM',
12626 'tn-ZA',
12627 'tr-TR',
12628 'tt-RU',
12629 'ug-CN',
12630 'uk-UA',
12631 'ur-PK',
12632 'vi-VN',
12633 'wo-SN',
12634 'xh-ZA',
12635 'yo-NG',
12636 'zh-CN',
12637 'zh-HK',
12638 'zh-MO',
12639 'zh-SG',
12640 'zh-TW',
12641 'zu-ZA',
12642 );
12643
12644 $buildprimarykeytotest = strtolower($countrycode) . '-' . strtoupper($countrycode);
12645 if (in_array($buildprimarykeytotest, $locales)) {
12646 return strtolower($countrycode) . '_' . strtoupper($countrycode);
12647 }
12648
12649 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
12650 foreach ($locales as $locale) {
12651 $locale_language = locale_get_primary_language($locale);
12652 $locale_region = locale_get_region($locale);
12653 if (strtoupper($countrycode) == $locale_region) {
12654 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
12655 return strtolower($locale_language) . '_' . strtoupper($locale_region);
12656 }
12657 }
12658 } else {
12659 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
12660 }
12661
12662 return null;
12663}
12664
12695function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
12696{
12697 global $hookmanager, $db;
12698
12699 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
12700 foreach ($conf->modules_parts['tabs'][$type] as $value) {
12701 $values = explode(':', $value);
12702
12703 $reg = array();
12704 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
12705 if (count($values) !== 6) {
12706 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);
12707 continue;
12708 }
12709
12710 // new declaration with permissions:
12711 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12712 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12713 if ($values[0] != $type) {
12714 continue;
12715 }
12716
12717 $newtab = array();
12718 $postab = $h;
12719 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
12720 $str = $values[1];
12721 $posstart = strpos($str, '(');
12722 if ($posstart > 0) {
12723 $posend = strpos($str, ')');
12724 if ($posstart > 0) {
12725 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
12726 if (is_numeric($res1)) {
12727 $postab = (int) $res1;
12728 $values[1] = '+' . substr($str, $posend + 1);
12729 }
12730 }
12731 }
12732
12733 global $objectoffield; // So we can use $objectoffield int verifCond
12734 $objectoffield = $object;
12735
12736 if (!verifCond($values[4], '2')) {
12737 continue;
12738 }
12739
12740 if ($values[3]) {
12741 if ($filterorigmodule) { // If a filter of module origin has been requested
12742 if (strpos($values[3], '@')) { // This is an external module
12743 if ($filterorigmodule != 'external') {
12744 continue;
12745 }
12746 } else { // This looks a core module
12747 if ($filterorigmodule != 'core') {
12748 continue;
12749 }
12750 }
12751 }
12752 $langs->load($values[3]);
12753 }
12754
12755 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
12756 // If label is "SUBSTITUION_..."
12757 $substitutionarray = array();
12758 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
12759 $label = make_substitutions($reg[1], $substitutionarray);
12760 } else {
12761 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
12762 $labeltemp = explode(',', $values[2]);
12763 $label = $langs->trans($labeltemp[0]);
12764
12765 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
12766 dol_include_once($labeltemp[2]);
12767 $classtoload = $labeltemp[1];
12768 if (class_exists($classtoload)) {
12769 $obj = new $classtoload($db);
12770 $function = $labeltemp[3];
12771 if ($obj && $function && method_exists($obj, $function)) {
12772 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12773 $nbrec = $obj->$function($object->id, $obj);
12774 if (!empty($nbrec)) {
12775 $label .= '<span class="badge marginleftonlyshort">' . $nbrec . '</span>';
12776 }
12777 }
12778 }
12779 }
12780 }
12781 $url = preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]);
12782 $link = parse_url($url);
12783 $query = [];
12784 if (isset($link['query'])) {
12785 parse_str($link['query'], $query);
12786 }
12787 $newtab[0] = dolBuildUrl(dol_buildpath($link['path'], 1), $query);
12788 $newtab[1] = $label;
12789 $newtab[2] = str_replace('+', '', $values[1]);
12790 $h++;
12791
12792 // set tab at its position
12793 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
12794 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
12795 if ($values[0] != $type) {
12796 continue;
12797 }
12798 $tabname = str_replace('-', '', $values[1]);
12799 foreach ($head as $key => $val) {
12800 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
12801 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
12802 if ($head[$key][2] == $tabname && $condition) {
12803 unset($head[$key]);
12804 break;
12805 }
12806 }
12807 }
12808 }
12809 }
12810
12811 // No need to make a return $head. Var is modified as a reference
12812 if (!empty($hookmanager)) {
12813 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
12814 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
12815 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
12816 if ($reshook > 0) { // Hook ask to replace completely the array
12817 $head = $hookmanager->resArray;
12818 } else { // Hook
12819 $head = array_merge($head, $hookmanager->resArray);
12820 }
12821 $h = count($head);
12822 }
12823}
12824
12836function printCommonFooter($zone = 'private')
12837{
12838 global $conf, $hookmanager, $user, $langs;
12839 global $action;
12840 global $micro_start_time;
12841
12842 if ($zone == 'private') {
12843 print "\n" . '<!-- Common footer for private page -->' . "\n";
12844 } else {
12845 print "\n" . '<!-- Common footer for public page -->' . "\n";
12846 }
12847
12848 // A div to store page_y POST parameter so we can read it using javascript
12849 print "\n<!-- A div to store page_y POST parameter -->\n";
12850 print '<div id="page_y" style="display: none;">' . (GETPOST('page_y') ? GETPOST('page_y') : '') . '</div>' . "\n";
12851
12852 $parameters = array('zone' => $zone);
12853 $tmpobject = null;
12854 // @phan-suppress-next-line PhanPluginConstantVariableNull
12855 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters, $tmpobject, $action); // Note that $action and $object may have been modified by some hooks
12856 if (empty($reshook)) {
12857 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
12858 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
12859 }
12860
12861 print "\n";
12862 if (!empty($conf->use_javascript_ajax)) {
12863 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
12864 print '<script>' . "\n";
12865 print 'jQuery(document).ready(function() {' . "\n";
12866
12867 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
12868 print "\n";
12869 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */' . "\n";
12870 print 'jQuery("li.menuhider").click(function(event) {';
12871 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }' . "\n";
12872 print ' console.log("We click on .menuhider");' . "\n";
12873 print ' $("body").toggleClass("sidebar-collapse")' . "\n";
12874 print '});' . "\n";
12875 }
12876
12877 // Management of focus and mandatory for fields
12878 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"])))) {
12879 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */' . "\n";
12880 $relativepathstring = $_SERVER["PHP_SELF"];
12881 // Clean $relativepathstring
12882 if (constant('DOL_URL_ROOT')) {
12883 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
12884 }
12885 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
12886 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
12887 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
12888
12889 if (!empty($user->default_values[$relativepathstring]['focus'])) {
12890 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
12891 $qualified = 0;
12892 if ($defkey != '_noquery_') {
12893 $tmpqueryarraytohave = explode('&', $defkey);
12894 $foundintru = 0;
12895 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12896 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12897 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12898 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12899 $foundintru = 1;
12900 }
12901 }
12902 if (!$foundintru) {
12903 $qualified = 1;
12904 }
12905 //var_dump($defkey.'-'.$qualified);
12906 } else {
12907 $qualified = 1;
12908 }
12909
12910 if ($qualified) {
12911 print 'console.log("set the focus by executing jQuery(...).focus();")' . "\n";
12912 foreach ($defval as $paramkey => $paramval) {
12913 // Set focus on field
12914 print 'jQuery("input[name=\'' . $paramkey . '\']").focus();' . "\n";
12915 print 'jQuery("textarea[name=\'' . $paramkey . '\']").focus();' . "\n"; // TODO KO with ckeditor
12916 print 'jQuery("select[name=\'' . $paramkey . '\']").focus();' . "\n"; // Not really useful, but we keep it in case of.
12917 }
12918 }
12919 }
12920 }
12921 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12922 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12923 $qualified = 0;
12924 if ($defkey != '_noquery_') {
12925 $tmpqueryarraytohave = explode('&', $defkey);
12926 $foundintru = 0;
12927 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12928 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12929 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12930 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12931 $foundintru = 1;
12932 }
12933 }
12934 if (!$foundintru) {
12935 $qualified = 1;
12936 }
12937 //var_dump($defkey.'-'.$qualified);
12938 } else {
12939 $qualified = 1;
12940 }
12941
12942 if ($qualified) {
12943 print 'console.log("set the js code to manage fields that are set as mandatory");' . "\n";
12944
12945 foreach ($defval as $paramkey => $paramval) {
12946 // Solution 1: Add handler on submit to check if mandatory fields are empty
12947 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12948 print "form.on('submit', function(event) {
12949 var submitter = \$(this).find(':submit:focus').get(0);
12950 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12951
12952 if (buttonName == 'cancel') {
12953 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12954 return true;
12955 }
12956
12957 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');
12958
12959 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12960 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12961
12962 if (tmptypefield == 'textarea') {
12963 // We must instead check the content of ckeditor
12964 var tmpeditor = CKEDITOR.instances['" . dol_escape_js($paramkey) . "'];
12965 if (tmpeditor) {
12966 tmpvalue = tmpeditor.getData();
12967 console.log('For textarea tmpvalue is '+tmpvalue);
12968 }
12969 }
12970
12971 let tmpvalueisempty = false;
12972 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
12973 tmpvalueisempty = true;
12974 }
12975 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
12976 tmpvalueisempty = true;
12977 }
12978 if (tmpvalueisempty && buttonName !== 'cancel') {
12979 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
12980 event.preventDefault(); // Stop submission of form to allow custom code to decide.
12981 event.stopPropagation(); // Stop other handlers.
12982
12983 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
12984
12985 return false;
12986 }
12987 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
12988 return true;
12989 });
12990 \n";
12991
12992 // Solution 2: Add property 'required' on input
12993 // so browser will check value and try to focus on it when submitting the form.
12994 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
12995 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12996 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12997 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
12998 //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";
12999 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
13000 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
13001 // Add 'field required' class on closest td for all input elements : input, textarea and select
13002 //print '}, 500);'; // 500 milliseconds delay
13003
13004 // Now set the class "fieldrequired"
13005 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
13006 }
13007
13008 // If we submit using the cancel button, we remove the required attributes
13009 print 'jQuery("input[name=\'cancel\']").click(function() {
13010 console.log("We click on cancel button so removed all required attribute");
13011 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
13012 });' . "\n";
13013 }
13014 }
13015 }
13016 }
13017
13018 print '});' . "\n";
13019
13020 // End of tuning
13021 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
13022 print "\n";
13023 print "/* JS CODE TO ENABLE to add memory info */\n";
13024 print 'window.console && console.log("';
13025 if (getDolGlobalString('MEMCACHED_SERVER')) {
13026 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER') . ' - ';
13027 }
13028 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
13029 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
13030 $micro_end_time = microtime(true);
13031 print ' - Build time: ' . ceil(1000 * ($micro_end_time - $micro_start_time)) . ' ms';
13032 }
13033
13034 if (function_exists("memory_get_usage")) {
13035 print ' - Mem: ' . memory_get_usage(); // Do not use true here, it seems it takes the peak amount
13036 }
13037 if (function_exists("memory_get_peak_usage")) {
13038 print ' - Real mem peak: ' . memory_get_peak_usage(true);
13039 }
13040 if (function_exists("zend_loader_file_encoded")) {
13041 print ' - Zend encoded file: ' . (zend_loader_file_encoded() ? 'yes' : 'no');
13042 }
13043 print '");' . "\n";
13044 }
13045
13046 print "\n" . '</script>' . "\n";
13047
13048 // Google Analytics
13049 // TODO Remove this, can be replaced with the hook printCommonFooter
13050 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
13051 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
13052 foreach ($tmptagarray as $tmptag) {
13053 print "\n";
13054 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
13055 print '
13056 <!-- Global site tag (gtag.js) - Google Analytics -->
13057 <script nonce="' . getNonce() . '" async src="https://www.googletagmanager.com/gtag/js?id=' . trim($tmptag) . '"></script>
13058 <script>
13059 window.dataLayer = window.dataLayer || [];
13060 function gtag(){dataLayer.push(arguments);}
13061 gtag(\'js\', new Date());
13062
13063 gtag(\'config\', \'' . trim($tmptag) . '\');
13064 </script>';
13065 print "\n";
13066 }
13067 }
13068 }
13069
13070 // Add Xdebug coverage of code
13071 if (defined('XDEBUGCOVERAGE')) {
13072 print_r(xdebug_get_code_coverage());
13073 }
13074
13075 // Output string from hooks
13076 if (!empty($hookmanager->resPrint)) {
13077 print $hookmanager->resPrint;
13078 }
13079
13080 // Add DebugBar data
13081 if ($user->hasRight('debugbar', 'read')) {
13082 global $debugbar;
13083 if ($debugbar instanceof DebugBar\DebugBar) {
13084 if (isset($debugbar['time'])) {
13085 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
13086 $debugbar['time']->stopMeasure('pageaftermaster');
13087 }
13088 print '<!-- Output debugbar data -->' . "\n";
13089 $renderer = $debugbar->getJavascriptRenderer();
13090 print $renderer->render();
13091 }
13092 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
13093 print "\n";
13094 print "<!-- Start of log output\n";
13095 //print '<div class="hidden">'."\n";
13096 foreach ($conf->logbuffer as $logline) {
13097 print $logline . "<br>\n";
13098 }
13099 //print '</div>'."\n";
13100 print "End of log output -->\n";
13101 }
13102 }
13103}
13104
13114function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
13115{
13116 if (is_null($string)) {
13117 return array();
13118 }
13119
13120 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
13121 // This is a regex string
13122 $newdelimiter = $delimiter;
13123 } else {
13124 // This is a simple string
13125 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
13126 $newdelimiter = preg_quote($delimiter, '/');
13127 }
13128
13129 if ($a = preg_split('/' . $newdelimiter . '/', $string)) {
13130 $ka = array();
13131 foreach ($a as $s) { // each part
13132 if ($s) {
13133 if ($pos = strpos($s, $kv)) { // key/value delimiter
13134 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
13135 } else { // key delimiter not found
13136 $ka[] = trim($s);
13137 }
13138 }
13139 }
13140 return $ka;
13141 }
13142
13143 return array();
13144}
13145
13153function dolExplodeKeepIfQuotes($input)
13154{
13155 // Use regexp to capture words and section in quotes
13156 $matches = array();
13157 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
13158
13159 // Merge result and delete empty values
13160
13161 $result = array_map(
13168 static function ($a, $b, $c) {
13169 if ($a !== '') {
13170 return $a;
13171 }
13172 if ($b !== '') {
13173 return $b;
13174 }
13175 if ($c !== '') {
13176 return $c;
13177 }
13178 return '';
13179 },
13180 $matches[1],
13181 $matches[2],
13182 $matches[3]
13183 );
13184 return array_values(array_filter(
13185 $result,
13192 static function ($val) {
13193 return $val !== '';
13194 }
13195 ));
13196}
13197
13198
13205function dol_set_focus($selector)
13206{
13207 print "\n" . '<!-- Set focus onto a specific field -->' . "\n";
13208 print '<script nonce="' . getNonce() . '">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("' . dol_escape_js($selector) . '").focus(); });</script>' . "\n";
13209}
13210
13211
13219function dol_getmypid()
13220{
13221 if (!function_exists('getmypid')) {
13222 return mt_rand(99900000, 99965535);
13223 } else {
13224 return getmypid(); // May be a number on 64 bits (depending on OS)
13225 }
13226}
13227
13250function natural_search($fields, $value, $mode = 0, $nofirstand = 0, $sqltoadd = '')
13251{
13252 global $db, $langs;
13253
13254 $value = trim($value);
13255
13256 if ($mode == 0) {
13257 $value = preg_replace('/\*/', '%', $value); // Replace * with %
13258 }
13259 if ($mode == 1) {
13260 $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
13261 }
13262
13263 $value = preg_replace('/\s*\|\s*/', '|', $value);
13264
13265 // Split criteria on ' ' but not if we are inside quotes.
13266 // For mode 3, the split is done later on the , only and not on the ' '.
13267 if ($mode != -3 && $mode != 3) {
13268 $crits = dolExplodeKeepIfQuotes($value);
13269 } else {
13270 $crits = array($value);
13271 }
13272
13273 $res = '';
13274 if (!is_array($fields)) {
13275 $fields = array($fields);
13276 }
13277 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
13278 foreach ($crits as $crit) { // Loop on each AND criteria
13279 $crit = trim($crit);
13280 $i2 = 0; // count the nb of valid criteria added for this this first criteria
13281 $newres = '';
13282
13283 foreach ($fields as $field) {
13284 if ($mode == 1) {
13285 $tmpcrits = explode('|', $crit);
13286 $i3 = 0; // count the nb of valid criteria added for this current field
13287 foreach ($tmpcrits as $tmpcrit) {
13288 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13289 continue;
13290 }
13291 $tmpcrit = trim($tmpcrit);
13292
13293 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13294
13295 $operator = '=';
13296 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
13297
13298 $reg = array();
13299 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
13300 if (!empty($reg[1])) {
13301 $operator = $reg[1];
13302 }
13303 if ($newcrit != '') {
13304 $numnewcrit = price2num($newcrit);
13305 if (is_numeric($numnewcrit)) {
13306 $newres .= $db->sanitize($field) . ' ' . $operator . ' ' . ((float) $numnewcrit); // should be a numeric
13307 } else {
13308 $newres .= '1 = 2'; // force false, we received a corrupted data
13309 }
13310 $i3++; // a criteria was added to string
13311 }
13312 }
13313 $i2++; // a criteria for 1 more field was added to string
13314 } elseif ($mode == 2 || $mode == -2) {
13315 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
13316 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -2 ? 'NOT ' : '');
13317 $newres .= $crit ? "IN (" . $db->sanitize($db->escape($crit)) . ")" : "IN (0)";
13318 if ($mode == -2) {
13319 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13320 }
13321 $i2++; // a criteria for 1 more field was added to string
13322 } elseif ($mode == 3 || $mode == -3) {
13323 $tmparray = explode(',', $crit);
13324 if (count($tmparray)) {
13325 $listofcodes = '';
13326 foreach ($tmparray as $val) {
13327 $val = trim($val);
13328 if ($val) { // TODO Test with if ($val !== '') {
13329 $listofcodes .= ($listofcodes ? ',' : '');
13330 $listofcodes .= "'" . $db->escape($val) . "'";
13331 }
13332 }
13333 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -3 ? 'NOT ' : '') . "IN (" . $db->sanitize($listofcodes, 1, 0, 1) . ")";
13334 $i2++; // a criteria for 1 more field was added to string
13335 }
13336 if ($mode == -3) {
13337 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13338 }
13339 } elseif ($mode == 4) {
13340 $tmparray = explode(',', $crit);
13341 if (count($tmparray)) {
13342 $listofcodes = '';
13343 foreach ($tmparray as $val) {
13344 $val = trim($val);
13345 if ($val) {
13346 $newres .= ($i2 > 0 ? " OR (" : "(") . $db->sanitize($field) . " LIKE '" . $db->escape($val) . ",%'";
13347 $newres .= ' OR ' . $db->sanitize($field) . " = '" . $db->escape($val) . "'";
13348 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . "'";
13349 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . ",%'";
13350 $newres .= ')';
13351 $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)
13352 }
13353 }
13354 }
13355 } else { // $mode=0
13356 $tmpcrits = explode('|', $crit);
13357 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
13358 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
13359 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13360 continue;
13361 }
13362 $tmpcrit = trim($tmpcrit);
13363
13364 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
13365 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
13366 } else {
13367 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13368 }
13369
13370 $isSellist = false;
13371 $table = $label = $key = null;
13372
13373 if (strpos($field, 'ef.') === 0) {
13374 $extrafieldName = substr($field, 3);
13375 $extrafields = new ExtraFields($db);
13376 $extrafields->fetch_name_optionals_label('product');
13377
13378 if (isset($extrafields->attributes['product']['type'][$extrafieldName]) && $extrafields->attributes['product']['type'][$extrafieldName] === 'sellist') {
13379 $isSellist = true;
13380 $paramArray = $extrafields->attributes['product']['param'][$extrafieldName]['options'] ?? [];
13381 $param = array_key_first($paramArray);
13382 list($table, $label, $key) = explode(':', $param);
13383 }
13384 }
13385
13386 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
13387 $newres .= $db->sanitize($field) . " = " . (is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
13388 } else {
13389 $tmpcrit2 = $tmpcrit;
13390 $tmpbefore = '%';
13391 $tmpafter = '%';
13392 $tmps = '';
13393
13394 if ($isSellist) {
13395 $newres .= $field . " IN (SELECT t." . $key . " FROM " . $db->prefix() . $table . " AS t WHERE t." . $label . " LIKE '%" . $db->escape($tmpcrit2) . "%')";
13396 } else {
13397 if (preg_match('/^!/', $tmpcrit)) {
13398 $tmps .= $db->sanitize($field) . " NOT LIKE "; // ! as exclude character
13399 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
13400 } else {
13401 $tmps .= $db->sanitize($field) . " LIKE ";
13402 }
13403 $tmps .= "'";
13404
13405 if (preg_match('/^[\^\$]/', $tmpcrit)) {
13406 $tmpbefore = '';
13407 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
13408 }
13409 if (preg_match('/[\^\$]$/', $tmpcrit)) {
13410 $tmpafter = '';
13411 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
13412 }
13413
13414 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13415 $tmps = "(" . $tmps;
13416 }
13417 $newres .= $tmps;
13418 $newres .= $tmpbefore;
13419 $newres .= $db->escape($tmpcrit2);
13420 $newres .= $tmpafter;
13421 $newres .= "'";
13422 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13423 $newres .= " OR " . $field . " IS NULL)";
13424 }
13425 }
13426 }
13427
13428 $i3++;
13429 }
13430
13431 $i2++; // a criteria for 1 more field was added to string
13432 }
13433 }
13434
13435 if ($sqltoadd) {
13436 $newres .= ($newres ? '' : ' OR ').str_replace('__KEYTOSEARCH__', $crit, $sqltoadd);
13437 }
13438
13439 if ($newres) {
13440 $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') . $newres . ($i2 > 1 ? ')' : '');
13441 }
13442 $i1++;
13443 }
13444 $res = ($nofirstand ? "" : " AND ") . "(" . $res . ")";
13445
13446 return $res;
13447}
13448
13455function showDirectDownloadLink($object)
13456{
13457 global $langs;
13458
13459 $out = '';
13460 $url = $object->getLastMainDocLink($object->element);
13461
13462 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe') . ' <span class="opacitymedium">' . $langs->trans("DirectDownloadLink") . '</span><br>';
13463 if ($url) {
13464 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="' . $url . '"></div>';
13465 $out .= ajax_autoselect("directdownloadlink", '');
13466 } else {
13467 $out .= '<div class="urllink">' . $langs->trans("FileNotShared") . '</div>';
13468 }
13469
13470 return $out;
13471}
13472
13481function getImageFileNameForSize($file, $extName, $extImgTarget = '')
13482{
13483 $dirName = dirname($file);
13484 if ($dirName == '.') {
13485 $dirName = '';
13486 }
13487
13488 if (!in_array($extName, array('', '_small', '_mini'))) {
13489 return 'Bad parameter extName';
13490 }
13491
13492 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
13493 $fileName = basename($fileName);
13494
13495 if (empty($extImgTarget)) {
13496 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
13497 }
13498 if (empty($extImgTarget)) {
13499 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
13500 }
13501 if (empty($extImgTarget)) {
13502 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
13503 }
13504 if (empty($extImgTarget)) {
13505 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
13506 }
13507 if (empty($extImgTarget)) {
13508 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
13509 }
13510 if (empty($extImgTarget)) {
13511 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
13512 }
13513
13514 if (!$extImgTarget) {
13515 return $file;
13516 }
13517
13518 $subdir = '';
13519 if ($extName) {
13520 $subdir = 'thumbs/';
13521 }
13522
13523 return ($dirName ? $dirName . '/' : '') . $subdir . $fileName . $extName . $extImgTarget; // New filename for thumb
13524}
13525
13526
13536function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
13537{
13538 global $conf, $langs;
13539
13540 if (empty($conf->use_javascript_ajax)) {
13541 return '';
13542 }
13543
13544 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
13545
13546 if ($alldata == 1) {
13547 if ($isAllowedForPreview) {
13548 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));
13549 } else {
13550 return array();
13551 }
13552 }
13553
13554 // old behavior, return a string
13555 if ($isAllowedForPreview) {
13556 $tmpurl = DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : '');
13557 $title = $langs->transnoentities("Preview");
13558 //$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().
13559 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
13560
13561 // 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,
13562 // 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.
13563 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
13564 return 'javascript:' . dol_escape_uri('document_preview(\'' . dol_escape_js($tmpurl) . '\', \'' . dol_escape_js(dol_mimetype($relativepath)) . '\', \'' . dol_escape_js($title) . '\')');
13565 } else {
13566 return '';
13567 }
13568}
13569
13576function getLabelSpecialCode($idcode)
13577{
13578 global $langs;
13579
13580 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
13581 if ($idcode > 10) {
13582 return 'Module ID ' . $idcode;
13583 }
13584 if (!empty($arrayspecialines[$idcode])) {
13585 return $langs->trans($arrayspecialines[$idcode]);
13586 }
13587 return '';
13588}
13589
13598function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
13599{
13600 global $langs;
13601 $out = '<script nonce="' . getNonce() . '">
13602 jQuery(document).ready(function () {
13603 jQuery("' . ((strpos($htmlname, '.') === 0 ? '' : '#') . $htmlname) . '").click(function() { jQuery(this).select(); } );
13604 });
13605 </script>';
13606 if ($addlink) {
13607 if ($textonlink === 'image') {
13608 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . img_picto('', 'globe') . '</a>';
13609 } else {
13610 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . $langs->trans("Link") . '</a>';
13611 }
13612 }
13613 return $out;
13614}
13615
13623function dolIsAllowedForPreview($file)
13624{
13625 // Check .noexe extension in filename
13626 if (preg_match('/\.noexe$/i', $file)) {
13627 return 0;
13628 }
13629
13630 // Check mime types
13631 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp', 'webm', 'mp4');
13632 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
13633 $mime_preview[] = 'svg+xml';
13634 }
13635 //$mime_preview[]='vnd.oasis.opendocument.presentation';
13636 //$mime_preview[]='archive';
13637 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
13638 if ($num_mime !== false) {
13639 return 1;
13640 }
13641
13642 // By default, not allowed for preview
13643 return 0;
13644}
13645
13646
13656function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
13657{
13658 $mime = $default;
13659 $imgmime = 'other.png';
13660 $famime = 'file-o';
13661 $srclang = '';
13662
13663 $tmpfile = preg_replace('/\.noexe$/', '', $file);
13664
13665 // Plain text files
13666 if (preg_match('/\.txt$/i', $tmpfile)) {
13667 $mime = 'text/plain';
13668 $imgmime = 'text.png';
13669 $famime = 'file-alt';
13670 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
13671 $mime = 'text/richtext';
13672 $imgmime = 'text.png';
13673 $famime = 'file-alt';
13674 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
13675 $mime = 'text/csv';
13676 $imgmime = 'text.png';
13677 $famime = 'file-csv';
13678 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
13679 $mime = 'text/tab-separated-values';
13680 $imgmime = 'text.png';
13681 $famime = 'file-alt';
13682 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
13683 $mime = 'text/plain';
13684 $imgmime = 'text.png';
13685 $famime = 'file-alt';
13686 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
13687 $mime = 'text/plain';
13688 $imgmime = 'text.png';
13689 $srclang = 'ini';
13690 $famime = 'file-alt';
13691 } elseif (preg_match('/\.md$/i', $tmpfile)) {
13692 $mime = 'text/plain';
13693 $imgmime = 'text.png';
13694 $srclang = 'md';
13695 $famime = 'file-alt';
13696 } elseif (preg_match('/\.css$/i', $tmpfile)) {
13697 $mime = 'text/css';
13698 $imgmime = 'css.png';
13699 $srclang = 'css';
13700 $famime = 'file-alt';
13701 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
13702 $mime = 'text/plain';
13703 $imgmime = 'text.png';
13704 $srclang = 'lang';
13705 $famime = 'file-alt';
13706 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
13707 $mime = 'text/plain';
13708 $imgmime = 'text.png';
13709 $famime = 'file-alt';
13710 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
13711 $mime = 'text/html';
13712 $imgmime = 'html.png';
13713 $srclang = 'html';
13714 $famime = 'file-alt';
13715 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
13716 $mime = 'text/xml';
13717 $imgmime = 'other.png';
13718 $srclang = 'xml';
13719 $famime = 'file-alt';
13720 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
13721 $mime = 'text/xml';
13722 $imgmime = 'other.png';
13723 $srclang = 'xaml';
13724 $famime = 'file-alt';
13725 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
13726 $mime = 'text/plain';
13727 $imgmime = 'text.png';
13728 $srclang = 'bas';
13729 $famime = 'file-code';
13730 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
13731 $mime = 'text/plain';
13732 $imgmime = 'text.png';
13733 $srclang = 'c';
13734 $famime = 'file-code';
13735 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
13736 $mime = 'text/plain';
13737 $imgmime = 'text.png';
13738 $srclang = 'cpp';
13739 $famime = 'file-code';
13740 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
13741 $mime = 'text/plain';
13742 $imgmime = 'text.png';
13743 $srclang = 'cs';
13744 $famime = 'file-code';
13745 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
13746 $mime = 'text/plain';
13747 $imgmime = 'text.png';
13748 $srclang = 'h';
13749 $famime = 'file-code';
13750 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
13751 $mime = 'text/plain';
13752 $imgmime = 'text.png';
13753 $srclang = 'java';
13754 $famime = 'file-code';
13755 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
13756 $mime = 'text/plain';
13757 $imgmime = 'php.png';
13758 $srclang = 'php';
13759 $famime = 'file-code';
13760 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
13761 $mime = 'text/plain';
13762 $imgmime = 'php.png';
13763 $srclang = 'php';
13764 $famime = 'file-code';
13765 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
13766 $mime = 'text/plain';
13767 $imgmime = 'pl.png';
13768 $srclang = 'perl';
13769 $famime = 'file-code';
13770 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
13771 $mime = 'text/plain';
13772 $imgmime = 'text.png';
13773 $srclang = 'sql';
13774 $famime = 'file-code';
13775 } elseif (preg_match('/\.js$/i', $tmpfile)) {
13776 $mime = 'text/x-javascript';
13777 $imgmime = 'jscript.png';
13778 $srclang = 'js';
13779 $famime = 'file-code';
13780 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
13781 $mime = 'application/vnd.oasis.opendocument.presentation';
13782 $imgmime = 'ooffice.png';
13783 $famime = 'file-powerpoint';
13784 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
13785 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
13786 $imgmime = 'ooffice.png';
13787 $famime = 'file-excel';
13788 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
13789 $mime = 'application/vnd.oasis.opendocument.text';
13790 $imgmime = 'ooffice.png';
13791 $famime = 'file-word';
13792 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
13793 $mime = 'application/msaccess';
13794 $imgmime = 'mdb.png';
13795 $famime = 'file';
13796 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
13797 $mime = 'application/msword';
13798 $imgmime = 'doc.png';
13799 $famime = 'file-word';
13800 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
13801 $mime = 'application/msword';
13802 $imgmime = 'doc.png';
13803 $famime = 'file-word';
13804 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
13805 $mime = 'application/vnd.ms-excel';
13806 $imgmime = 'xls.png';
13807 $famime = 'file-excel';
13808 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
13809 $mime = 'application/vnd.ms-excel';
13810 $imgmime = 'xls.png';
13811 $famime = 'file-excel';
13812 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
13813 $mime = 'application/vnd.ms-excel';
13814 $imgmime = 'xls.png';
13815 $famime = 'file-excel';
13816 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
13817 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
13818 $imgmime = 'xls.png';
13819 $famime = 'file-excel';
13820 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
13821 $mime = 'application/vnd.ms-powerpoint';
13822 $imgmime = 'ppt.png';
13823 $famime = 'file-powerpoint';
13824 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
13825 $mime = 'application/x-mspowerpoint';
13826 $imgmime = 'ppt.png';
13827 $famime = 'file-powerpoint';
13828 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
13829 $mime = 'application/pdf';
13830 $imgmime = 'pdf.png';
13831 $famime = 'file-pdf';
13832 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
13833 $mime = 'text/x-bat';
13834 $imgmime = 'script.png';
13835 $srclang = 'dos';
13836 $famime = 'file-code';
13837 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
13838 $mime = 'text/x-sh';
13839 $imgmime = 'script.png';
13840 $srclang = 'bash';
13841 $famime = 'file-code';
13842 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
13843 $mime = 'text/x-ksh';
13844 $imgmime = 'script.png';
13845 $srclang = 'bash';
13846 $famime = 'file-code';
13847 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
13848 $mime = 'text/x-bash';
13849 $imgmime = 'script.png';
13850 $srclang = 'bash';
13851 $famime = 'file-code';
13852 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
13853 $mime = 'image/x-icon';
13854 $imgmime = 'image.png';
13855 $famime = 'file-image';
13856 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
13857 $mime = 'image/jpeg';
13858 $imgmime = 'image.png';
13859 $famime = 'file-image';
13860 } elseif (preg_match('/\.png$/i', $tmpfile)) {
13861 $mime = 'image/png';
13862 $imgmime = 'image.png';
13863 $famime = 'file-image';
13864 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
13865 $mime = 'image/gif';
13866 $imgmime = 'image.png';
13867 $famime = 'file-image';
13868 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
13869 $mime = 'image/bmp';
13870 $imgmime = 'image.png';
13871 $famime = 'file-image';
13872 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
13873 $mime = 'image/tiff';
13874 $imgmime = 'image.png';
13875 $famime = 'file-image';
13876 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
13877 $mime = 'image/svg+xml';
13878 $imgmime = 'image.png';
13879 $famime = 'file-image';
13880 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
13881 $mime = 'image/webp';
13882 $imgmime = 'image.png';
13883 $famime = 'file-image';
13884 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
13885 $mime = 'text/calendar';
13886 $imgmime = 'other.png';
13887 $famime = 'file-alt';
13888 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
13889 $mime = 'text/calendar';
13890 $imgmime = 'other.png';
13891 $famime = 'file-alt';
13892 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
13893 $mime = 'application/x-bittorrent';
13894 $imgmime = 'other.png';
13895 $famime = 'file-o';
13896 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
13897 $mime = 'audio';
13898 $imgmime = 'audio.png';
13899 $famime = 'file-audio';
13900 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
13901 $mime = 'video/mp4';
13902 $imgmime = 'video.png';
13903 $famime = 'file-video';
13904 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
13905 $mime = 'video/ogg';
13906 $imgmime = 'video.png';
13907 $famime = 'file-video';
13908 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
13909 $mime = 'video/webm';
13910 $imgmime = 'video.png';
13911 $famime = 'file-video';
13912 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
13913 $mime = 'video/x-msvideo';
13914 $imgmime = 'video.png';
13915 $famime = 'file-video';
13916 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
13917 $mime = 'video/divx';
13918 $imgmime = 'video.png';
13919 $famime = 'file-video';
13920 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
13921 $mime = 'video/xvid';
13922 $imgmime = 'video.png';
13923 $famime = 'file-video';
13924 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
13925 $mime = 'video';
13926 $imgmime = 'video.png';
13927 $famime = 'file-video';
13928 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
13929 // application/xxx where zzz is zip, ...
13930 $mime = 'archive';
13931 $imgmime = 'archive.png';
13932 $famime = 'file-archive';
13933 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
13934 $mime = 'application/octet-stream';
13935 $imgmime = 'other.png';
13936 $famime = 'file-o';
13937 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
13938 $mime = 'library';
13939 $imgmime = 'library.png';
13940 $famime = 'file-o';
13941 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13942 $mime = 'error';
13943 $imgmime = 'error.png';
13944 $famime = 'file-alt';
13945 }
13946
13947 if ($famime == 'file-o') {
13948 // file-o seems to not work in fontawesome 5
13949 $famime = 'file';
13950 }
13951
13952 // Return mimetype string
13953 switch ((int) $mode) {
13954 case 1:
13955 $tmp = explode('/', $mime);
13956 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13957 case 2:
13958 return $imgmime;
13959 case 3:
13960 return $srclang;
13961 case 4:
13962 return $famime;
13963 }
13964 return $mime;
13965}
13966
13978function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
13979{
13980 global $conf, $db;
13981
13982 $tablename = preg_replace('/^' . preg_quote(MAIN_DB_PREFIX, '/') . '/', '', $tablename); // Clean name of table for backward compatibility.
13983
13984 $dictvalues = (isset($conf->cache['dictvalues_' . $tablename]) ? $conf->cache['dictvalues_' . $tablename] : null);
13985
13986 if (is_null($dictvalues)) {
13987 $dictvalues = array();
13988
13989 $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
13990 if ($checkentity) {
13991 $sql .= ' AND entity IN (0,' . getEntity($tablename) . ')';
13992 }
13993
13994 $resql = $db->query($sql);
13995 if ($resql) {
13996 while ($obj = $db->fetch_object($resql)) {
13997 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
13998 }
13999 } else {
14000 dol_print_error($db);
14001 }
14002
14003 $conf->cache['dictvalues_' . $tablename] = $dictvalues;
14004 }
14005
14006 if (!empty($dictvalues[$id])) {
14007 // Found
14008 $tmp = $dictvalues[$id];
14009 return (property_exists($tmp, $field) ? $tmp->$field : '');
14010 } else {
14011 // Not found
14012 return '';
14013 }
14014}
14015
14022function colorIsLight($stringcolor)
14023{
14024 $stringcolor = str_replace('#', '', $stringcolor);
14025 $res = -1;
14026 if (!empty($stringcolor)) {
14027 $res = 0;
14028 $tmp = explode(',', $stringcolor);
14029 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
14030 $r = $tmp[0];
14031 $g = $tmp[1];
14032 $b = $tmp[2];
14033 } else {
14034 $hexr = $stringcolor[0] . $stringcolor[1];
14035 $hexg = $stringcolor[2] . $stringcolor[3];
14036 $hexb = $stringcolor[4] . $stringcolor[5];
14037 $r = hexdec($hexr);
14038 $g = hexdec($hexg);
14039 $b = hexdec($hexb);
14040 }
14041 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
14042 if ($bright > 0.6) {
14043 $res = 1;
14044 }
14045 }
14046 return $res;
14047}
14048
14057function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
14058{
14059 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
14060 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
14061 if (empty($menuentry['enabled'])) {
14062 return 0; // Entry disabled by condition
14063 }
14064 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
14065 $tmploops = explode('|', $menuentry['module']);
14066 $found = 0;
14067 foreach ($tmploops as $tmploop) {
14068 if (in_array($tmploop, $listofmodulesforexternal)) {
14069 $found++;
14070 break;
14071 }
14072 }
14073 if (!$found) {
14074 return 0; // Entry is for menus all excluded to external users
14075 }
14076 }
14077 if (!$menuentry['perms'] && $type_user) {
14078 return 0; // No permissions and user is external
14079 }
14080 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
14081 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
14082 }
14083 if (!$menuentry['perms']) {
14084 return 2; // No permissions and user is external
14085 }
14086 return 1;
14087}
14088
14096function roundUpToNextMultiple($n, $x = 5)
14097{
14098 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
14099 return (int) $result;
14100}
14101
14113function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
14114{
14115 $csstouse = 'badge';
14116 $csstouse .= (!empty($mode) ? ' badge-' . $mode : '');
14117 $csstouse .= (!empty($type) ? ' badge-' . $type : '');
14118 $csstouse .= (empty($params['css']) ? '' : ' ' . $params['css']);
14119
14120 $attr = array(
14121 'class' => $csstouse
14122 );
14123
14124 if (empty($html)) {
14125 $html = $label;
14126 }
14127
14128 if (!empty($url)) {
14129 $attr['href'] = $url;
14130 }
14131
14132 if ($mode === 'dot') {
14133 $attr['class'] .= ' classfortooltip';
14134 $attr['title'] = $html;
14135 $attr['aria-label'] = $label;
14136 $html = '';
14137 }
14138
14139 // Override attr
14140 if (!empty($params['attr']) && is_array($params['attr'])) {
14141 foreach ($params['attr'] as $key => $value) {
14142 if ($key == 'class') {
14143 $attr['class'] .= ' ' . $value;
14144 } elseif ($key == 'classOverride') {
14145 $attr['class'] = $value;
14146 } else {
14147 $attr[$key] = $value;
14148 }
14149 }
14150 }
14151
14152 // TODO: add hook
14153
14154 // escape all attribute
14155 $attr = array_map('dolPrintHTMLForAttribute', $attr);
14156
14157 $TCompiledAttr = array();
14158 foreach ($attr as $key => $value) {
14159 $TCompiledAttr[] = $key . '="' . $value . '"';
14160 }
14161
14162 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
14163
14164 $tag = !empty($url) ? 'a' : 'span';
14165
14166 return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
14167}
14168
14169
14182function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
14183{
14184 global $conf;
14185
14186 $return = '';
14187 $dolGetBadgeParams = array();
14188
14189 if (!empty($params['badgeParams'])) {
14190 $dolGetBadgeParams = $params['badgeParams'];
14191 }
14192
14193 // TODO : add a hook
14194 if ($displayMode == 0) {
14195 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
14196 } elseif ($displayMode == 1) {
14197 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14198 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
14199 // Use status with images (for backward compatibility)
14200 $return = '';
14201 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : $statusLabel) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
14202 $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>' : '');
14203
14204 // For small screen, we always use the short label instead of long label.
14205 if (!empty($conf->dol_optimize_smallscreen)) {
14206 if ($displayMode == 0) {
14207 $displayMode = 1;
14208 } elseif ($displayMode == 4) {
14209 $displayMode = 2;
14210 } elseif ($displayMode == 6) {
14211 $displayMode = 5;
14212 }
14213 }
14214
14215 // For backward compatibility. Image's filename are still in French, so we use this array to convert
14216 $statusImg = array(
14217 'status0' => 'statut0',
14218 'status1' => 'statut1',
14219 'status2' => 'statut2',
14220 'status3' => 'statut3',
14221 'status4' => 'statut4',
14222 'status5' => 'statut5',
14223 'status6' => 'statut6',
14224 'status7' => 'statut7',
14225 'status8' => 'statut8',
14226 'status9' => 'statut9'
14227 );
14228
14229 if (!empty($statusImg[$statusType])) {
14230 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
14231 } else {
14232 $htmlImg = img_picto($statusLabel, $statusType);
14233 }
14234
14235 if ($displayMode === 2) {
14236 $return = $htmlImg . ' ' . $htmlLabelShort;
14237 } elseif ($displayMode === 3) {
14238 $return = $htmlImg;
14239 } elseif ($displayMode === 4) {
14240 $return = $htmlImg . ' ' . $htmlLabel;
14241 } elseif ($displayMode === 5) {
14242 $return = $htmlLabelShort . ' ' . $htmlImg;
14243 } else { // $displayMode >= 6
14244 $return = $htmlLabel . ' ' . $htmlImg;
14245 }
14246 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
14247 // Use new badge
14248 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14249
14250 $dolGetBadgeParams['attr']['class'] = 'badge-status';
14251 if (empty($dolGetBadgeParams['attr']['title'])) {
14252 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
14253 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
14254 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
14255 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
14256 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
14257 }
14258
14259 if ($displayMode == 3) {
14260 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
14261 } elseif ($displayMode === 5) {
14262 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
14263 } else {
14264 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
14265 }
14266 }
14267
14268 return $return;
14269}
14270
14271
14307function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
14308{
14309 global $hookmanager, $action, $object, $langs;
14310
14311 // If $url is an array, we must build a dropdown button or recursively iterate over each value
14312 if (is_array($url)) {
14313 // Loop on $url array to remove entries of disabled modules
14314 foreach ($url as $key => $subbutton) {
14315 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
14316 unset($url[$key]);
14317 }
14318 }
14319
14320 $out = '';
14321
14322 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
14323 foreach ($url as $button) {
14324 if (!empty($button['lang'])) {
14325 $langs->load($button['lang']);
14326 }
14327 $label = $langs->trans($button['label']);
14328 $text = $button['text'] ?? '';
14329 $actionType = $button['actionType'] ?? '';
14330 $tmpUrl = DOL_URL_ROOT . $button['url'] . (empty($params['backtopage']) ? '' : '&amp;backtopage=' . urlencode($params['backtopage']));
14331 $id = $button['id'] ?? '';
14332 $userRight = $button['perm'] ?? 1;
14333 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
14334
14335 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
14336 }
14337 return $out;
14338 }
14339
14340 if (count($url) > 1) {
14341 $out .= '<div class="dropdown inline-block dropdown-holder">';
14342 $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>';
14343 $out .= '<div class="dropdown-content">';
14344 foreach ($url as $subbutton) {
14345 if (!empty($subbutton['lang'])) {
14346 $langs->load($subbutton['lang']);
14347 }
14348
14349 if (!empty($subbutton['urlraw'])) {
14350 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14351 } else {
14352 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14353 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14354 }
14355
14356 $subbuttonparam = array();
14357 if (!empty($subbutton['attr'])) {
14358 $subbuttonparam['attr'] = $subbutton['attr'];
14359 }
14360 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
14361
14362 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
14363 }
14364 $out .= "</div>";
14365 $out .= "</div>";
14366 } else {
14367 foreach ($url as $subbutton) { // Should loop on 1 record only
14368 if (!empty($subbutton['lang'])) {
14369 $langs->load($subbutton['lang']);
14370 }
14371
14372 if (!empty($subbutton['urlraw'])) {
14373 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14374 } else {
14375 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14376 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14377 }
14378
14379 $label = $langs->trans($subbutton['label']);
14380 $text = $subbutton['text'] ?? '';
14381 if (empty($text)) {
14382 $text = $label;
14383 $label = '';
14384 }
14385
14386 $out .= dolGetButtonAction($label, $text, 'default', $tmpurl, '', $subbutton['perm'], $params);
14387 }
14388 }
14389
14390 return $out;
14391 }
14392
14393 // Here, $url is a simple link
14394 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
14395 $class = "dropdown-item";
14396 } else {
14397 $class = 'butAction';
14398 if ($actionType == 'edit') {
14399 $class = 'butAction butActionEdit';
14400 } elseif ($actionType == 'email') {
14401 $class = 'butAction butActionEmail';
14402 } elseif ($actionType == 'clone') {
14403 $class = 'butAction butActionClone';
14404 } elseif ($actionType == 'danger' || $actionType == 'delete') {
14405 $class = 'butAction butActionDelete';
14406 if (!empty($url) && strpos($url, 'token=') === false) {
14407 $url .= '&token=' . newToken();
14408 }
14409 }
14410 }
14411 $attr = array(
14412 'class' => $class,
14413 'href' => empty($url) ? '' : $url,
14414 'title' => $label
14415 );
14416
14417 if (empty($text)) {
14418 $text = $label;
14419 $attr['title'] = ''; // if html not set, using label on title is redundant
14420 } else {
14421 $attr['title'] = $label;
14422 $attr['aria-label'] = $label;
14423 }
14424
14425 if (empty($userRight) || $userRight < 0) {
14426 $attr['class'] = 'butActionRefused';
14427 $attr['href'] = '';
14428 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
14429 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '');
14430 $attr['title'] .= ((empty($userRight) && empty($label)) ? $langs->trans('NotEnoughPermissions') : '');
14431 }
14432
14433 if (!empty($id)) {
14434 $attr['id'] = $id;
14435 }
14436
14437 // Override attr
14438 if (!empty($params['attr']) && is_array($params['attr'])) {
14439 foreach ($params['attr'] as $key => $value) {
14440 if ($key == 'class') {
14441 $attr['class'] .= ' ' . $value;
14442 } elseif ($key == 'classOverride') {
14443 $attr['class'] = $value;
14444 } else {
14445 $attr[$key] = $value;
14446 }
14447 }
14448 }
14449
14450 // automatic add tooltip when title is detected
14451 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
14452 $attr['class'] .= ' classfortooltip';
14453 }
14454
14455 // Js Confirm button
14456 if ($userRight && !empty($params['confirm'])) {
14457 if (!is_array($params['confirm'])) {
14458 $params['confirm'] = array();
14459 }
14460
14461 if (empty($params['confirm']['url'])) {
14462 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
14463 }
14464
14465 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
14466 $attr['data-confirm-url'] = $params['confirm']['url'];
14467 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
14468 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
14469 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
14470 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
14471 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
14472 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
14473
14474 $attr['class'] .= ' butActionConfirm';
14475 }
14476
14477 if (isset($attr['href']) && empty($attr['href'])) {
14478 unset($attr['href']);
14479 }
14480
14481 // TODO replace $TCompiledAttr generation by commonHtmlAttributeBuilder given below
14482 $TCompiledAttr = array();
14483 foreach ($attr as $key => $value) {
14484 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
14485 // Not recommended
14486 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
14487 } elseif ($key == 'href') {
14488 $value = dolPrintHTMLForAttributeUrl($value);
14489 } else {
14490 $value = dolPrintHTMLForAttribute($value);
14491 }
14492
14493 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14494 }
14495 // TODO replace $TCompiledAttr generation by uncomment line below and remove old code
14496 // $TCompiledAttr = commonHtmlAttributeBuilder($attr,$params['use_unsecured_unescapedattr'] ?? []);
14497 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
14498
14499 $tag = !empty($attr['href']) ? 'a' : 'span';
14500
14501 $parameters = array(
14502 'TCompiledAttr' => $TCompiledAttr, // array
14503 'compiledAttributes' => $compiledAttributes, // string
14504 'attr' => $attr,
14505 'tag' => $tag,
14506 'label' => $label,
14507 'html' => $text,
14508 'actionType' => $actionType,
14509 'url' => $url,
14510 'id' => $id,
14511 'userRight' => $userRight,
14512 'params' => $params
14513 );
14514
14515 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14516 if ($reshook < 0) {
14517 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
14518 }
14519
14520 if (empty($reshook)) {
14521 if (dol_textishtml($text)) { // If content already HTML encoded
14522 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
14523 } else {
14524 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
14525 }
14526 } else {
14527 return $hookmanager->resPrint;
14528 }
14529}
14530
14566function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
14567{
14568 $TCompiledAttr = array();
14569 if (empty($attr)) {
14570 return [];
14571 }
14572
14573 foreach ($attr as $key => $value) {
14574 // special boolean attributes case
14575 if (in_array($key, getListOfHtmlBooleanAttributes())) {
14576 if ($value) {
14577 $TCompiledAttr[$key] = $key;
14578 }
14579 continue;
14580 }
14581
14582 if (!empty($unescapedAttr) && in_array($key, $unescapedAttr)) {
14583 // Not recommended
14584 $value = dol_htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE);
14585 } elseif ($key == 'href') {
14586 $value = dolPrintHTMLForAttributeUrl((string) $value);
14587 } else {
14588 $value = dolPrintHTMLForAttribute((string) $value);
14589 }
14590
14591 $TCompiledAttr[$key] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14592 }
14593
14594 return $TCompiledAttr;
14595}
14596
14611function getListOfHtmlBooleanAttributes(): array
14612{
14613 return [
14614 // Input / Form
14615 'checked',
14616 'disabled',
14617 'readonly',
14618 'required',
14619 'autofocus',
14620 'multiple',
14621
14622 // Option
14623 'selected',
14624
14625 // Form / General
14626 'novalidate',
14627 'formnovalidate',
14628
14629 // Media
14630 'autoplay',
14631 'controls',
14632 'loop',
14633 'muted',
14634 'playsinline',
14635
14636 // Other elements
14637 'hidden',
14638 'open',
14639 'ismap',
14640 'reversed',
14641 'allowfullscreen',
14642 'itemscope',
14643 'nomodule',
14644 'defer',
14645 'async',
14646 'default',
14647 'inert',
14648 ];
14649}
14650
14651
14660function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
14661{
14662 if (empty($url)) {
14663 return '';
14664 }
14665
14666 $parsedUrl = parse_url($url);
14667 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
14668 return $url;
14669 }
14670
14671 if (!empty($parsedUrl['query'])) {
14672 // Use parse_str() function to parse the string passed via URL
14673 parse_str($parsedUrl['query'], $urlQuery);
14674 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
14675 $url .= '&amp;backtopage=' . urlencode($params['backtopage']);
14676 }
14677 }
14678
14679 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
14680 $url = DOL_URL_ROOT . $url;
14681 }
14682
14683 return $url;
14684}
14685
14686
14693function dolGetButtonTitleSeparator($moreClass = "")
14694{
14695 return '<span class="button-title-separator ' . $moreClass . '" ></span>';
14696}
14697
14704function getFieldErrorIcon($fieldValidationErrorMsg)
14705{
14706 $out = '';
14707 if (!empty($fieldValidationErrorMsg)) {
14708 $out .= '<span class="field-error-icon classfortooltip" title="' . dol_escape_htmltag($fieldValidationErrorMsg, 1) . '" role="alert" >'; // role alert is used for accessibility
14709 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
14710 $out .= '</span>';
14711 }
14712
14713 return $out;
14714}
14715
14728function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
14729{
14730 global $langs, $user;
14731
14732 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
14733 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
14734 return '';
14735 }
14736 // Fix old picto fa-th-list to use fa-grid-vertical instead
14737 if ($iconClass == 'fa fa-th-list imgforviewmode') {
14738 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
14739 }
14740
14741 $class = 'btnTitle';
14742 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
14743 $class .= ' btnTitlePlus';
14744 }
14745 $useclassfortooltip = 1;
14746
14747 if (!empty($params['morecss'])) {
14748 $class .= ' ' . $params['morecss'];
14749 }
14750
14751 $attr = array(
14752 'class' => $class,
14753 'href' => empty($url) ? '' : $url
14754 );
14755
14756 if (!empty($helpText)) {
14757 $attr['title'] = $helpText;
14758 } elseif ($label) { // empty($attr['title']) &&
14759 $attr['title'] = $label;
14760 $useclassfortooltip = 0;
14761 }
14762
14763 if ($status == 2) {
14764 $attr['class'] .= ' btnTitleSelected';
14765 } elseif ($status <= 0) {
14766 $attr['class'] .= ' refused';
14767
14768 $attr['href'] = '';
14769
14770 if ($status == -1) { // disable
14771 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
14772 } elseif ($status == 0) { // Not enough permissions
14773 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
14774 }
14775 }
14776
14777 if (!empty($attr['title']) && $useclassfortooltip) {
14778 $attr['class'] .= ' classfortooltip';
14779 }
14780
14781 if (!empty($id)) {
14782 $attr['id'] = $id;
14783 }
14784
14785 // Override attr
14786 if (!empty($params['attr']) && is_array($params['attr'])) {
14787 foreach ($params['attr'] as $key => $value) {
14788 if ($key == 'class') {
14789 $attr['class'] .= ' ' . $value;
14790 } elseif ($key == 'classOverride') {
14791 $attr['class'] = $value;
14792 } else {
14793 $attr[$key] = $value;
14794 }
14795 }
14796 }
14797
14798 if (isset($attr['href']) && empty($attr['href'])) {
14799 unset($attr['href']);
14800 }
14801
14802 // TODO : add a hook
14803
14804 // Generate attributes with escapement
14805 $TCompiledAttr = array();
14806 foreach ($attr as $key => $value) {
14807 $TCompiledAttr[] = $key . '="' . dol_escape_htmltag($value) . '"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
14808 }
14809
14810 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
14811
14812 $tag = (empty($attr['href']) ? 'span' : 'a');
14813
14814 $button = '<' . $tag . ' ' . $compiledAttributes . '>';
14815 $button .= '<span class="' . $iconClass . ' valignmiddle btnTitle-icon"></span>';
14816 if (!empty($params['forcenohideoftext'])) {
14817 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label' . (empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '') . '">' . $label . '</span>';
14818 }
14819 $button .= '</' . $tag . '>';
14820
14821 return $button;
14822}
14823
14833function getElementProperties($elementType)
14834{
14835 global $conf, $db, $hookmanager;
14836
14837 $regs = array();
14838
14839 //$element_type='facture';
14840
14841 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
14842
14843 // Parse element/subelement
14844 $module = $elementType;
14845 $element = $elementType;
14846 $subelement = $elementType;
14847 $table_element = $elementType;
14848
14849 // If we ask a resource form external module (instead of default path)
14850 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
14851 $element = $subelement = $regs[1];
14852 $module = $regs[2];
14853 }
14854
14855 // If we ask a resource for a string with an element and a subelement
14856 // Example 'project_task'
14857 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
14858 $module = $element = $regs[1];
14859 $subelement = $regs[2];
14860 }
14861
14862 // Object lines will use parent classpath and module ref
14863 if (substr($elementType, -3) == 'det') {
14864 $module = preg_replace('/det$/', '', $element);
14865 $subelement = preg_replace('/det$/', '', $subelement);
14866 $classpath = $module . '/class';
14867 $classfile = $module;
14868 $classname = preg_replace('/det$/', 'Line', $element);
14869 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'supplier_order', 'commandefournisseur'))) {
14870 $classname = preg_replace('/det$/', 'Ligne', $element);
14871 }
14872 }
14873 // For compatibility and to work with non standard path
14874 if ($elementType == "action" || $elementType == "actioncomm") {
14875 $classpath = 'comm/action/class';
14876 $subelement = 'Actioncomm';
14877 $module = 'agenda';
14878 $table_element = 'actioncomm';
14879 } elseif ($elementType == 'cronjob') {
14880 $classpath = 'cron/class';
14881 $module = 'cron';
14882 $table_element = 'cron';
14883 } elseif ($elementType == 'adherent_type') {
14884 $classpath = 'adherents/class';
14885 $classfile = 'adherent_type';
14886 $module = 'adherent';
14887 $subelement = 'adherent_type';
14888 $classname = 'AdherentType';
14889 $table_element = 'adherent_type';
14890 } elseif ($elementType == 'bank_account') {
14891 $classpath = 'compta/bank/class';
14892 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14893 $classfile = 'account';
14894 $classname = 'Account';
14895 } elseif ($elementType == 'bank_line') {
14896 $classpath = 'compta/bank/class';
14897 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14898 $classfile = 'account';
14899 $classname = 'AccountLine';
14900 } elseif ($elementType == 'category') {
14901 $classpath = 'categories/class';
14902 $module = 'categorie';
14903 $subelement = 'categorie';
14904 $table_element = 'categorie';
14905 } elseif ($elementType == 'contact') {
14906 $classpath = 'contact/class';
14907 $classfile = 'contact';
14908 $module = 'societe';
14909 $subelement = 'contact';
14910 $table_element = 'socpeople';
14911 } elseif ($elementType == 'inventory') {
14912 $module = 'product';
14913 $classpath = 'product/inventory/class';
14914 } elseif ($elementType == 'inventoryline') {
14915 $module = 'product';
14916 $classpath = 'product/inventory/class';
14917 $table_element = 'inventorydet';
14918 $parent_element = 'inventory';
14919 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
14920 $module = 'stock';
14921 $classpath = 'product/stock/class';
14922 $classfile = 'entrepot';
14923 $classname = 'Entrepot';
14924 $table_element = 'entrepot';
14925 } elseif ($elementType == 'project') {
14926 $classpath = 'projet/class';
14927 $module = 'projet';
14928 $table_element = 'projet';
14929 } elseif ($elementType == 'project_task') {
14930 $classpath = 'projet/class';
14931 $module = 'projet';
14932 $subelement = 'task';
14933 $table_element = 'projet_task';
14934 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
14935 $classpath = 'compta/facture/class';
14936 $module = 'facture';
14937 $subelement = 'facture';
14938 $table_element = 'facture';
14939 } elseif ($elementType == 'facturedet') {
14940 $classpath = 'compta/facture/class';
14941 $classfile = 'facture';
14942 $classname = 'FactureLigne';
14943 $module = 'facture';
14944 $table_element = 'facturedet';
14945 $parent_element = 'facture';
14946 } elseif ($elementType == 'facturerec'|| $elementType == 'facture_rec') {
14947 $classpath = 'compta/facture/class';
14948 $classfile = 'facture-rec';
14949 $module = 'facture';
14950 $classname = 'FactureRec';
14951 } elseif ($elementType == 'commande' || $elementType == 'order') {
14952 $classpath = 'commande/class';
14953 $module = 'commande';
14954 $subelement = 'commande';
14955 $table_element = 'commande';
14956 } elseif ($elementType == 'commandedet') {
14957 $classpath = 'commande/class';
14958 $classfile = 'commande';
14959 $classname = 'OrderLine';
14960 $module = 'commande';
14961 $table_element = 'commandedet';
14962 $parent_element = 'commande';
14963 } elseif ($elementType == 'propal') {
14964 $classpath = 'comm/propal/class';
14965 $table_element = 'propal';
14966 } elseif ($elementType == 'propaldet') {
14967 $classpath = 'comm/propal/class';
14968 $classfile = 'propal';
14969 $subelement = 'propaleligne';
14970 $module = 'propal';
14971 $table_element = 'propaldet';
14972 $parent_element = 'propal';
14973 } elseif ($elementType == 'shipping') {
14974 $classpath = 'expedition/class';
14975 $classfile = 'expedition';
14976 $classname = 'Expedition';
14977 $module = 'expedition';
14978 $table_element = 'expedition';
14979 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
14980 $classpath = 'expedition/class';
14981 $classfile = 'expedition';
14982 $classname = 'ExpeditionLigne';
14983 $module = 'expedition';
14984 $table_element = 'expeditiondet';
14985 $parent_element = 'expedition';
14986 } elseif ($elementType == 'delivery_note') {
14987 $classpath = 'delivery/class';
14988 $subelement = 'delivery';
14989 $module = 'expedition';
14990 } elseif ($elementType == 'delivery') {
14991 $classpath = 'delivery/class';
14992 $subelement = 'delivery';
14993 $module = 'expedition';
14994 } elseif ($elementType == 'deliverydet') {
14995 // @todo
14996 } elseif ($elementType == 'supplier_proposal') {
14997 $classpath = 'supplier_proposal/class';
14998 $module = 'supplier_proposal';
14999 $element = 'supplierproposal';
15000 $classfile = 'supplier_proposal';
15001 $subelement = 'supplierproposal';
15002 } elseif ($elementType == 'supplier_proposaldet') {
15003 $classpath = 'supplier_proposal/class';
15004 $module = 'supplier_proposal';
15005 $classfile = 'supplier_proposal';
15006 $classname = 'SupplierProposalLine';
15007 $table_element = 'supplier_proposaldet';
15008 $parent_element = 'supplier_proposal';
15009 } elseif ($elementType == 'contract') {
15010 $classpath = 'contrat/class';
15011 $module = 'contrat';
15012 $subelement = 'contrat';
15013 $table_element = 'contract';
15014 } elseif ($elementType == 'contratdet') {
15015 $classpath = 'contrat/class';
15016 $module = 'contrat';
15017 $table_element = 'contratdet';
15018 $parent_element = 'contrat';
15019 } elseif ($elementType == 'mailing') {
15020 $classpath = 'comm/mailing/class';
15021 $module = 'mailing';
15022 $classfile = 'mailing';
15023 $classname = 'Mailing';
15024 $subelement = '';
15025 } elseif ($elementType == 'member' || $elementType == 'adherent') {
15026 $classpath = 'adherents/class';
15027 $module = 'adherent';
15028 $subelement = 'adherent';
15029 $table_element = 'adherent';
15030 } elseif ($elementType == 'subscription') {
15031 $classpath = 'adherents/class';
15032 $classfile = 'subscription';
15033 $module = 'adherent';
15034 $subelement = 'subscription';
15035 $classname = 'Subscription';
15036 $table_element = 'subscription';
15037 } elseif ($elementType == 'usergroup') {
15038 $classpath = 'user/class';
15039 $module = 'user';
15040 } elseif ($elementType == 'mo' || $elementType == 'mrp') {
15041 $classpath = 'mrp/class';
15042 $classfile = 'mo';
15043 $classname = 'Mo';
15044 $module = 'mrp';
15045 $subelement = '';
15046 $table_element = 'mrp_mo';
15047 } elseif ($elementType == 'mrp_production') {
15048 $classpath = 'mrp/class';
15049 $classfile = 'mo';
15050 $classname = 'MoLine';
15051 $module = 'mrp';
15052 $subelement = '';
15053 $table_element = 'mrp_production';
15054 $parent_element = 'mo';
15055 } elseif ($elementType == 'cabinetmed_cons') {
15056 $classpath = 'cabinetmed/class';
15057 $module = 'cabinetmed';
15058 $subelement = 'cabinetmedcons';
15059 $table_element = 'cabinetmedcons';
15060 } elseif ($elementType == 'fichinter') {
15061 $classpath = 'fichinter/class';
15062 $module = 'ficheinter';
15063 $subelement = 'fichinter';
15064 $table_element = 'fichinter';
15065 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
15066 $classpath = 'resource/class';
15067 $module = 'resource';
15068 $subelement = 'dolresource';
15069 $table_element = 'resource';
15070 } elseif ($elementType == 'opensurvey_sondage') {
15071 $classpath = 'opensurvey/class';
15072 $module = 'opensurvey';
15073 $subelement = 'opensurveysondage';
15074 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' || $elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
15075 $classpath = 'fourn/class';
15076 $module = 'fournisseur';
15077 $classfile = 'fournisseur.commande';
15078 $element = 'order_supplier';
15079 $subelement = '';
15080 $classname = 'CommandeFournisseur';
15081 $table_element = 'commande_fournisseur';
15082 } elseif ($elementType == 'commande_fournisseurdet') {
15083 $classpath = 'fourn/class';
15084 $module = 'fournisseur';
15085 $classfile = 'fournisseur.commande';
15086 $element = 'commande_fournisseurdet';
15087 $subelement = '';
15088 $classname = 'CommandeFournisseurLigne';
15089 $table_element = 'commande_fournisseurdet';
15090 $parent_element = 'commande_fournisseur';
15091 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
15092 $classpath = 'fourn/class';
15093 $module = 'fournisseur';
15094 $classfile = 'fournisseur.facture';
15095 $element = 'invoice_supplier';
15096 $subelement = '';
15097 $classname = 'FactureFournisseur';
15098 $table_element = 'facture_fourn';
15099 } elseif ($elementType == 'facture_fourn_det') {
15100 $classpath = 'fourn/class';
15101 $module = 'fournisseur';
15102 $classfile = 'fournisseur.facture';
15103 $element = 'facture_fourn_det';
15104 $subelement = '';
15105 $classname = 'SupplierInvoiceLine';
15106 $table_element = 'facture_fourn_det';
15107 $parent_element = 'invoice_supplier';
15108 } elseif ($elementType == "service") {
15109 $classpath = 'product/class';
15110 $subelement = 'product';
15111 $table_element = 'product';
15112 } elseif ($elementType == 'salary') {
15113 $classpath = 'salaries/class';
15114 $module = 'salaries';
15115 } elseif ($elementType == 'payment_salary') {
15116 $classpath = 'salaries/class';
15117 $classfile = 'paymentsalary';
15118 $classname = 'PaymentSalary';
15119 $module = 'salaries';
15120 } elseif ($elementType == 'productlot') {
15121 $module = 'productbatch';
15122 $classpath = 'product/stock/class';
15123 $classfile = 'productlot';
15124 $classname = 'Productlot';
15125 $element = 'productlot';
15126 $subelement = '';
15127 $table_element = 'product_lot';
15128 } elseif ($elementType == 'societeaccount') {
15129 $classpath = 'societe/class';
15130 $classfile = 'societeaccount';
15131 $classname = 'SocieteAccount';
15132 $module = 'societe';
15133 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
15134 $classpath = 'website/class';
15135 $classfile = 'websitepage';
15136 $classname = 'Websitepage';
15137 $module = 'website';
15138 $subelement = 'websitepage';
15139 $table_element = 'website_page';
15140 } elseif ($elementType == 'fiscalyear') {
15141 $classpath = 'core/class';
15142 $module = 'accounting';
15143 $subelement = 'fiscalyear';
15144 } elseif ($elementType == 'chargesociales') {
15145 $classpath = 'compta/sociales/class';
15146 $module = 'tax';
15147 $table_element = 'chargesociales';
15148 } elseif ($elementType == 'tva') {
15149 $classpath = 'compta/tva/class';
15150 $module = 'tax';
15151 $subdir = '/vat';
15152 $table_element = 'tva';
15153 } elseif ($elementType == 'emailsenderprofile') {
15154 $module = '';
15155 $classpath = 'core/class';
15156 $classfile = 'emailsenderprofile';
15157 $classname = 'EmailSenderProfile';
15158 $table_element = 'c_email_senderprofile';
15159 $subelement = '';
15160 } elseif ($elementType == 'conferenceorboothattendee') {
15161 $classpath = 'eventorganization/class';
15162 $classfile = 'conferenceorboothattendee';
15163 $classname = 'ConferenceOrBoothAttendee';
15164 $module = 'eventorganization';
15165 } elseif ($elementType == 'conferenceorbooth') {
15166 $classpath = 'eventorganization/class';
15167 $classfile = 'conferenceorbooth';
15168 $classname = 'ConferenceOrBooth';
15169 $module = 'eventorganization';
15170 } elseif ($elementType == 'ccountry') {
15171 $module = '';
15172 $classpath = 'core/class';
15173 $classfile = 'ccountry';
15174 $classname = 'Ccountry';
15175 $table_element = 'c_country';
15176 $subelement = '';
15177 } elseif ($elementType == 'ecmfiles') {
15178 $module = 'ecm';
15179 $classpath = 'ecm/class';
15180 $classfile = 'ecmfiles';
15181 $classname = 'Ecmfiles';
15182 $table_element = 'ecmfiles';
15183 $subelement = '';
15184 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
15185 $module = 'knowledgemanagement';
15186 $classpath = 'knowledgemanagement/class';
15187 $classfile = 'knowledgerecord';
15188 $classname = 'KnowledgeRecord';
15189 $table_element = 'knowledgemanagement_knowledgerecord';
15190 $subelement = '';
15191 } elseif ($elementType == 'customer') {
15192 $module = 'societe';
15193 $classpath = 'societe/class';
15194 $classfile = 'client';
15195 $classname = 'Client';
15196 $table_element = 'societe';
15197 $subelement = '';
15198 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
15199 $module = 'societe';
15200 $classpath = 'fourn/class';
15201 $classfile = 'fournisseur';
15202 $classname = 'Fournisseur';
15203 $table_element = 'societe';
15204 $subelement = '';
15205 } elseif ($elementType == 'recruitmentcandidature') {
15206 $module = 'recruitment';
15207 $classfile = 'recruitmentcandidature';
15208 $classpath = 'recruitment/class';
15209 $classname = 'RecruitmentCandidature';
15210 $subelement = 'recruitmentcandidature';
15211 $subdir = '/recruitmentcandidature';
15212 } elseif ($elementType == 'recruitmentjobposition') {
15213 $module = 'recruitment';
15214 $classfile = 'recruitmentjobposition';
15215 $classpath = 'recruitment/class';
15216 $classname = 'RecruitmentJobPosition';
15217 $subelement = 'recruitmentjobposition';
15218 $subdir = '/recruitmentjobposition';
15219 }
15220
15221
15222 if (empty($classfile)) {
15223 $classfile = strtolower($subelement);
15224 }
15225 if (empty($classname)) {
15226 $classname = ucfirst($subelement);
15227 }
15228 if (empty($classpath)) {
15229 $classpath = $module . '/class';
15230 }
15231
15232 //print 'getElementProperties subdir='.$subdir;
15233
15234 // Set dir_output
15235 if ($module && isset($conf->$module)) { // The generic case
15236 if (!empty($conf->$module->multidir_output[$conf->entity])) {
15237 $dir_output = $conf->$module->multidir_output[$conf->entity];
15238 } elseif (!empty($conf->$module->output[$conf->entity])) {
15239 $dir_output = $conf->$module->output[$conf->entity];
15240 } elseif (!empty($conf->$module->dir_output)) {
15241 $dir_output = $conf->$module->dir_output;
15242 }
15243 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
15244 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
15245 } elseif (!empty($conf->$module->temp[$conf->entity])) {
15246 $dir_temp = $conf->$module->temp[$conf->entity];
15247 } elseif (!empty($conf->$module->dir_temp)) {
15248 $dir_temp = $conf->$module->dir_temp;
15249 }
15250 }
15251
15252 // Overwrite value for special cases
15253 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
15254 $dir_output = $conf->fournisseur->commande->dir_output;
15255 $dir_temp = $conf->fournisseur->commande->dir_temp;
15256 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
15257 $dir_output = $conf->fournisseur->facture->dir_output;
15258 $dir_temp = $conf->fournisseur->facture->dir_temp;
15259 }
15260 $dir_output .= $subdir;
15261 $dir_temp .= $subdir;
15262
15263 $elementProperties = array(
15264 'module' => $module,
15265 'element' => $element,
15266 'table_element' => $table_element,
15267 'subelement' => $subelement,
15268 'classpath' => $classpath,
15269 'classfile' => $classfile,
15270 'classname' => $classname,
15271 'dir_output' => $dir_output,
15272 'dir_temp' => $dir_temp,
15273 'parent_element' => $parent_element,
15274 );
15275
15276
15277 // Add hook
15278 if (!is_object($hookmanager)) {
15279 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
15280 $hookmanager = new HookManager($db);
15281 }
15282 $hookmanager->initHooks(array('elementproperties'));
15283
15284
15285 // Hook params
15286 $parameters = array(
15287 'elementType' => $elementType,
15288 'elementProperties' => $elementProperties
15289 );
15290
15291 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
15292
15293 if ($reshook) {
15294 $elementProperties = $hookmanager->resArray;
15295 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
15296 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
15297 }
15298
15299 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
15300 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
15301 unset($hookmanager->contextarray[$key]);
15302 }
15303
15304 return $elementProperties;
15305}
15306
15319function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
15320{
15321 global $db, $conf;
15322
15323 $ret = 0;
15324
15325 $element_prop = getElementProperties($element_type);
15326
15327 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
15328 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
15329 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
15330 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
15331 // of service and we will return properties of a product.
15332 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
15333 } elseif ($element_prop['module'] == 'societeaccount') {
15334 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
15335 } else {
15336 $ismodenabled = isModEnabled($element_prop['module']);
15337 }
15338 //var_dump('element_type='.$element_type);
15339 //var_dump($element_prop);
15340 //var_dump($element_prop['module'].' '.$ismodenabled);
15341 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
15342 if ($useCache === 1 && $element_id > 0
15343 && !empty($conf->cache['fetchObjectByElement'][$element_type])
15344 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15345 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15346 ) {
15347 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
15348 }
15349
15350 dol_include_once('/' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15351
15352 if (class_exists($element_prop['classname'])) {
15353 $className = $element_prop['classname'];
15354 $objecttmp = new $className($db);
15355 '@phan-var-force CommonObject $objecttmp';
15358 if ($element_id > 0 || !empty($element_ref)) {
15359 $ret = $objecttmp->fetch($element_id, $element_ref);
15360 if ($ret >= 0) {
15361 if (empty($objecttmp->module)) {
15362 $objecttmp->module = $element_prop['module'];
15363 }
15364
15365 if ($useCache > 0) {
15366 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
15367 $conf->cache['fetchObjectByElement'][$element_type] = [];
15368 }
15369
15370 // Manage cache limit
15371 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
15372 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
15373 }
15374
15375 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
15376 }
15377
15378 return $objecttmp;
15379 }
15380 } else {
15381 return $objecttmp; // returned an object without fetch
15382 }
15383 } else {
15384 dol_syslog($element_prop['classname'] . ' doesn\'t exists in /' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15385 return -1;
15386 }
15387 }
15388
15389 return $ret;
15390}
15391
15397function getExecutableContent()
15398{
15399 $arrayofregexextension = array(
15400 'htm',
15401 'html',
15402 'shtml',
15403 'js',
15404 'phar',
15405 'php',
15406 'php3',
15407 'php4',
15408 'php5',
15409 'phtml',
15410 'pht',
15411 'pl',
15412 'py',
15413 'cgi',
15414 'ksh',
15415 'sh',
15416 'shtml',
15417 'bash',
15418 'bat',
15419 'cmd',
15420 'wpk',
15421 'exe',
15422 'dmg',
15423 'appimage'
15424 );
15425
15426 return $arrayofregexextension;
15427}
15428
15435function isAFileWithExecutableContent($filename)
15436{
15437 $arrayofregexextension = getExecutableContent();
15438
15439 foreach ($arrayofregexextension as $fileextension) {
15440 if (preg_match('/\.' . preg_quote($fileextension, '/') . '$/i', $filename)) {
15441 return true;
15442 }
15443 }
15444
15445 return false;
15446}
15447
15455function newToken()
15456{
15457 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
15458}
15459
15467function currentToken()
15468{
15469 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
15470}
15471
15477function getNonce()
15478{
15479 global $conf;
15480
15481 if (empty($conf->cache['nonce'])) {
15482 include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
15483 $conf->cache['nonce'] = dolGetRandomBytes(8);
15484 }
15485
15486 return $conf->cache['nonce'];
15487}
15488
15489
15503function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
15504{
15505 global $langs;
15506
15507 print '<div class="div-table-responsive-no-min">';
15508 print '<table class="noborder centpercent">';
15509 print '<tr class="liste_titre">';
15510
15511 print ($emptyColumns < 1) ? '<th>' : '<th colspan="' . ($emptyColumns + 1) . '">';
15512
15513 print '<span class="valignmiddle">' . $langs->trans($header) . '</span>';
15514
15515 if (!empty($link)) {
15516 if (!empty($arguments)) {
15517 print '<a href="' . DOL_URL_ROOT . '/' . $link . '?' . $arguments . '">';
15518 } else {
15519 print '<a href="' . DOL_URL_ROOT . '/' . $link . '">';
15520 }
15521 }
15522
15523 if ($number > -1) {
15524 print '<span class="badge marginleftonlyshort">' . $number . '</span>';
15525 } elseif (!empty($link)) {
15526 print '<span class="badge marginleftonlyshort">...</span>';
15527 }
15528
15529 if (!empty($link)) {
15530 print '</a>';
15531 }
15532
15533 print '</th>';
15534
15535 if ($number < 0 && !empty($link)) {
15536 print '<th class="right">';
15537 print '</th>';
15538 }
15539
15540 print '</tr>';
15541}
15542
15551function finishSimpleTable($addLineBreak = false)
15552{
15553 print '</table>';
15554 print '</div>';
15555
15556 if ($addLineBreak) {
15557 print '<br>';
15558 }
15559}
15560
15572function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
15573{
15574 global $langs;
15575
15576 if ($num === 0) {
15577 print '<tr class="oddeven">';
15578 print '<td colspan="' . $tableColumnCount . '"><span class="opacitymedium">' . $langs->trans($noneWord) . '</span></td>';
15579 print '</tr>';
15580 return;
15581 }
15582
15583 if ($nbofloop === 0) {
15584 // don't show a summary line
15585 return;
15586 }
15587
15588 /* Case already handled above, commented to satisfy phpstan.
15589 if ($num === 0) {
15590 $colspan = $tableColumnCount;
15591 } else
15592 */
15593 if ($num > $nbofloop) {
15594 $colspan = $tableColumnCount;
15595 } else {
15596 $colspan = $tableColumnCount - 1;
15597 }
15598
15599 if ($extraRightColumn) {
15600 $colspan--;
15601 }
15602
15603 print '<tr class="liste_total">';
15604
15605 if ($nbofloop > 0 && $num > $nbofloop) {
15606 print '<td colspan="' . $colspan . '" class="right">' . $langs->trans("XMoreLines", ($num - $nbofloop)) . '</td>';
15607 } else {
15608 print '<td colspan="' . $colspan . '" class="right"> ' . $langs->trans("Total") . '</td>';
15609 print '<td class="right centpercent">' . price($total) . '</td>';
15610 }
15611
15612 if ($extraRightColumn) {
15613 print '<td></td>';
15614 }
15615
15616 print '</tr>';
15617}
15618
15627function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
15628{
15629 if ($method == -1) {
15630 $method = 0;
15631 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
15632 $method = 1;
15633 }
15634 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
15635 $method = 2;
15636 }
15637 }
15638
15639 // Be sure we don't have output buffering enabled to have readfile working correctly
15640 while (ob_get_level()) {
15641 ob_end_flush();
15642 }
15643
15644 // Solution 0
15645 if ($method == 0) {
15646 readfile($fullpath_original_file_osencoded);
15647 } elseif ($method == 1) {
15648 // Solution 1
15649 $handle = fopen($fullpath_original_file_osencoded, "rb");
15650 while (!feof($handle)) {
15651 print fread($handle, 8192);
15652 }
15653 fclose($handle);
15654 } elseif ($method == 2) {
15655 // Solution 2
15656 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
15657 $handle2 = fopen("php://output", "wb");
15658 stream_copy_to_stream($handle1, $handle2);
15659 fclose($handle1);
15660 fclose($handle2);
15661 }
15662}
15663
15673function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
15674{
15675 global $langs;
15676
15677 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
15678
15679 $result = '<span class="clipboardCP' . ($showonlyonhover ? ' clipboardCPShowOnHover valignmiddle' : '') . '">';
15680 if ($texttoshow === 'none') {
15681 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15682 $result .= '<span class="clipboardCPValueToPrint"></span>';
15683 } elseif ($texttoshow) {
15684 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15685 $result .= '<span class="clipboardCPValueToPrint">' . dol_escape_htmltag($texttoshow, 1, 1) . '</span>';
15686 } else {
15687 $result .= '<' . $tag . ' class="clipboardCPValue">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15688 }
15689 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="' . dolPrintHTML($langs->trans("ClickToCopyToClipboard")) . '"></span>';
15690 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
15691 $result .= '<span class="clipboardCPText"></span>';
15692 $result .= '</span>';
15693
15694 return $result;
15695}
15696
15697
15705function jsonOrUnserialize($stringtodecode, $assoc = true)
15706{
15707 $result = json_decode($stringtodecode, $assoc);
15708 if ($result === null) {
15709 $result = unserialize($stringtodecode); // For backward compatibility. Is no more used in recent versions.
15710 }
15711
15712 return $result;
15713}
15714
15715
15732function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
15733{
15734 global $db, $user;
15735
15736 if (is_null($filter) || !is_string($filter) || $filter === '') {
15737 return '';
15738 }
15739 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
15740 $filter = '(' . $filter . ')';
15741 }
15742
15743 $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'
15744 $firstandlastparenthesis = 0;
15745
15746 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
15747 if ($noerror) {
15748 return '1 = 2';
15749 } else {
15750 return 'Filter syntax error - ' . $errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
15751 }
15752 }
15753
15754 // Test the filter syntax
15755 $t = preg_replace_callback('/' . $regexstring . '/i', 'dolForgeDummyCriteriaCallback', $filter);
15756 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
15757 // If the string result contains something else than '()', the syntax was wrong
15758
15759 if (preg_match('/[^\‍(\‍)]/', $t)) {
15760 $tmperrorstr = 'Bad syntax of the search string';
15761 $errorstr = 'Bad syntax of the search string: ' . $filter;
15762 if ($noerror) {
15763 return '1 = 2';
15764 } else {
15765 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - " . $errorstr, LOG_WARNING);
15766 return 'Filter error - ' . $tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
15767 }
15768 }
15769
15770 $ret = ($noand ? "" : " AND ") . ($nopar ? "" : '(') . preg_replace_callback('/' . $regexstring . '/i', 'dolForgeSQLCriteriaCallback', $filter) . ($nopar ? "" : ')');
15771
15772 if (is_object($db)) {
15773 $ret = str_replace('__NOW__', "'" . $db->idate(dol_now()) . "'", $ret);
15774 }
15775 if (is_object($user)) {
15776 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
15777 }
15778
15779 return $ret;
15780}
15781
15789function dolForgeExplodeAnd($sqlfilters)
15790{
15791 $arrayofandtags = array();
15792 $nbofchars = dol_strlen($sqlfilters);
15793
15794 $error = '';
15795 $parenthesislevel = 0;
15796 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
15797 if (!$result) {
15798 return array();
15799 }
15800 if ($parenthesislevel >= 1) {
15801 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
15802 }
15803
15804 $i = 0;
15805 $s = '';
15806 $countparenthesis = 0;
15807 while ($i < $nbofchars) {
15808 $char = dol_substr($sqlfilters, $i, 1);
15809
15810 if ($char == '(') {
15811 $countparenthesis++;
15812 } elseif ($char == ')') {
15813 $countparenthesis--;
15814 }
15815
15816 if ($countparenthesis == 0) {
15817 $char2 = dol_substr($sqlfilters, $i + 1, 1);
15818 $char3 = dol_substr($sqlfilters, $i + 2, 1);
15819 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
15820 // We found a AND
15821 $s = trim($s);
15822 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15823 $s = '(' . $s . ')';
15824 }
15825 $arrayofandtags[] = $s;
15826 $s = '';
15827 $i += 2;
15828 } else {
15829 $s .= $char;
15830 }
15831 } else {
15832 $s .= $char;
15833 }
15834 $i++;
15835 }
15836 if ($s) {
15837 $s = trim($s);
15838 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15839 $s = '(' . $s . ')';
15840 }
15841 $arrayofandtags[] = $s;
15842 }
15843
15844 return $arrayofandtags;
15845}
15846
15856function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
15857{
15858 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
15859 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
15860 $tmp = $sqlfilters;
15861
15862 $nb = dol_strlen($tmp);
15863 $counter = 0;
15864 $parenthesislevel = 0;
15865
15866 $error = '';
15867
15868 $i = 0;
15869 while ($i < $nb) {
15870 $char = dol_substr($tmp, $i, 1);
15871
15872 if ($char == '(') {
15873 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
15874 // We open a parenthesis and it is the first char
15875 $parenthesislevel++;
15876 }
15877 $counter++;
15878 } elseif ($char == ')') {
15879 $nbcharremaining = ($nb - $i - 1);
15880 if ($nbcharremaining >= $counter) {
15881 $parenthesislevel = min($parenthesislevel, $counter - 1);
15882 }
15883 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
15884 $parenthesislevel = $counter;
15885 }
15886 $counter--;
15887 }
15888
15889 if ($counter < 0) {
15890 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15891 $parenthesislevel = 0;
15892 dol_syslog($error, LOG_WARNING);
15893 return false;
15894 }
15895
15896 $i++;
15897 }
15898
15899 if ($counter > 0) {
15900 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15901 $parenthesislevel = 0;
15902 dol_syslog($error, LOG_WARNING);
15903 return false;
15904 }
15905
15906 return true;
15907}
15908
15916function dolForgeDummyCriteriaCallback($matches)
15917{
15918 //dol_syslog("Convert matches ".$matches[1]);
15919 if (empty($matches[1])) {
15920 return '';
15921 }
15922 $tmp = explode(':', $matches[1]);
15923 if (count($tmp) < 3) {
15924 return '';
15925 }
15926
15927 return '()'; // An empty criteria
15928}
15929
15938function dolForgeSQLCriteriaCallback($matches)
15939{
15940 global $db;
15941
15942 //dol_syslog("Convert matches ".$matches[1]);
15943 if (empty($matches[1])) {
15944 return '';
15945 }
15946 $tmp = explode(':', $matches[1], 3);
15947 if (count($tmp) < 3) {
15948 return '';
15949 }
15950
15951 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
15952
15953 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
15954
15955 $realOperator = [
15956 'NOTLIKE' => 'NOT LIKE',
15957 'ISNOT' => 'IS NOT',
15958 'NOTIN' => 'NOT IN',
15959 '!=' => '<>',
15960 ];
15961
15962 if (array_key_exists($operator, $realOperator)) {
15963 $operator = $realOperator[$operator];
15964 }
15965
15966 $tmpescaped = $tmp[2];
15967
15968 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
15969
15970 $regbis = array();
15971
15972 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)
15973 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
15974 $tmpescaped2 = '(';
15975 // Explode and sanitize each element in list
15976 $tmpelemarray = explode(',', $tmpescaped);
15977 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
15978 $reg = array();
15979 $tmpelem = trim($tmpelem);
15980 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
15981 $tmpelemarray[$tmpkey] = "'" . $db->escape($db->sanitize($reg[1], 2, 1, 1, 1)) . "'";
15982 } elseif (ctype_digit((string) $tmpelem)) { // if only 0-9 chars, no .
15983 $tmpelemarray[$tmpkey] = (int) $tmpelem;
15984 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
15985 $tmpelemarray[$tmpkey] = (float) $tmpelem;
15986 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
15987 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
15988 } else {
15989 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
15990 }
15991 }
15992 $tmpescaped2 .= implode(',', $tmpelemarray);
15993 $tmpescaped2 .= ')';
15994
15995 $tmpescaped = $tmpescaped2;
15996 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
15997 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
15998 $tmpescaped = $regbis[1];
15999 }
16000 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
16001 $tmpescaped = "'" . $db->escape($tmpescaped) . "'"; // We do not escape the _ and % so the LIKE will work as expected
16002 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
16003 // TODO Retrieve type of field for $operand field name.
16004 // So we can complete format. For example we could complete a year with month and day.
16005 $tmpescaped = "'" . $db->escape($regbis[1]) . "'";
16006 } else {
16007 if (strtoupper($tmpescaped) == 'NULL') {
16008 $tmpescaped = 'NULL';
16009 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
16010 $tmpescaped = (int) $tmpescaped;
16011 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
16012 $tmpescaped = (float) $tmpescaped;
16013 } else {
16014 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
16015 }
16016 }
16017
16018 return '(' . $db->escape($operand) . ' ' . strtoupper($operator) . ' ' . $tmpescaped . ')';
16019}
16020
16021
16031function getTimelineIcon($actionstatic, &$histo, $key)
16032{
16033 global $langs;
16034
16035 $out = '<!-- timeline icon -->' . "\n";
16036 $iconClass = 'fa fa-comments';
16037 $img_picto = '';
16038 $colorClass = '';
16039 $pictoTitle = '';
16040
16041 if ($histo[$key]['percent'] == -1) {
16042 $colorClass = 'timeline-icon-not-applicble';
16043 $pictoTitle = $langs->trans('StatusNotApplicable');
16044 } elseif ($histo[$key]['percent'] == 0) {
16045 $colorClass = 'timeline-icon-todo';
16046 $pictoTitle = $langs->trans('StatusActionToDo') . ' (0%)';
16047 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
16048 $colorClass = 'timeline-icon-in-progress';
16049 $pictoTitle = $langs->trans('StatusActionInProcess') . ' (' . $histo[$key]['percent'] . '%)';
16050 } elseif ($histo[$key]['percent'] >= 100) {
16051 $colorClass = 'timeline-icon-done';
16052 $pictoTitle = $langs->trans('StatusActionDone') . ' (100%)';
16053 }
16054
16055 if ($actionstatic->code == 'AC_TICKET_CREATE') {
16056 $iconClass = 'fa fa-ticket';
16057 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
16058 $iconClass = 'fa fa-pencilxxx';
16059 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16060 $iconClass = 'fa fa-comments';
16061 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16062 $iconClass = 'fa fa-mask';
16063 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16064 if ($actionstatic->type_picto) {
16065 $img_picto = img_picto('', $actionstatic->type_picto);
16066 } else {
16067 if ($actionstatic->type_code == 'AC_RDV') {
16068 $iconClass = 'fa fa-handshake';
16069 } elseif ($actionstatic->type_code == 'AC_TEL') {
16070 $iconClass = 'fa fa-phone';
16071 } elseif ($actionstatic->type_code == 'AC_FAX') {
16072 $iconClass = 'fa fa-fax';
16073 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
16074 $iconClass = 'fa fa-envelope';
16075 } elseif ($actionstatic->type_code == 'AC_INT') {
16076 $iconClass = 'fa fa-shipping-fast';
16077 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
16078 $iconClass = 'fa fa-robot';
16079 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
16080 $iconClass = 'fa fa-robot';
16081 }
16082 }
16083 }
16084
16085 $out .= '<i class="' . $iconClass . ' ' . $colorClass . '" title="' . $pictoTitle . '">' . $img_picto . '</i>' . "\n";
16086 return $out;
16087}
16088
16096{
16097 global $db;
16098
16099 $documents = array();
16100
16101 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
16102 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'ecm_files ecm';
16103 $sql .= " WHERE ecm.filepath = 'agenda/" . ((int) $object->id) . "'";
16104 //$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
16105 $sql .= ' OR ecm.agenda_id = ' . (int) $object->id;
16106 $sql .= ' ORDER BY ecm.position ASC';
16107
16108 $resql = $db->query($sql);
16109 if ($resql) {
16110 if ($db->num_rows($resql)) {
16111 while ($obj = $db->fetch_object($resql)) {
16112 $documents[$obj->id] = $obj;
16113 }
16114 }
16115 }
16116
16117 return $documents;
16118}
16119
16120
16138function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
16139{
16140 global $user, $conf;
16141 global $form;
16142
16143 global $param, $massactionbutton;
16144
16145 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16146
16147 // Check parameters
16148 if (!is_object($filterobj) && !is_object($objcon)) {
16149 dol_print_error(null, 'BadParameter');
16150 }
16151
16152 $histo = array();
16153 '@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';
16154
16155 $numaction = 0;
16156 $now = dol_now();
16157
16158 $sortfield_list = explode(',', $sortfield);
16159 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
16160 $sortfield_new_list = array();
16161 foreach ($sortfield_list as $sortfield_value) {
16162 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
16163 }
16164 $sortfield_new = implode(',', $sortfield_new_list);
16165
16166 $sql = null;
16167 $sql2 = null;
16168
16169 if (isModEnabled('agenda')) {
16170 // Search histo on actioncomm
16171 if (is_object($objcon) && $objcon->id > 0) {
16172 $sql = "SELECT DISTINCT a.id, a.label as label,";
16173 } else {
16174 $sql = "SELECT a.id, a.label as label,";
16175 }
16176 $sql .= " a.datep as dp,";
16177 $sql .= " a.note as message,";
16178 $sql .= " a.datep2 as dp2,";
16179 $sql .= " a.percent as percent, 'action' as type,";
16180 $sql .= " a.fk_element, a.elementtype,";
16181 $sql .= " a.fk_contact,";
16182 $sql .= " a.email_from as msg_from,";
16183 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
16184 $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";
16185 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16186 $sql .= ", sp.lastname, sp.firstname";
16187 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16188 $sql .= ", m.lastname, m.firstname";
16189 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur', 'Propal', 'Expedition'))) {
16190 $sql .= ", o.ref";
16191 } else {
16192 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16193 $sql .= ", o.ref";
16194 }
16195 }
16196 $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as a";
16197 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as u on u.rowid = a.fk_user_action";
16198 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_actioncomm as c ON a.fk_action = c.id";
16199
16200 $force_filter_contact = $filterobj instanceof User;
16201
16202 if (is_object($objcon) && $objcon->id > 0) {
16203 $force_filter_contact = true;
16204 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "actioncomm_resources as r ON a.id = r.fk_actioncomm";
16205 $sql .= " AND r.element_type = '" . $db->escape($objcon->table_element) . "' AND r.fk_element = " . ((int) $objcon->id);
16206 }
16207
16208 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
16209 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople as sp ON a.fk_contact = sp.rowid";
16210 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
16211 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "element_resources as er";
16212 $sql .= " ON er.resource_type = 'dolresource'";
16213 $sql .= " AND er.element_id = a.id";
16214 $sql .= " AND er.resource_id = " . ((int) $filterobj->id);
16215 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16216 $sql .= ", " . MAIN_DB_PREFIX . "adherent as m";
16217 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16218 $sql .= ", " . MAIN_DB_PREFIX . "commande_fournisseur as o";
16219 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16220 $sql .= ", " . MAIN_DB_PREFIX . "product as o";
16221 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16222 $sql .= ", " . MAIN_DB_PREFIX . "ticket as o";
16223 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16224 $sql .= ", " . MAIN_DB_PREFIX . "bom_bom as o";
16225 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16226 $sql .= ", " . MAIN_DB_PREFIX . "contrat as o";
16227 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16228 $sql .= ", " . MAIN_DB_PREFIX . "facture as o";
16229 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16230 $sql .= ", " . MAIN_DB_PREFIX . "facture_fourn as o";
16231 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16232 $sql .= ", " . MAIN_DB_PREFIX . "commande as o";
16233 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16234 $sql .= ", " . MAIN_DB_PREFIX . "expedition as o";
16235 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16236 $sql .= ", " . MAIN_DB_PREFIX . "propal as o";
16237 } else {
16238 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16239 $sql .= ", " . MAIN_DB_PREFIX . $filterobj->table_element . " as o";
16240 }
16241 }
16242 $sql .= " WHERE a.entity IN (" . getEntity('agenda') . ")";
16243 if (!$force_filter_contact) {
16244 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
16245 $sql .= " AND a.fk_soc = " . ((int) $filterobj->id);
16246 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
16247 $sql .= " AND a.fk_project = o.rowid AND a.fk_project = " . ((int) $filterobj->id);
16248 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16249 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
16250 if ($filterobj->id) {
16251 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16252 }
16253 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16254 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
16255 if ($filterobj->id) {
16256 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16257 }
16258 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16259 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'shipping'";
16260 if ($filterobj->id) {
16261 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16262 }
16263 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16264 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'propal'";
16265 if ($filterobj->id) {
16266 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16267 }
16268 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16269 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
16270 if ($filterobj->id) {
16271 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16272 }
16273 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16274 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
16275 if ($filterobj->id) {
16276 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16277 }
16278 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16279 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
16280 if ($filterobj->id) {
16281 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16282 }
16283 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16284 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
16285 if ($filterobj->id) {
16286 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16287 }
16288 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16289 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
16290 if ($filterobj->id) {
16291 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16292 }
16293 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
16294 $sql .= " AND a.fk_contact = sp.rowid";
16295 if ($filterobj->id) {
16296 $sql .= " AND a.fk_contact = " . ((int) $filterobj->id);
16297 }
16298 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16299 $sql .= " AND a.fk_element = o.rowid";
16300 if ($filterobj->id) {
16301 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice'";
16302 }
16303 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16304 $sql .= " AND a.fk_element = o.rowid";
16305 if ($filterobj->id) {
16306 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice_supplier'";
16307 }
16308 } else {
16309 if (is_object($filterobj) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16310 $sql .= " AND a.fk_element = o.rowid";
16311 $sql .= " AND a.elementtype = '" . $db->escape($filterobj->element) . "'";
16312 if ($filterobj->id) {
16313 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16314 }
16315 }
16316 }
16317 } else {
16318 $sql .= " AND u.rowid = " . ((int) $filterobj->id);
16319 }
16320
16321 // Condition on actioncode
16322 if (!empty($actioncode) && $actioncode != '-1') {
16323 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16324 if ($actioncode == 'AC_NON_AUTO') {
16325 $sql .= " AND c.type != 'systemauto'";
16326 } elseif ($actioncode == 'AC_ALL_AUTO') {
16327 $sql .= " AND c.type = 'systemauto'";
16328 } else {
16329 if ($actioncode == 'AC_OTH') {
16330 $sql .= " AND c.type != 'systemauto'";
16331 } elseif ($actioncode == 'AC_OTH_AUTO') {
16332 $sql .= " AND c.type = 'systemauto'";
16333 }
16334 }
16335 } else {
16336 if ($actioncode == 'AC_NON_AUTO') {
16337 $sql .= " AND c.type != 'systemauto'";
16338 } elseif ($actioncode == 'AC_ALL_AUTO') {
16339 $sql .= " AND c.type = 'systemauto'";
16340 } else {
16341 $sql .= " AND c.code = '" . $db->escape($actioncode) . "'";
16342 }
16343 }
16344 }
16345 if ($donetodo == 'todo') {
16346 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '" . $db->idate($now) . "'))";
16347 } elseif ($donetodo == 'done') {
16348 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '" . $db->idate($now) . "'))";
16349 }
16350 if (is_array($filters) && $filters['search_agenda_label']) {
16351 $sql .= natural_search('a.label', $filters['search_agenda_label']);
16352 }
16353 }
16354
16355 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
16356 if (
16357 isModEnabled('mailing') && !empty($objcon->email)
16358 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')
16359 ) {
16360 $langs->load("mails");
16361
16362 $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";
16363 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
16364 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
16365 $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
16366 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16367 $sql2 .= ", '' as lastname, '' as firstname";
16368 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16369 $sql2 .= ", '' as lastname, '' as firstname";
16370 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16371 $sql2 .= ", '' as ref";
16372 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16373 $sql2 .= ", '' as ref";
16374 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16375 $sql2 .= ", '' as ref";
16376 }
16377 $sql2 .= " FROM " . MAIN_DB_PREFIX . "mailing as m, " . MAIN_DB_PREFIX . "mailing_cibles as mc, " . MAIN_DB_PREFIX . "user as u";
16378 $sql2 .= " WHERE mc.email = '" . $db->escape($objcon->email) . "'"; // Search is done on email.
16379 $sql2 .= " AND mc.statut = 1";
16380 $sql2 .= " AND u.rowid = m.fk_user_valid";
16381 $sql2 .= " AND mc.fk_mailing=m.rowid";
16382 }
16383
16384 $num = 0;
16385 $MAXWITHOUTPAGINATION = getDolGlobalInt('AGENDA_MAX_EVENTS_ON_PAGE_WITHOUT_PAGINATION', 100);
16386
16387 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
16388 if (!empty($sql) && !empty($sql2)) {
16389 $sql = $sql . " UNION " . $sql2;
16390 } elseif (empty($sql) && !empty($sql2)) {
16391 $sql = $sql2;
16392 }
16393
16394 //TODO Add navigation with this limits...
16395 $offset = 0;
16396 $limit = $MAXWITHOUTPAGINATION;
16397
16398 // Complete request and execute it with limit
16399 $sql .= $db->order($sortfield_new, $sortorder);
16400 if ($limit) {
16401 $sql .= $db->plimit($limit + 1, $offset);
16402 }
16403
16404 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
16405
16406 $resql = $db->query($sql);
16407 if ($resql) {
16408 $i = 0;
16409 $num = $db->num_rows($resql);
16410
16411 $imaxinloop = ($limit ? min($num, $limit) : $num);
16412 while ($i < $imaxinloop) {
16413 $obj = $db->fetch_object($resql);
16414
16415 if ($obj->type == 'action') {
16416 $contactaction = new ActionComm($db);
16417 $contactaction->id = $obj->id;
16418 $result = $contactaction->fetchResources();
16419 if ($result < 0) {
16420 dol_print_error($db);
16421 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
16422 }
16423
16424 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
16425 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
16426 $tododone = '';
16427 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
16428 $tododone = 'todo';
16429 }
16430
16431 $histo[$numaction] = array(
16432 'type' => $obj->type,
16433 'tododone' => $tododone,
16434 'id' => $obj->id,
16435 'datestart' => $db->jdate($obj->dp),
16436 'dateend' => $db->jdate($obj->dp2),
16437 'note' => $obj->label,
16438 'message' => $obj->message,
16439 'percent' => $obj->percent,
16440
16441 'userid' => $obj->user_id,
16442 'login' => $obj->user_login,
16443 'userfirstname' => $obj->user_firstname,
16444 'userlastname' => $obj->user_lastname,
16445 'userphoto' => $obj->user_photo,
16446 'msg_from' => $obj->msg_from,
16447
16448 'contact_id' => $obj->fk_contact,
16449 'socpeopleassigned' => $contactaction->socpeopleassigned,
16450 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
16451 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
16452 'fk_element' => $obj->fk_element,
16453 'elementtype' => $obj->elementtype,
16454 // Type of event
16455 'acode' => $obj->acode,
16456 'alabel' => $obj->alabel,
16457 'libelle' => $obj->alabel, // deprecated
16458 'apicto' => $obj->apicto
16459 );
16460 } else {
16461 $histo[$numaction] = array(
16462 'type' => $obj->type,
16463 'tododone' => 'done',
16464 'id' => $obj->id,
16465 'datestart' => $db->jdate($obj->dp),
16466 'dateend' => $db->jdate($obj->dp2),
16467 'note' => $obj->label,
16468 'message' => $obj->message,
16469 'percent' => $obj->percent,
16470 'acode' => $obj->acode,
16471
16472 'userid' => $obj->user_id,
16473 'login' => $obj->user_login,
16474 'userfirstname' => $obj->user_firstname,
16475 'userlastname' => $obj->user_lastname,
16476 'userphoto' => $obj->user_photo
16477 );
16478 }
16479
16480 $numaction++;
16481 $i++;
16482 }
16483 } else {
16484 dol_print_error($db);
16485 }
16486 }
16487
16488 // Set $out to show events
16489 $out = '';
16490
16491 if (!isModEnabled('agenda')) {
16492 $langs->loadLangs(array("admin", "errors"));
16493 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
16494 }
16495
16496 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
16497 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
16498
16499 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16500 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
16501 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
16502 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
16503
16504 $formactions = new FormActions($db);
16505
16506 $actionstatic = new ActionComm($db);
16507 $userstatic = new User($db);
16508 $contactstatic = new Contact($db);
16509 $userGetNomUrlCache = array();
16510 $contactGetNomUrlCache = array();
16511
16512 $out .= '<div class="filters-container" >';
16513 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
16514 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
16515
16516 if (
16517 $objcon && get_class($objcon) == 'Contact' &&
16518 (is_null($filterobj) || get_class($filterobj) == 'Societe')
16519 ) {
16520 $out .= '<input type="hidden" name="id" value="' . $objcon->id . '" />';
16521 } else {
16522 $out .= '<input type="hidden" name="id" value="' . $filterobj->id . '" />';
16523 }
16524 if (($filterobj && get_class($filterobj) == 'Societe')) {
16525 $out .= '<input type="hidden" name="socid" value="' . $filterobj->id . '" />';
16526 } else {
16527 $out .= '<input type="hidden" name="userid" value="' . $filterobj->id . '" />';
16528 }
16529
16530 $out .= "\n";
16531
16532 $out .= '<div class="div-table-responsive-no-min">';
16533 $out .= '<table class="noborder borderbottom centpercent">';
16534
16535 $out .= '<tr class="liste_titre">';
16536
16537 // Action column
16538 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16539 $out .= '<th class="liste_titre width50 middle">';
16540 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16541 $out .= $searchpicto;
16542 $out .= '</th>';
16543 }
16544
16545 // Date
16546 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ') . "\n";
16547
16548 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">' . $langs->trans("Search") . ' : </strong></th>';
16549 if ($donetodo) {
16550 $out .= '<th class="liste_titre"></th>';
16551 }
16552 // Type of event
16553 $out .= '<th class="liste_titre">';
16554 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="' . $langs->trans("ActionType") . '"></span>';
16555 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
16556 $out .= '</th>';
16557 // Label
16558 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
16559 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="' . $filters['search_agenda_label'] . '" placeholder="' . $langs->trans("Label") . '">';
16560 $out .= '</th>';
16561
16562 // Action column
16563 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16564 $out .= '<th class="liste_titre width50 middle">';
16565 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16566 $out .= $searchpicto;
16567 $out .= '</th>';
16568 }
16569
16570 $out .= '</tr>';
16571
16572 $out .= '</table>';
16573
16574 $out .= '</form>';
16575 $out .= '</div>';
16576
16577 $out .= "\n";
16578
16579 $out .= '<ul class="timeline">';
16580
16581 if ($donetodo) {
16582 $tmp = '';
16583 if ($filterobj instanceof Societe) {
16584 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16585 }
16586 if ($filterobj instanceof User) {
16587 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16588 }
16589 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
16590 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
16591 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
16592 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
16593 if ($filterobj instanceof Societe) {
16594 $tmp .= '</a>';
16595 }
16596 if ($filterobj instanceof User) {
16597 $tmp .= '</a>';
16598 }
16599 $out .= getTitleFieldOfList($tmp);
16600 }
16601
16602 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/cactioncomm.class.php';
16603 $caction = new CActionComm($db);
16604 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
16605
16606 $actualCycleDate = false;
16607
16608 // Loop on each event to show it
16609 foreach ($histo as $key => $value) {
16610 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
16611
16612 $actionstatic->type_picto = $histo[$key]['apicto'];
16613 $actionstatic->type_code = $histo[$key]['acode'];
16614
16615 $labeltype = $actionstatic->type_code;
16616 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
16617 $labeltype = 'AC_OTH';
16618 }
16619 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16620 $labeltype = $langs->trans("Message");
16621 } else {
16622 if (!empty($arraylist[$labeltype])) {
16623 $labeltype = $arraylist[$labeltype];
16624 }
16625 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
16626 $labeltype .= ' - ' . $arraylist[$actionstatic->code]; // Use code in priority on type_code
16627 }
16628 }
16629
16630 $url = DOL_URL_ROOT . '/comm/action/card.php?id=' . $histo[$key]['id'];
16631
16632 $tmpa = dol_getdate($histo[$key]['datestart'], false);
16633
16634 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'] . '-' . $tmpa['yday']) {
16635 $actualCycleDate = $tmpa['year'] . '-' . $tmpa['yday'];
16636 $out .= '<!-- timeline time label -->';
16637 $out .= '<li class="time-label">';
16638 $out .= '<span class="timeline-badge-date">';
16639 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
16640 $out .= '</span>';
16641 $out .= '</li>';
16642 $out .= '<!-- /.timeline-label -->';
16643 }
16644
16645
16646 $out .= '<!-- timeline item -->' . "\n";
16647 $out .= '<li class="timeline-code-' . (!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none") . '">';
16648
16649 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
16650 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
16651 //$out .= $timelineicon;
16652 //var_dump($timelineicon);
16653 $out .= $typeicon;
16654
16655 $out .= '<div class="timeline-item">' . "\n";
16656
16657 $out .= '<span class="time timeline-header-action2">';
16658
16659 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
16660 $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") . ' ';
16661 $out .= $histo[$key]['id'];
16662 $out .= '</a> ';
16663 } else {
16664 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle') . ' ';
16665 }
16666
16667 if (
16668 $user->hasRight('agenda', 'allactions', 'create') ||
16669 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))
16670 ) {
16671 $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) . '">';
16672 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
16673 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
16674 $out .= '</a>';
16675 }
16676
16677 $out .= '</span>';
16678
16679 // Date
16680 $out .= '<span class="time"><i class="fa fa-clock valignmiddle"></i> ';
16681 $out .= '<span class="valignmiddle marginrightonly">';
16682 $out .= dol_print_date($histo[$key]['datestart'], 'day', 'tzuserrel');
16683 //$out .= '</span>';
16684 //$out .= '<span class="valignmiddle">'.
16685 $out .= ' '.dol_print_date($histo[$key]['datestart'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16686 //$out .= '</span>';
16687 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
16688 $tmpa = dol_getdate($histo[$key]['datestart'], true);
16689 $tmpb = dol_getdate($histo[$key]['dateend'], true);
16690 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
16691 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 1);
16692 } else {
16693 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'day', 'tzuserrel');
16694 //$out .= '<span class="valignmiddle marginrightonly">';
16695 $out .= ' '.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16696 //$out .= '</span>';
16697 }
16698 }
16699 $late = 0;
16700 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16701 $late = 1;
16702 }
16703 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16704 $late = 1;
16705 }
16706 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
16707 $late = 1;
16708 }
16709 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16710 $late = 1;
16711 }
16712 if ($late) {
16713 $out .= img_warning($langs->trans("Late")) . ' ';
16714 }
16715 $out .= "</span></span>\n";
16716
16717 $out .= '<span class="time">';
16718 $out .= $actionstatic->getLibStatut(2);
16719 $out .= '</span>';
16720
16721 // Ref
16722 $out .= '<h3 class="timeline-header">';
16723
16724 // Author of event
16725 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
16726 if ($histo[$key]['userid'] > 0) {
16727 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
16728 $userstatic->fetch($histo[$key]['userid']);
16729 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
16730 }
16731 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
16732 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
16733 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
16734 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
16735 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
16736 } else {
16737 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
16738 }
16739 }
16740 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
16741 } else {
16742 $out .= '<img class="photomemberphoto userphoto" alt="" src="/public/theme/common/user_anonymous.png">'.$langs->trans("Anonymous");
16743 }
16744 $out .= '</div>';
16745
16746 // Title
16747 $out .= ' <div class="messaging-title inline-block">';
16748 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
16749 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
16750 $out .= $labeltype . ' - ';
16751 }
16752
16753 $libelle = '';
16754
16755 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16756 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
16757 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16758 $out .= $langs->trans('TicketNewMessage');
16759 } elseif (isset($histo[$key]['type'])) {
16760 if ($histo[$key]['type'] == 'action') {
16761 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16762 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
16763 $libelle = $histo[$key]['note'];
16764 $actionstatic->id = $histo[$key]['id'];
16765 if ($libelle != $labeltype) {
16766 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16767 }
16768 } elseif ($histo[$key]['type'] == 'mailing') {
16769 $out .= '<a href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
16770 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16771 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : 'Send mass mailing');
16772 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16773 } else {
16774 $libelle .= $histo[$key]['note'];
16775 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16776 }
16777 }
16778 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
16779
16780 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
16781 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
16782 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
16783 } else {
16784 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
16785 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
16786 }
16787 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
16788 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
16789 }
16790 if ($link) {
16791 $out .= ' - ' . $link;
16792 }
16793 }
16794
16795 $out .= '</div>';
16796
16797 $out .= '</h3>';
16798
16799 // Message
16800 if (
16801 !empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
16802 && $actionstatic->code != 'AC_TICKET_CREATE'
16803 && $actionstatic->code != 'AC_TICKET_MODIFY'
16804 ) {
16805 $out .= '<div class="timeline-body wordbreak small">';
16806 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
16807 $newmess = dol_htmlentitiesbr($histo[$key]['message']);
16808 $truncatedText = dolGetFirstLineOfText($newmess, $truncateLines);
16809 if ($truncateLines > 0 && strlen($newmess) > strlen($truncatedText)) {
16810 $out .= '<div class="readmore-block --closed" >';
16811 $out .= ' <div class="readmore-block__excerpt">';
16812 $out .= dolPrintHTML($truncatedText);
16813 $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>';
16814 $out .= ' </div>';
16815 $out .= ' <div class="readmore-block__full-text" >';
16816 $out .= dolPrintHTML($newmess);
16817 $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>';
16818 $out .= ' </div>';
16819 $out .= '</div>';
16820 } else {
16821 $out .= dolPrintHTML($newmess);
16822 }
16823
16824 $out .= '</div>';
16825 }
16826
16827 // Timeline footer
16828 $footer = '';
16829
16830 // Contact for this action
16831 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
16832 $contactList = '';
16833 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
16834 if (empty($conf->cache['contact'][$cid])) {
16835 $contact = new Contact($db);
16836 $contact->fetch($cid);
16837 $conf->cache['contact'][$cid] = $contact;
16838 } else {
16839 $contact = $conf->cache['contact'][$cid];
16840 }
16841
16842 if ($contact) {
16843 $contactList .= !empty($contactList) ? ', ' : '';
16844 $contactList .= $contact->getNomUrl(1);
16845 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16846 if (!empty($contact->phone_pro)) {
16847 $contactList .= '(' . dol_print_phone($contact->phone_pro) . ')';
16848 }
16849 }
16850 }
16851 }
16852
16853 $footer .= $langs->trans('ActionOnContact') . ' : ' . $contactList;
16854 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
16855 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
16856 $contact = new Contact($db);
16857 $result = $contact->fetch($histo[$key]['contact_id']);
16858 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
16859 } else {
16860 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
16861 $result = ($contact instanceof Contact) ? $contact->id : 0;
16862 }
16863
16864 if ($result > 0) {
16865 $footer .= $contact->getNomUrl(1);
16866 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16867 if (!empty($contact->phone_pro)) {
16868 $footer .= '(' . dol_print_phone($contact->phone_pro) . ')';
16869 }
16870 }
16871 }
16872 }
16873
16874 $documents = getActionCommEcmList($actionstatic);
16875 if (!empty($documents)) {
16876 $footer .= '<div class="timeline-documents-container">';
16877 foreach ($documents as $doc) {
16878 $footer .= '<span id="document_' . $doc->id . '" class="timeline-documents" ';
16879 $footer .= ' data-id="' . $doc->id . '" ';
16880 $footer .= ' data-path="' . $doc->filepath . '"';
16881 $footer .= ' data-filename="' . dol_escape_htmltag($doc->filename) . '" ';
16882 $footer .= '>';
16883
16884 $filePath = DOL_DATA_ROOT . '/' . $doc->filepath . '/' . $doc->filename;
16885 $mime = dol_mimetype($filePath);
16886 if (empty($doc->agenda_id)) {
16887 $dir_ref = $actionstatic->id;
16888 $modulepart = 'actions';
16889 } else {
16890 $split_dir = explode('/', $doc->filepath);
16891 $modulepart = array_shift($split_dir);
16892 $dir_ref = implode('/', $split_dir);
16893 }
16894
16895 $file = $dir_ref . '/' . $doc->filename;
16896 $thumb = $dir_ref . '/thumbs/' . substr($doc->filename, 0, strrpos($doc->filename, '.')) . '_mini' . substr($doc->filename, strrpos($doc->filename, '.'));
16897 $doclink = dol_buildpath('document.php', 1) . '?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($file) . '&entity=' . $conf->entity;
16898 $viewlink = dol_buildpath('viewimage.php', 1) . '?modulepart=' . $modulepart . '&file=' . urlencode($thumb) . '&entity=' . $conf->entity;
16899
16900
16901
16902 $mimeAttr = ' mime="' . $mime . '" ';
16903 $class = '';
16904 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
16905 $class .= ' documentpreview';
16906 }
16907
16908 $footer .= '<a href="' . $doclink . '" class="btn-link ' . $class . '" target="_blank" rel="noopener noreferrer" ' . $mimeAttr . ' >';
16909 $footer .= img_mime($filePath) . ' ' . $doc->filename;
16910 $footer .= '</a>';
16911
16912 $footer .= '</span>';
16913 }
16914 $footer .= '</div>';
16915 }
16916
16917 if (!empty($footer)) {
16918 $out .= '<div class="timeline-footer">' . $footer . '</div>';
16919 }
16920
16921 $out .= '</div>' . "\n"; // end timeline-item
16922
16923 $out .= '</li>';
16924 $out .= '<!-- END timeline item -->';
16925 }
16926
16927 $out .= "</ul>\n";
16928
16929 // Code to manage the click on button data-read-more-action to show full description of an event
16930 $out .= '<script>
16931 jQuery(document).ready(function () {
16932 $(document).on("click", "[data-read-more-action]", function(e){
16933 console.log("We click on data-read-more-action");
16934 let readMoreBloc = $(this).closest(".readmore-block");
16935 if(readMoreBloc.length > 0){
16936 e.preventDefault();
16937 if($(this).attr("data-read-more-action") == "close"){
16938 readMoreBloc.addClass("--closed").removeClass("--open");
16939 $("html, body").animate({
16940 scrollTop: readMoreBloc.offset().top - 200
16941 }, 100);
16942 }else{
16943 readMoreBloc.addClass("--open").removeClass("--closed");
16944 }
16945 }
16946 });
16947 });
16948 </script>';
16949
16950
16951 if (empty($histo)) {
16952 $out .= '<span class="opacitymedium">' . $langs->trans("NoRecordFound") . '</span>';
16953 }
16954
16955 if ($num > $MAXWITHOUTPAGINATION) {
16956 $langs->load("errors");
16957 $colspan = 9;
16958 $out .= '<center><span class="opacitymedium">...' . $langs->trans("WarningTooManyDataPleaseUseMoreFilters", $MAXWITHOUTPAGINATION) . '...</span></center>';
16959 }
16960 }
16961
16962 if ($noprint) {
16963 return $out;
16964 } else {
16965 print $out;
16966 return null;
16967 }
16968}
16969
16981function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
16982{
16983 if ($timestamp === null) {
16984 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
16985 }
16986 $TParam = array(
16987 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
16988 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
16989 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
16990 );
16991 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
16992 $TParam = array_merge($TParam, array(
16993 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
16994 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
16995 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
16996 ));
16997 }
16998
16999 return '&' . http_build_query($TParam);
17000}
17001
17020function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
17021{
17022 global $conf, $db, $langs, $hookmanager;
17023 global $action, $object;
17024
17025 if (!is_object($langs)) {
17026 include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
17027 $langs = new Translate('', $conf);
17028 $langs->setDefaultLang();
17029 }
17030
17031 $langs->load("errors");
17032
17033 if ($printheader) {
17034 if (function_exists("llxHeader")) {
17035 llxHeader('');
17036 } elseif (function_exists("llxHeaderVierge")) {
17037 llxHeaderVierge('');
17038 }
17039 }
17040
17041 print '<div class="error">';
17042 if (empty($message)) {
17043 print $langs->trans("ErrorRecordNotFound");
17044 } else {
17045 print $langs->trans($message);
17046 }
17047 print '</div>';
17048 print '<br>';
17049
17050 if (empty($showonlymessage)) {
17051 if (empty($hookmanager)) {
17052 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
17053 $hookmanager = new HookManager($db);
17054 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
17055 $hookmanager->initHooks(array('main'));
17056 }
17057
17058 $parameters = array('message' => $message, 'params' => $params);
17059 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
17060 print $hookmanager->resPrint;
17061 }
17062
17063 if ($printfooter && function_exists("llxFooter")) {
17064 llxFooter();
17065 if (is_object($db)) {
17066 $db->close();
17067 }
17068 }
17069 exit(0);
17070}
17071
17100function array_merge_recursive_distinct(array $array1, array $array2): array
17101{
17102 $merged = $array1;
17103
17104 foreach ($array2 as $key => $value) {
17105 if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
17106 $merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
17107 } else {
17108 $merged[$key] = $value;
17109 }
17110 }
17111
17112 return $merged;
17113}
17114
17121function getObjectSocId($obj)
17122{
17123 if (!empty($obj->socid)) {
17124 return (int) $obj->socid;
17125 } elseif (!empty($obj->soc_id)) {
17126 return (int) $obj->soc_id;
17127 } elseif (!empty($obj->societe_id)) {
17128 return (int) $obj->societe_id;
17129 }
17130 return null;
17131}
$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).
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