dolibarr 23.0.4
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2025 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16 * Copyright (C) 2018-2026 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 // An entity with no declared directory returned an undefined index, so an empty path that
191 // made the caller read or write a relative path under the web root. Answer the error instead.
192 $entity = (int) (empty($object->entity) ? $conf->entity : $object->entity);
193 if (!isset($conf->$module->multidir_output[$entity])) {
194 return 'error-diroutput-not-defined-for-this-entity-and-object='.$module;
195 }
196 $s = $conf->$module->multidir_output[$entity] . $subdirectory;
197 }
198 if ($forobject && $object->id > 0) {
199 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
200 }
201 return $s;
202 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
203 $s = '';
204 if ($mode != 'outputrel') {
205 $s = $conf->$module->dir_output . $subdirectory;
206 }
207 if ($forobject && $object->id > 0) {
208 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
209 }
210 return $s;
211 } else {
212 return 'error-diroutput-not-defined-for-this-entity-and-object='.$module;
213 }
214 } elseif ($mode == 'temp') {
215 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
216 // Same guard as the 'output' mode above, see the comment there
217 $entity = (int) (empty($object->entity) ? $conf->entity : $object->entity);
218 if (!isset($conf->$module->multidir_temp[$entity])) {
219 return 'error-dirtemp-not-defined-for-this-entity-and-object='.$module;
220 }
221 return $conf->$module->multidir_temp[$entity];
222 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
223 return $conf->$module->dir_temp;
224 } else {
225 return 'error-dirtemp-not-defined-for-this-entity-and-object='.$module;
226 }
227 } else {
228 return 'error-bad-value-for-mode';
229 }
230}
231
241function getMultidirTemp($object, $module = '', $forobject = 0)
242{
243 return getMultidirOutput($object, $module, $forobject, 'temp');
244}
245
255function getMultidirVersion($object, $module = '', $forobject = 0)
256{
257 return getMultidirOutput($object, $module, $forobject, 'version');
258}
259
260
269function getDolGlobalString($key, $default = '')
270{
271 global $conf;
272 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
273}
274
284function getDolGlobalInt($key, $default = 0)
285{
286 global $conf;
287 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
288}
289
299function getDolGlobalFloat($key, $default = 0)
300{
301 global $conf;
302 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
303}
304
313function getDolGlobalBool($key, $default = false)
314{
315 global $conf;
316 return (bool) ($conf->global->$key ?? $default);
317}
318
325{
326 global $conf;
327 return (string) $conf->currency;
328}
329
336{
337 global $conf;
338 return (string) $conf->dol_optimize_smallscreen;
339}
340
346function getDolEntity()
347{
348 global $conf;
349 return (int) $conf->entity;
350}
351
357function getDolDBType()
358{
359 global $conf;
360 return $conf->db->type;
361}
362
370{
371 return str_replace('_', '', basename(dirname($s)).basename($s, '.php'));
372}
373
383function getDolUserString($key, $default = '', $tmpuser = null)
384{
385 if (empty($tmpuser)) {
386 global $user;
387 $tmpuser = $user;
388 }
389
390 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
391}
392
401function getDolUserInt($key, $default = 0, $tmpuser = null)
402{
403 if (empty($tmpuser)) {
404 global $user;
405 $tmpuser = $user;
406 }
407
408 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
409}
410
411
421define(
422 'MODULE_MAPPING',
423 array(
424 // Map deprecated names to new names
425 'adherent' => 'member', // Has new directory
426 'member_type' => 'adherent_type', // No directory, but file called adherent_type
427 'banque' => 'bank', // Has new directory
428 'contrat' => 'contract', // Has new directory
429 'entrepot' => 'stock', // Has new directory
430 'projet' => 'project', // Has new directory
431 'categorie' => 'category', // Has old directory
432 'commande' => 'order', // Has old directory
433 'expedition' => 'shipping', // Has old directory
434 'facture' => 'invoice', // Has old directory
435 'fichinter' => 'intervention', // Has old directory
436 'ficheinter' => 'intervention', // Backup for 'fichinter'
437 'propale' => 'propal', // Has old directory
438 'socpeople' => 'contact', // Has old directory
439 'fournisseur' => 'supplier', // Has old directory
440
441 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
442 'product_price' => 'productprice', // NO directory
443 'product_fournisseur_price' => 'productsupplierprice', // NO directory
444 )
445);
446
453function isModEnabled($module)
454{
455 global $conf;
456
457 // Fix old names (map to new names)
458 $arrayconv = MODULE_MAPPING;
459 $arrayconvbis = array_flip(MODULE_MAPPING);
460
461 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
462 // Special cases: both use the same module.
463 $arrayconv['supplier_order'] = 'fournisseur';
464 $arrayconv['supplier_invoice'] = 'fournisseur';
465 }
466
467 $module_alt = $module;
468 if (!empty($arrayconv[$module])) {
469 $module_alt = $arrayconv[$module];
470 }
471 $module_bis = $module;
472 if (!empty($arrayconvbis[$module])) {
473 $module_bis = $arrayconvbis[$module];
474 }
475
476 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
477}
478
489function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
490{
491 global $conf;
492
493 // For compatibility with bad naming on module
494 $moduletomoduletouse = array(
495 'invoice' => 'facture',
496 );
497 $moduleParmsMapping = array(
498 'product' => 'produit',
499 );
500
501 if (!empty($moduletomoduletouse[$module])) {
502 $module = $moduletomoduletouse[$module];
503 }
504
505 $warningDelayPath = $parmlevel1;
506 if (!empty($moduleParmsMapping[$warningDelayPath])) {
507 $warningDelayPath = $moduleParmsMapping[$warningDelayPath];
508 }
509
510 if ($parmlevel2) {
511 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->$parmlevel2) && !empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
512 return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
513 }
514 } else {
515 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->warning_delay)) {
516 return (int) $conf->$module->$warningDelayPath->warning_delay;
517 }
518 }
519
520 return 0;
521}
522
529function isDolTms($timestamp)
530{
531 if ($timestamp === '') {
532 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page ' . $_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_DEBUG);
533 return false;
534 }
535 if (is_null($timestamp) || !is_numeric($timestamp)) {
536 return false;
537 }
538
539 return true;
540}
541
553function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
554{
555 require_once DOL_DOCUMENT_ROOT . "/core/db/" . $type . '.class.php';
556
557 $class = 'DoliDB' . ucfirst($type);
558 $db = new $class($type, $host, $user, $pass, $name, $port);
559 return $db;
560}
561
579function getEntity($element, $shared = 1, $currentobject = null)
580{
581 global $conf, $mc, $hookmanager, $object, $action, $db;
582
583 if (!is_object($hookmanager)) {
584 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
585 $hookmanager = new HookManager($db);
586 }
587
588 // fix different element names (France to English)
589 switch ($element) {
590 case 'projet':
591 $element = 'project';
592 break;
593 case 'contrat':
594 $element = 'contract';
595 break; // "/contrat/class/contrat.class.php"
596 case 'order_supplier':
597 $element = 'supplier_order';
598 break; // "/fourn/class/fournisseur.commande.class.php"
599 case 'invoice_supplier':
600 $element = 'supplier_invoice';
601 break; // "/fourn/class/fournisseur.facture.class.php"
602 }
603
604 if (is_object($mc)) {
605 $out = $mc->getEntity($element, $shared, $currentobject);
606 } else {
607 $out = '';
608 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
609 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
610 $addzero[] = 'c_holiday_types';
611 }
612 if (in_array($element, $addzero)) {
613 $out .= '0,';
614 }
615 $out .= ((int) $conf->entity);
616 }
617
618 // Manipulate entities to query on the fly
619 $parameters = array(
620 'element' => $element,
621 'shared' => $shared,
622 'object' => $object,
623 'currentobject' => $currentobject,
624 'out' => $out
625 );
626 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
627 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
628
629 if (is_numeric($reshook)) {
630 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
631 $out .= ',' . $hookmanager->resPrint; // add
632 } elseif ($reshook == 1) {
633 $out = $hookmanager->resPrint; // replace
634 }
635 }
636
637 return $out;
638}
639
646function setEntity($currentobject)
647{
648 global $conf, $mc;
649
650 if (is_object($mc) && method_exists($mc, 'setEntity')) {
651 return $mc->setEntity($currentobject);
652 } else {
653 return ((is_object($currentobject) && $currentobject->id > 0 && ((int) $currentobject->entity) > 0) ? (int) $currentobject->entity : $conf->entity);
654 }
655}
656
663function isASecretKey($keyname)
664{
665 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
666}
667
668
675function num2Alpha($n)
676{
677 $r = '';
678 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
679 $r = chr($n % 26 + 0x41) . $r;
680 }
681 return $r;
682}
683
684
701function getBrowserInfo($user_agent)
702{
703 include_once DOL_DOCUMENT_ROOT . '/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
704
705 $name = 'unknown';
706 $version = '';
707 $os = 'unknown';
708 $phone = '';
709
710 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
711
712 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal Bad definition of Mobile_Detect function
713 $detectmobile = new Mobile_Detect(null, $user_agent);
714 $tablet = $detectmobile->isTablet();
715
716 if ($detectmobile->isMobile()) {
717 $phone = 'unknown';
718
719 // If phone/smartphone, we set phone os name.
720 if ($detectmobile->is('AndroidOS')) {
721 $os = $phone = 'android';
722 } elseif ($detectmobile->is('BlackBerryOS')) {
723 $os = $phone = 'blackberry';
724 } elseif ($detectmobile->is('iOS')) {
725 $os = 'ios';
726 $phone = 'iphone';
727 } elseif ($detectmobile->is('PalmOS')) {
728 $os = $phone = 'palm';
729 } elseif ($detectmobile->is('SymbianOS')) {
730 $os = 'symbian';
731 } elseif ($detectmobile->is('webOS')) {
732 $os = 'webos';
733 } elseif ($detectmobile->is('MaemoOS')) {
734 $os = 'maemo';
735 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
736 $os = 'windows';
737 }
738 }
739
740 // OS
741 if (preg_match('/linux/i', $user_agent)) {
742 $os = 'linux';
743 } elseif (preg_match('/macintosh/i', $user_agent)) {
744 $os = 'macintosh';
745 } elseif (preg_match('/windows/i', $user_agent)) {
746 $os = 'windows';
747 }
748
749 // Name
750 $reg = array();
751 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
752 $name = 'firefox';
753 $version = empty($reg[2]) ? '' : $reg[2];
754 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
755 $name = 'edge';
756 $version = empty($reg[2]) ? '' : $reg[2];
757 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
758 $name = 'chrome';
759 $version = empty($reg[2]) ? '' : $reg[2];
760 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
761 // we can have 'chrome (Mozilla...) chrome x.y' in one string
762 $name = 'chrome';
763 } elseif (preg_match('/iceweasel/i', $user_agent)) {
764 $name = 'iceweasel';
765 } elseif (preg_match('/epiphany/i', $user_agent)) {
766 $name = 'epiphany';
767 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
768 $name = 'safari';
769 $version = empty($reg[2]) ? '' : $reg[2];
770 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
771 // Safari is often present in string for mobile but its not.
772 $name = 'opera';
773 $version = empty($reg[2]) ? '' : $reg[2];
774 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
775 $name = 'ie';
776 $version = end($reg);
777 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
778 // MS products at end
779 $name = 'ie';
780 $version = end($reg);
781 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
782 // MS products at end
783 $name = 'textbrowser';
784 $version = empty($reg[3]) ? '' : $reg[3];
785 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
786 // MS products at end
787 $name = 'textbrowser';
788 $version = empty($reg[1]) ? '' : $reg[1];
789 }
790
791 if ($tablet) {
792 $layout = 'tablet';
793 } elseif ($phone) {
794 $layout = 'phone';
795 } else {
796 $layout = 'classic';
797 }
798
799 return array(
800 'browsername' => $name,
801 'browserversion' => $version,
802 'browseros' => $os,
803 'browserua' => $user_agent,
804 'layout' => $layout, // tablet, phone, classic
805 'phone' => $phone, // deprecated
806 'tablet' => $tablet // deprecated
807 );
808}
809
815function dol_shutdown()
816{
817 global $db;
818 $disconnectdone = false;
819 $depth = 0;
820 if (is_object($db) && !empty($db->connected)) {
821 $depth = $db->transaction_opened;
822 $disconnectdone = $db->close();
823 }
824 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));
825}
826
836function GETPOSTISSET($paramname)
837{
838 $isset = false;
839
840 $relativepathstring = $_SERVER["PHP_SELF"];
841 // Clean $relativepathstring
842 if (constant('DOL_URL_ROOT')) {
843 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
844 }
845 $relativepathstring = ltrim($relativepathstring, '/');
846 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
847
848 // Code for search criteria persistence.
849 // Retrieve values if restore_lastsearch_values
850 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
851 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
852 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
853 if (is_array($tmp)) {
854 foreach ($tmp as $key => $val) {
855 if ($key == $paramname) { // We are on the requested parameter
856 $isset = true;
857 break;
858 }
859 }
860 }
861 }
862 // If there is saved contextpage, limit, page or mode
863 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
864 $isset = true;
865 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
866 $isset = true;
867 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
868 $isset = true;
869 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
870 $isset = true;
871 }
872 } else {
873 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
874 }
875
876 return $isset;
877}
878
887function GETPOSTISARRAY($paramname, $method = 0)
888{
889 // for $method test need return the same $val as GETPOST
890 if (empty($method)) {
891 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
892 } elseif ($method == 1) {
893 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
894 } elseif ($method == 2) {
895 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
896 } elseif ($method == 3) {
897 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
898 } else {
899 $val = 'BadFirstParameterForGETPOST';
900 }
901
902 return is_array($val);
903}
904
905
915function GETPOSTINT($paramname, $method = 0)
916{
917 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
918}
919
933function GETPOSTFLOAT($paramname, $rounding = '', $option = 2)
934{
935 // 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.)
936 return (float) price2num(GETPOST($paramname), $rounding, $option);
937}
938
954function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto', $saverestore = '')
955{
956 $m = array();
957 if ($hourTime === 'getpost' || $hourTime === 'getpostend') {
958 $hour = (GETPOSTISSET($prefix . 'hour') && GETPOSTINT($prefix . 'hour') >= 0) ? GETPOSTINT($prefix . 'hour') : ($hourTime === 'getpostend' ? 23 : 0);
959 $minute = (GETPOSTISSET($prefix . 'min') && GETPOSTINT($prefix . 'min') >= 0) ? GETPOSTINT($prefix . 'min') : ($hourTime === 'getpostend' ? 59 : 0);
960 $second = (GETPOSTISSET($prefix . 'sec') && GETPOSTINT($prefix . 'sec') >= 0) ? GETPOSTINT($prefix . 'sec') : ($hourTime === 'getpostend' ? 59 : 0);
961 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
962 $hour = intval($m[1]);
963 $minute = intval($m[2]);
964 $second = intval($m[3]);
965 } elseif ($hourTime === 'end') {
966 $hour = 23;
967 $minute = 59;
968 $second = 59;
969 } else {
970 $hour = $minute = $second = 0;
971 }
972
973 if (
974 $saverestore
975 && !GETPOSTISSET($prefix . 'day')
976 && !GETPOSTISSET($prefix . 'month')
977 && !GETPOSTISSET($prefix . 'year')
978 && isset($_SESSION['DOLDATE_' . $saverestore . '_day'])
979 && isset($_SESSION['DOLDATE_' . $saverestore . '_month'])
980 && isset($_SESSION['DOLDATE_' . $saverestore . '_year'])
981 ) {
982 $day = $_SESSION['DOLDATE_' . $saverestore . '_day'];
983 $month = $_SESSION['DOLDATE_' . $saverestore . '_month'];
984 $year = $_SESSION['DOLDATE_' . $saverestore . '_year'];
985 } else {
986 $month = GETPOSTINT($prefix . 'month');
987 $day = GETPOSTINT($prefix . 'day');
988 $year = GETPOSTINT($prefix . 'year');
989 }
990
991 // normalize out of range values
992 $hour = (int) min($hour, 23);
993 $minute = (int) min($minute, 59);
994 $second = (int) min($second, 59);
995
996 if ($saverestore) {
997 $_SESSION['DOLDATE_' . $saverestore . '_day'] = $day;
998 $_SESSION['DOLDATE_' . $saverestore . '_month'] = $month;
999 $_SESSION['DOLDATE_' . $saverestore . '_year'] = $year;
1000 }
1001
1002 //print "$hour, $minute, $second, $month, $day, $year, $gm<br>";
1003 return dol_mktime($hour, $minute, $second, $month, $day, $year, $gm);
1004}
1005
1046function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
1047{
1048 global $mysoc, $user, $conf;
1049
1050 if (empty($paramname)) { // Explicit test for null for phan.
1051 return 'BadFirstParameterForGETPOST';
1052 }
1053 if (empty($check)) {
1054 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);
1055 // Enable this line to know who call the GETPOST with '' $check parameter.
1056 //var_dump(getCallerInfoString());
1057 }
1058
1059 if (empty($method)) {
1060 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
1061 } elseif ($method == 1) {
1062 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
1063 } elseif ($method == 2) {
1064 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
1065 } elseif ($method == 3) {
1066 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
1067 } else {
1068 return 'BadThirdParameterForGETPOST';
1069 }
1070
1071 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
1072
1073 if (empty($method) || $method == 3 || $method == 4) {
1074 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
1075 // Clean $relativepathstring
1076 if (constant('DOL_URL_ROOT')) {
1077 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
1078 }
1079 $relativepathstring = ltrim($relativepathstring, '/');
1080 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1081
1082 // Code for search criteria persistence.
1083 // Retrieve saved values if restore_lastsearch_values is set
1084 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
1085 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
1086 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
1087 if (is_array($tmp)) {
1088 foreach ($tmp as $key => $val) {
1089 if ($key == $paramname) { // We are on the requested parameter
1090 $out = $val;
1091 break;
1092 }
1093 }
1094 }
1095 }
1096 // If there is saved contextpage, page or limit
1097 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
1098 $out = $_SESSION['lastsearch_contextpage_' . $relativepathstring];
1099 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
1100 $out = $_SESSION['lastsearch_limit_' . $relativepathstring];
1101 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
1102 $out = $_SESSION['lastsearch_page_' . $relativepathstring];
1103 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
1104 $out = $_SESSION['lastsearch_mode_' . $relativepathstring];
1105 }
1106 } elseif (!isset($_GET['sortfield'])) {
1107 // Else, retrieve default values if we are not doing a sort
1108 // 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
1109 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1110 // Search default value from $object->field
1111 global $object;
1112 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
1113 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
1114 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
1115 $out = $object->fields[$paramname]['default'];
1116 }
1117 }
1118 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1119 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1120 // Now search in setup to overwrite default values
1121 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
1122 if (isset($user->default_values[$relativepathstring]['createform'])) {
1123 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
1124 $qualified = 0;
1125 if ($defkey != '_noquery_') {
1126 $tmpqueryarraytohave = explode('&', $defkey);
1127 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1128 $foundintru = 0;
1129 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1130 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1131 $foundintru = 1;
1132 }
1133 }
1134 if (!$foundintru) {
1135 $qualified = 1;
1136 }
1137 } else {
1138 $qualified = 1;
1139 }
1140
1141 if ($qualified) {
1142 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
1143 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
1144 break;
1145 }
1146 }
1147 }
1148 }
1149 }
1150 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1151 // Management of default search_filters and sort order
1152 if (!empty($user->default_values)) {
1153 // $user->default_values defined from menu 'Setup - Default values'
1154 //var_dump($user->default_values[$relativepathstring]);
1155 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
1156 // Sorted on which fields ? ASC or DESC ?
1157 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
1158 // Even if paramname is sortfield, data are stored into ['sortorder...']
1159 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
1160 $qualified = 0;
1161 if ($defkey != '_noquery_') {
1162 $tmpqueryarraytohave = explode('&', $defkey);
1163 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1164 $foundintru = 0;
1165 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1166 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1167 $foundintru = 1;
1168 }
1169 }
1170 if (!$foundintru) {
1171 $qualified = 1;
1172 }
1173 } else {
1174 $qualified = 1;
1175 }
1176
1177 if ($qualified) {
1178 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1179 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
1180 if ($out) {
1181 $out .= ', ';
1182 }
1183 if ($paramname == 'sortfield') {
1184 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
1185 }
1186 if ($paramname == 'sortorder') {
1187 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
1188 }
1189 }
1190 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
1191 }
1192 }
1193 }
1194 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
1195 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
1196 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
1197 continue;
1198 }
1199 $qualified = 0;
1200 if ($defkey != '_noquery_') {
1201 $tmpqueryarraytohave = explode('&', $defkey);
1202 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1203 $foundintru = 0;
1204 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1205 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1206 $foundintru = 1;
1207 }
1208 }
1209 if (!$foundintru) {
1210 $qualified = 1;
1211 }
1212 } else {
1213 $qualified = 1;
1214 }
1215
1216 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
1217 // We must keep $_POST and $_GET here
1218 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
1219 // We made a search from quick search menu, do we still use default filter ?
1220 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
1221 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1222 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1223 }
1224 } else {
1225 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1226 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1227 }
1228 break;
1229 }
1230 }
1231 }
1232 }
1233 }
1234 }
1235 }
1236 }
1237
1238 // 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)
1239 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1240 // 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.
1241 '@phan-var-force string $paramname';
1242 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1243 $reg = array();
1244 $regreplace = array();
1245 $maxloop = 20;
1246 $loopnb = 0; // Protection against infinite loop
1247
1248 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.
1249 $loopnb++;
1250 $newout = '';
1251
1252 if ($reg[1] == 'DAY') {
1253 $tmp = dol_getdate(dol_now(), true);
1254 $newout = $tmp['mday'];
1255 } elseif ($reg[1] == 'MONTH') {
1256 $tmp = dol_getdate(dol_now(), true);
1257 $newout = $tmp['mon'];
1258 } elseif ($reg[1] == 'YEAR') {
1259 $tmp = dol_getdate(dol_now(), true);
1260 $newout = $tmp['year'];
1261 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1262 $tmp = dol_getdate(dol_now(), true);
1263 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1264 $newout = $tmp2['day'];
1265 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1266 $tmp = dol_getdate(dol_now(), true);
1267 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1268 $newout = $tmp2['month'];
1269 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1270 $tmp = dol_getdate(dol_now(), true);
1271 $newout = ($tmp['year'] - 1);
1272 } elseif ($reg[1] == 'NEXT_DAY') {
1273 $tmp = dol_getdate(dol_now(), true);
1274 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1275 $newout = $tmp2['day'];
1276 } elseif ($reg[1] == 'NEXT_MONTH') {
1277 $tmp = dol_getdate(dol_now(), true);
1278 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1279 $newout = $tmp2['month'];
1280 } elseif ($reg[1] == 'NEXT_YEAR') {
1281 $tmp = dol_getdate(dol_now(), true);
1282 $newout = ($tmp['year'] + 1);
1283 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1284 $newout = $mysoc->country_id;
1285 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1286 $newout = $user->id;
1287 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1288 $newout = $user->fk_user;
1289 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1290 $newout = $conf->entity;
1291 } elseif ($reg[1] == 'ID') {
1292 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1293 } else {
1294 $newout = 'REGREPLACE_' . $loopnb; // Key not found, we replace with temporary string to reload later
1295 $regreplace[$loopnb] = $reg[0];
1296 }
1297 //var_dump('__'.$reg[1].'__ -> '.$newout);
1298 $out = preg_replace('/__' . preg_quote($reg[1], '/') . '__/', $newout, $out);
1299 }
1300 if (!empty($regreplace)) {
1301 foreach ($regreplace as $key => $value) {
1302 $out = preg_replace('/REGREPLACE_' . $key . '/', $value, $out);
1303 }
1304 }
1305 }
1306
1307 // Check type of variable and make sanitization according to this
1308 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1309 $tmpcheck = 'alphanohtml';
1310 if (empty($out)) {
1311 $out = array();
1312 } elseif (!is_array($out)) {
1313 $out = explode(',', $out);
1314 } else {
1315 $tmparray = explode(':', $check);
1316 if (!empty($tmparray[1])) {
1317 $tmpcheck = $tmparray[1];
1318 }
1319 }
1320 foreach ($out as $outkey => $outval) {
1321 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1322 }
1323 } else {
1324 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1325 // 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
1326 if (strpos($paramname, 'search_') === 0) {
1327 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1328 }
1329
1330 // @phan-suppress-next-line UnknownSanitizeType
1331 $out = sanitizeVal($out, $check, $filter, $options);
1332 }
1333
1334 // Sanitizing for special parameters.
1335 // 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.
1336 // @TODO Merge backtopage with backtourl
1337 // @TODO Rename backtolist into backtopagelist
1338 if (preg_match('/^backto/i', $paramname)) {
1339 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1340 $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.
1341 do {
1342 $oldstringtoclean = $out;
1343 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1344 $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'
1345 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1346 } while ($oldstringtoclean != $out);
1347 }
1348
1349 // Code for search criteria persistence.
1350 // Save data into session if key start with 'search_'
1351 if (empty($method) || $method == 3 || $method == 4) {
1352 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1353 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1354
1355 // We save search key only if $out not empty that means:
1356 // - posted value not empty, or
1357 // - 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).
1358
1359 if ($out != '' && isset($user)) { // $out = '0' or 'abc', it is a search criteria to keep
1360 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1361 }
1362 }
1363 }
1364
1365 if ($paramname == 'hashp' && $out == 'shared') {
1366 $out = ''; // We refuse to have hashp=shared as a parameter
1367 }
1368
1369 return $out;
1370}
1371
1381function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1382{
1383 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1384 // Check is done after replacement
1385 if ($out === null) {
1386 $out = '';
1387 }
1388 switch ($check) {
1389 case 'none':
1390 case 'password':
1391 break;
1392 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1393 if (!is_numeric($out)) {
1394 $out = '';
1395 }
1396 break;
1397 case 'intcomma':
1398 if (is_array($out)) {
1399 $out = implode(',', $out);
1400 }
1401 if (preg_match('/[^0-9,-]+/i', $out)) {
1402 $out = '';
1403 }
1404 break;
1405 case 'san_alpha':
1406 dol_syslog("Use of parameter value 'san_alpha' in GETPOST is deprecated. Use 'alphanohtml', 'aZ09comma', ...", LOG_WARNING);
1407 $out = filter_var($out, FILTER_SANITIZE_STRING);
1408 break;
1409 case 'email':
1410 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1411 break;
1412 case 'url':
1413 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1414 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1415 // TODO Allow ( ) but only into password of https://login:password@domain...
1416 break;
1417 case 'aZ':
1418 if (!is_array($out)) {
1419 $out = trim($out);
1420 if (preg_match('/[^a-z]+/i', $out)) {
1421 $out = '';
1422 }
1423 }
1424 break;
1425 case 'aZ09':
1426 if (!is_array($out)) {
1427 $out = trim($out);
1428 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1429 $out = '';
1430 }
1431 }
1432 break;
1433 case 'aZ09arobase': // great to sanitize $objecttype parameter
1434 if (!is_array($out)) {
1435 $out = trim($out);
1436 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1437 $out = '';
1438 }
1439 }
1440 break;
1441 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1442 if (!is_array($out)) {
1443 $out = trim($out);
1444 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1445 $out = '';
1446 }
1447 }
1448 break;
1449 case 'alpha': // No html and no ../ and "
1450 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1451 if (!is_array($out)) {
1452 $out = trim($out);
1453 do {
1454 $oldstringtoclean = $out;
1455 // Remove html tags
1456 $out = dol_string_nohtmltag($out, 0);
1457 // 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).
1458 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1459 // Remove also other dangerous string sequences
1460 // '../' or '..\' is dangerous because it allows dir transversals
1461 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1462 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1463 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1464 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1465 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1466 } while ($oldstringtoclean != $out);
1467 // keep lines feed
1468 }
1469 break;
1470 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'
1471 if (!is_array($out)) {
1472 $out = trim($out);
1473 do {
1474 $oldstringtoclean = $out;
1475 // Decode html entities
1476 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1477 // 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).
1478 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1479 // Remove also other dangerous string sequences
1480 // '../' or '..\' is dangerous because it allows dir transversals
1481 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1482 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1483 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1484 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1485 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1486 } while ($oldstringtoclean != $out);
1487 }
1488 break;
1489 case 'nohtml': // No html. Valid for JSON strings.
1490 $out = dol_string_nohtmltag($out, 0);
1491 break;
1492 case 'restricthtmlnolink':
1493 case 'restricthtml': // Recommended for most html textarea
1494 case 'restricthtmlallowclass':
1495 case 'restricthtmlallowiframe':
1496 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1497 case 'restricthtmlallowunvalid':
1498 $out = dol_htmlwithnojs($out, 1, $check);
1499 break;
1500 case 'custom':
1501 if (!empty($out)) {
1502 if (empty($filter)) {
1503 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1504 }
1505 if (is_null($options)) {
1506 $options = 0;
1507 }
1508 $out = filter_var($out, $filter, $options);
1509 }
1510 break;
1511 default:
1512 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1513 $out = GETPOST($out, 'alphanohtml');
1514 break;
1515 }
1516
1517 return $out;
1518}
1519
1528function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1529{
1530 global $dolibarr_main_force_https;
1531
1532 if ($expire == -1) {
1533 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1534 }
1535
1536 if (PHP_VERSION_ID < 70300) {
1537 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1538 } else {
1539 // Only available for php >= 7.3
1540 $cookieparams = array(
1541 'expires' => empty($cookievalue) ? 0 : $expire,
1542 'path' => '/',
1543 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1544 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1545 'httponly' => true,
1546 'samesite' => 'Lax' // None || Lax || Strict
1547 );
1548 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1549 }
1550 if (empty($cookievalue)) {
1551 unset($_COOKIE[$cookiename]);
1552 }
1553}
1554
1555if (!function_exists('dol_getprefix')) {
1566 function dol_getprefix($mode = '')
1567 {
1568 // If prefix is for email (we need to have $conf already loaded for this case)
1569 if ($mode == 'email') {
1570 global $conf;
1571
1572 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1573 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1574 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1575 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1576 return $_SERVER["SERVER_NAME"];
1577 }
1578 }
1579
1580 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1581 if (!empty($conf->file->instance_unique_id)) {
1582 return sha1('dolibarr' . $conf->file->instance_unique_id);
1583 }
1584
1585 // For backward compatibility when instance_unique_id is not set
1586 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1587 }
1588
1589 // If prefix is for session (no need to have $conf loaded)
1590 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1591 $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
1592
1593 // The recommended value (may be not defined for old versions)
1594 if (!empty($tmp_instance_unique_id)) {
1595 return sha1('dolibarr' . $tmp_instance_unique_id);
1596 }
1597
1598 // For backward compatibility when instance_unique_id is not set
1599 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1600 return sha1($_SERVER["SERVER_NAME"] . $_SERVER["DOCUMENT_ROOT"] . DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1601 } else {
1602 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1603 }
1604 }
1605}
1606
1617function dol_include_once($relpath, $classname = '')
1618{
1619 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']
1620
1621 if (strpos($relpath, '..') !== false) {
1622 // Found a not valid path
1623 dol_syslog('functions::dol_include_once Tried to load a file with a path including a forbidden sequence ".." : ' . $relpath, LOG_WARNING);
1624 return false;
1625 }
1626 if (!preg_match('/\.php$/', $relpath)) {
1627 // Found a not valid path
1628 dol_syslog('functions::dol_include_once Tried to load a file that is not a PHP file : ' . $relpath, LOG_WARNING);
1629 return false;
1630 }
1631
1632 $fullpath = dol_buildpath($relpath);
1633
1634 if (!file_exists($fullpath)) {
1635 dol_syslog('functions::dol_include_once Tried to load unexisting file: ' . $relpath, LOG_WARNING);
1636 return false;
1637 }
1638 if (!empty($classname) && !class_exists($classname)) {
1639 return include $fullpath;
1640 } else {
1641 return include_once $fullpath;
1642 }
1643}
1644
1645
1659function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1660{
1661 global $conf;
1662
1663 $path = preg_replace('/^\//', '', $path);
1664
1665 if (empty($type)) { // For a filesystem path
1666 $res = DOL_DOCUMENT_ROOT . '/' . $path; // Standard default path
1667 if (is_array($conf->file->dol_document_root)) {
1668 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1669 if ($key == 'main') {
1670 continue;
1671 }
1672 // if (@file_exists($dirroot.'/'.$path)) {
1673 if (@file_exists($dirroot . '/' . $path)) { // avoid [php:warn]
1674 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/...'
1675 continue;
1676 }
1677 $res = $dirroot . '/' . $path;
1678 return $res;
1679 }
1680 }
1681 }
1682 if ($returnemptyifnotfound) {
1683 // Not found into alternate dir
1684 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1685 return '';
1686 }
1687 }
1688 } else {
1689 // For an url path
1690 // We try to get local path of file on filesystem from url
1691 // Note that trying to know if a file on disk exist by forging path on disk from url
1692 // works only for some web server and some setup. This is bugged when
1693 // using proxy, rewriting, virtual path, etc...
1694 $res = '';
1695 if ($type == 1) {
1696 $res = DOL_URL_ROOT . '/' . $path; // Standard value
1697 }
1698 if ($type == 2) {
1699 $res = DOL_MAIN_URL_ROOT . '/' . $path; // Standard value
1700 }
1701 if ($type == 3) {
1702 $res = DOL_URL_ROOT . '/' . $path;
1703 }
1704
1705 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1706 if ($key == 'main') {
1707 if ($type == 3) {
1708 /*global $dolibarr_main_url_root;*/
1709
1710 // Define $urlwithroot
1711 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1712 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1713 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1714
1715 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . '/' . $path; // Test on start with http is for old conf syntax
1716 }
1717 continue;
1718 }
1719 $regs = array();
1720 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1721 if (!empty($regs[1])) {
1722 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1723 //if (file_exists($dirroot.'/'.$regs[1])) {
1724 if (@file_exists($dirroot . '/' . $regs[1])) { // avoid [php:warn]
1725 if ($type == 1) {
1726 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1727 } elseif ($type == 2) {
1728 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1729 } elseif ($type == 3) {
1730 /*global $dolibarr_main_url_root;*/
1731
1732 // Define $urlwithroot
1733 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1734 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1735 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1736
1737 $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
1738 }
1739 break;
1740 }
1741 }
1742 }
1743 }
1744
1745 return $res;
1746}
1747
1756function dolBuildUrl($url, $params = [], $addtoken = false)
1757{
1758 global $db, $hookmanager;
1759
1760 if (!is_object($hookmanager)) {
1761 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
1762 $hookmanager = new HookManager($db);
1763 }
1764 if ((!isset($params['mainmenu']) || empty($params['mainmenu'])) && GETPOSTISSET('mainmenu')) {
1765 $params = array_merge($params, ['mainmenu' => (GETPOST('mainmenu', 'restricthtml'))]);
1766 }
1767 if ((!isset($params['leftmenu'])/* || empty($params['leftmenu']) */) && GETPOSTISSET('leftmenu')) { // do not fill leftmenu if we have leftmenu=
1768 $params = array_merge($params, ['leftmenu' => (GETPOST('leftmenu', 'restricthtml'))]);
1769 }
1770 $parameters = [
1771 'path' => &$url,
1772 'params' => &$params,
1773 'addtoken' => &$addtoken,
1774 ];
1775 $hookmanager->executeHooks('buildurl', $parameters);
1776 if ($addtoken) {
1777 $params = array_merge($params, ['token' => newToken()]);
1778 }
1779 // TODO TO REMOVE
1780 if (getDolGlobalString('MAIN_DEBUG_DOL_BUILDURL')) {
1781 $params = array_merge($params, ['debug' => 'debug']);
1782 }
1783 if ($params) {
1784 $url .= '?' . http_build_query($params);
1785 }
1786
1787 return $url;
1788}
1789
1800function dol_get_object_properties($obj, $properties = [])
1801{
1802 // Get real properties using get_object_vars() if $properties is empty
1803 if (empty($properties)) {
1804 return get_object_vars($obj);
1805 }
1806
1807 $existingProperties = [];
1808 $realProperties = get_object_vars($obj);
1809
1810 // Get the real or magic property values
1811 foreach ($properties as $property) {
1812 if (array_key_exists($property, $realProperties)) {
1813 // Real property, add the value
1814 $existingProperties[$property] = $obj->{$property};
1815 } elseif (property_exists($obj, $property)) {
1816 // Magic property
1817 $existingProperties[$property] = $obj->{$property};
1818 }
1819 }
1820
1821 return $existingProperties;
1822}
1823
1824
1840function dol_clone($srcobject, $native = 2)
1841{
1842 if ($native == 0) {
1843 // deprecated method, use the method with native = 2 instead
1844 dol_syslog("Warning, call to dol_clone() with the deprecated parameter native=0, use 2 instead", LOG_WARNING);
1845
1846 $tmpsavdb = null;
1847 if (isset($srcobject->db) && isset($srcobject->db->db) && is_object($srcobject->db->db) && get_class($srcobject->db->db) == 'PgSql\Connection') {
1848 $tmpsavdb = $srcobject->db;
1849 unset($srcobject->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1850 }
1851
1852 $myclone = unserialize(serialize($srcobject)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1853
1854 if (!empty($tmpsavdb)) {
1855 $srcobject->db = $tmpsavdb;
1856 }
1857 } elseif ($native == 2) {
1858 // recommended method to have a full secured isolated cloned object
1859 $myclone = new stdClass();
1860 $tmparray = get_object_vars($srcobject); // return only public properties
1861
1862 if (is_array($tmparray)) {
1863 foreach ($tmparray as $propertykey => $propertyval) {
1864 if (is_scalar($propertyval) || is_array($propertyval)) {
1865 $myclone->$propertykey = $propertyval;
1866 }
1867 }
1868 }
1869 } else {
1870 $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)
1871 }
1872
1873 return $myclone;
1874}
1875
1876
1885function dol_clone_in_array($srcobject, $startlevel = 0)
1886{
1887 if (is_object($srcobject)) {
1888 $srcobject = get_object_vars($srcobject); // exclude private/protected properties
1889 }
1890
1891 if (is_array($srcobject)) {
1892 $result = [];
1893 foreach ($srcobject as $key => $value) {
1894 if (in_array($key, array('db', 'fields', 'error', 'errorhidden', 'errors', 'oldcopy', 'linkedObjects', 'linked_objects'))) {
1895 continue;
1896 }
1897 $result[$key] = dol_clone_in_array($value, $startlevel + 1);
1898 }
1899 return $result;
1900 }
1901
1902 return $srcobject;
1903}
1904
1905
1915function dol_size($size, $type = '')
1916{
1917 global $conf;
1918 if (empty($conf->dol_optimize_smallscreen)) {
1919 return $size;
1920 }
1921 if ($type == 'width' && $size > 250) {
1922 return 250;
1923 } else {
1924 return 10;
1925 }
1926}
1927
1928
1942function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0, $allowdash = 0)
1943{
1944 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1945 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1946 // Char '/' and '\' are file delimiters.
1947 // 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
1948 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1949 if ($includequotes) {
1950 $filesystem_forbidden_chars[] = "'";
1951 }
1952 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1953 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1954 if (empty($allowdash)) {
1955 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1956 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1957 }
1958 $tmp = str_replace('..', '', $tmp);
1959 $tmp = str_replace('~', $newstr, $tmp);
1960 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1961
1962 return $tmp;
1963}
1964
1965
1978function dol_sanitizePathName($str, $newstr = '_', $unaccent = 0, $allowdash = 0)
1979{
1980 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1981 // Char '>' '<' '|' '$' ';' and '`' are special chars for shells.
1982 // Char '?' and '*' are for wild card chars.
1983 // Char '"' is dangerous.
1984 // Char '°' is just not expected.
1985 // 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
1986 // Chars '--' and '~' can be used for path transversal
1987 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1988
1989 $tmp = $str;
1990 if ($unaccent) {
1991 $tmp = dol_string_unaccent($tmp);
1992 }
1993 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1994 $tmp = preg_replace('/\-\-+/', $newstr, $tmp);
1995 if (empty($allowdash)) {
1996 $tmp = preg_replace('/\s+\-([^\s])/', ' '.$newstr.'$1', $tmp);
1997 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1998 }
1999 $tmp = str_replace('..', $newstr, $tmp);
2000 $tmp = str_replace('~', $newstr, $tmp);
2001 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
2002
2003 return $tmp;
2004}
2005
2013function dol_sanitizeUrl($stringtoclean, $type = 1)
2014{
2015 // 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)
2016 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
2017 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2018 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
2019 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
2020
2021 $stringtoclean = str_replace('\\', '/', $stringtoclean);
2022 if ($type == 1) {
2023 // removing : should disable links to external url like http:aaa)
2024 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
2025 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
2026 }
2027
2028 do {
2029 $oldstringtoclean = $stringtoclean;
2030 // removing '&colon' should disable links to external url like http:aaa)
2031 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
2032 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
2033 } while ($oldstringtoclean != $stringtoclean);
2034
2035 if ($type == 1) {
2036 // removing '//' should disable links to external url like //aaa or http//)
2037 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
2038 }
2039
2040 return $stringtoclean;
2041}
2042
2049function dol_sanitizeEmail($stringtoclean)
2050{
2051 do {
2052 $oldstringtoclean = $stringtoclean;
2053 $stringtoclean = str_ireplace(array('"', ':', '[', ']', "\n", "\r", '\\', '\/'), '', $stringtoclean);
2054 } while ($oldstringtoclean != $stringtoclean);
2055
2056 return $stringtoclean;
2057}
2058
2067function dol_sanitizeKeyCode($str)
2068{
2069 return preg_replace('/[^\w]+/', '', $str);
2070}
2071
2072
2081function dol_string_unaccent($str)
2082{
2083 if (is_null($str)) {
2084 return '';
2085 }
2086
2087 if (utf8_check($str)) {
2088 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
2089 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
2090 return $transliterator->transliterate($str);
2091 }
2092 // See http://www.utf8-chartable.de/
2093 $string = rawurlencode($str);
2094 $replacements = array(
2095 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
2096 '%C3%87' => 'C',
2097 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
2098 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
2099 '%C3%91' => 'N',
2100 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', '%C5%90' => 'O',
2101 '%C5%A0' => 'S',
2102 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', '%C5%B0' => 'U',
2103 '%C3%9D' => 'Y', '%C5%B8' => 'y',
2104 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
2105 '%C3%A7' => 'c',
2106 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
2107 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
2108 '%C3%B1' => 'n',
2109 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', '%C5%91' => 'o',
2110 '%C5%A1' => 's',
2111 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', '%C5%B1' => 'u',
2112 '%C3%BD' => 'y', '%C3%BF' => 'y',
2113 '%CC%80' => '',
2114 '%CC%81' => '',
2115 '%CC%82' => '',
2116 '%CC%83' => '',
2117 '%CC%84' => '',
2118 '%CC%85' => '',
2119 '%CC%86' => '',
2120 '%CC%87' => '',
2121 '%CC%88' => '',
2122 '%CC%89' => '',
2123 '%CC%8A' => '',
2124 '%CC%8B' => '',
2125 '%CC%8C' => '',
2126 '%CC%8D' => '',
2127 '%CC%8E' => '',
2128 '%CC%8F' => '',
2129 '%CC%90' => '',
2130 '%CC%91' => '',
2131 '%CC%A7' => ''
2132 );
2133 $string = strtr($string, $replacements);
2134 return rawurldecode($string);
2135 } else {
2136 // See http://www.ascii-code.com/
2137 $string = strtr(
2138 $str,
2139 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
2140 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
2141 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
2142 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
2143 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
2144 \xF9\xFA\xFB\xFC\xFD\xFF",
2145 "AAAAAAC
2146 EEEEIIIIDN
2147 OOOOOUUUY
2148 aaaaaaceeee
2149 iiiidnooooo
2150 uuuuyy"
2151 );
2152 $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"));
2153 return $string;
2154 }
2155}
2156
2170function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
2171{
2172 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
2173 if (empty($keepspaces)) {
2174 $forbidden_chars_to_replace[] = " ";
2175 }
2176 $forbidden_chars_to_remove = array();
2177 //$forbidden_chars_to_remove=array("(",")");
2178
2179 if (is_array($badcharstoreplace)) {
2180 $forbidden_chars_to_replace = $badcharstoreplace;
2181 }
2182 if (is_array($badcharstoremove)) {
2183 $forbidden_chars_to_remove = $badcharstoremove;
2184 }
2185
2186 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2187 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
2188}
2189
2190
2204function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2205{
2206 if ($removetabcrlf) {
2207 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2208 } else {
2209 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
2210 }
2211}
2212
2219function dolSlugify($stringtoslugify)
2220{
2221 $slug = dol_string_unaccent($stringtoslugify);
2222
2223 // Convert special characters to their ASCII equivalents
2224 if (function_exists('iconv')) {
2225 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2226 }
2227
2228 // Convert to lowercase
2229 $slug = strtolower($slug);
2230
2231 // Replace non-alphanumeric characters with hyphens
2232 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2233
2234 // Remove leading and trailing hyphens
2235 $slug = trim($slug, '-');
2236
2237 return $slug;
2238}
2239
2248function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2249{
2250 if (is_null($stringtoescape)) {
2251 return '';
2252 }
2253
2254 // escape quotes and backslashes, newlines, etc.
2255 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2256 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2257 if (empty($noescapebackslashn)) {
2258 $substitjs["\n"] = '\\n';
2259 $substitjs['\\'] = '\\\\';
2260 }
2261 if (empty($mode)) {
2262 $substitjs["'"] = "\\'";
2263 $substitjs['"'] = "\\'";
2264 } elseif ($mode == 1) {
2265 $substitjs["'"] = "\\'";
2266 } elseif ($mode == 2) {
2267 $substitjs['"'] = '\\"';
2268 } elseif ($mode == 3) {
2269 $substitjs["'"] = "\\'";
2270 $substitjs['"'] = "\\\"";
2271 }
2272 return strtr((string) $stringtoescape, $substitjs);
2273}
2274
2284function dol_escape_uri($stringtoescape)
2285{
2286 return rawurlencode($stringtoescape);
2287}
2288
2295function dol_escape_json($stringtoescape)
2296{
2297 return str_replace('"', '\"', $stringtoescape);
2298}
2299
2307function dol_escape_php($stringtoescape, $stringforquotes = 2)
2308{
2309 if (is_null($stringtoescape)) {
2310 return '';
2311 }
2312
2313 if ($stringforquotes == 2) {
2314 return str_replace('"', "'", $stringtoescape);
2315 } elseif ($stringforquotes == 1) {
2316 // We remove the \ char.
2317 // If we allow the \ char, we can have $stringtoescape =
2318 // abc\';phpcodedanger; so the escapement will become
2319 // abc\\';phpcodedanger; and injecting this into
2320 // $a='...' will give $ac='abc\\';phpcodedanger;
2321 $stringtoescape = str_replace('\\', '', $stringtoescape);
2322 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2323 }
2324
2325 return 'Bad parameter for stringforquotes in dol_escape_php';
2326}
2327
2334function dol_escape_all($stringtoescape)
2335{
2336 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2337}
2338
2345function dol_escape_xml($stringtoescape)
2346{
2347 return $stringtoescape;
2348}
2349
2359function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2360{
2361 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2362}
2363
2372function dolPrintText($s)
2373{
2374 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2375}
2376
2387function dolPrintHTML($s, $allowiframe = 0)
2388{
2389 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2390 //$isAlreadyHTML = dol_textishtml($s);
2391
2392 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2393 // encode only special char like é but not &, <, >, ", ' if already HTML.
2394 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2395
2396 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2397}
2398
2409function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2410{
2411 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2412 if (!empty($allowothertags) && is_array($allowothertags)) {
2413 $allowedtags = array_merge($allowedtags, $allowothertags);
2414 }
2415 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2416 // The dol_escape_htmltag will escape html tags.
2417 if ($escapeonlyhtmltags) {
2418 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2419 } else {
2420 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2421 }
2422}
2423
2432function dolPrintHTMLForAttributeUrl($s)
2433{
2434 // 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)
2435 // The dol_escape_htmltag will escape html chars.
2436 $escapeonlyhtmltags = 1;
2437 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2438}
2439
2449function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2450{
2451 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2452}
2453
2460function dolPrintPassword($s)
2461{
2462 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2463}
2464
2465
2482function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2483{
2484 if ($noescapetags == 'common') {
2485 $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';
2486 // Add also html5 tags
2487 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2488 }
2489 if ($cleanalsojavascript) {
2490 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2491 }
2492
2493 // escape quotes and backslashes, newlines, etc.
2494 if ($escapeonlyhtmltags) {
2495 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2496 } else {
2497 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2498 // Because content can be or not already HTML.
2499 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2500 // In a future, we should not need this
2501
2502 $tmp = (string) $stringtoescape;
2503
2504 // We protect the 6 special entities that we don't want to decode.
2505 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2506 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2507 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2508 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2509 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2510 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2511
2512 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2513
2514 // We restore the 6 special entities that we don't want to have been decoded by previous command
2515 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2516 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2517 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2518 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2519 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2520 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2521
2522 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2523 }
2524 if (!$keepb) {
2525 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2526 }
2527 if (!$keepn) {
2528 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2529 } elseif ($keepn == -1) {
2530 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2531 }
2532
2533 if ($escapeonlyhtmltags) {
2534 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2535 return $tmp;
2536 } else {
2537 // Now we protect all the tags we want to keep
2538 $tmparrayoftags = array();
2539 if ($noescapetags) {
2540 $tmparrayoftags = explode(',', $noescapetags);
2541 }
2542
2543 if (count($tmparrayoftags)) {
2544 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2545
2546 $reg = array();
2547 // Remove reserved keywords. They are forbidden in a source string
2548 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2549
2550 foreach ($tmparrayoftags as $tagtoreplace) {
2551 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2552 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . '>/', '__BEGINTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2553 $tmp = str_ireplace('</' . $tagtoreplace . '>', '__ENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2554 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . ' \/>/', '__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2555
2556 // For case of tag with attributes
2557 do {
2558 $tmpold = $tmp;
2559
2560 if (preg_match('/<' . preg_quote($tagtoreplace, '/') . '(\s+)([^>]+)>/', $tmp, $reg)) {
2561 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2562 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2563 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2564 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2565 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2566 $tmp = str_replace('<' . $tagtoreplace . $reg[1] . $reg[2] . '>', '__BEGINTAGTOREPLACE' . $tagtoreplace . '[' . $tmpattributes . ']__', $tmp);
2567 }
2568
2569 $diff = strcmp($tmpold, $tmp);
2570 } while ($diff);
2571 }
2572
2573 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2574 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2575 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2576 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2577 }
2578
2579 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2580 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2581
2582 //print $result;
2583
2584 if (count($tmparrayoftags)) {
2585 // Restore protected tags
2586 foreach ($tmparrayoftags as $tagtoreplace) {
2587 $result = str_ireplace('__BEGINTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . '>', $result);
2588 $result = preg_replace('/__BEGINTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1>', $result);
2589 $result = str_ireplace('__ENDTAGTOREPLACE' . $tagtoreplace . '__', '</' . $tagtoreplace . '>', $result);
2590 $result = str_ireplace('__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . ' />', $result);
2591 $result = preg_replace('/__BEGINENDTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1 />', $result);
2592 }
2593
2594 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2595
2596 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2597 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2598 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2599 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2600 }
2601
2602 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2603
2604 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2605
2606 return $result;
2607 }
2608}
2609
2617function dol_strtolower($string, $encoding = "UTF-8")
2618{
2619 if (function_exists('mb_strtolower')) {
2620 return mb_strtolower($string, $encoding);
2621 } else {
2622 return strtolower($string);
2623 }
2624}
2625
2634function dol_strtoupper($string, $encoding = "UTF-8")
2635{
2636 if (function_exists('mb_strtoupper')) {
2637 return mb_strtoupper($string, $encoding);
2638 } else {
2639 return strtoupper($string);
2640 }
2641}
2642
2651function dol_ucfirst($string, $encoding = "UTF-8")
2652{
2653 if (function_exists('mb_substr')) {
2654 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding) . mb_substr($string, 1, null, $encoding);
2655 } else {
2656 return ucfirst($string);
2657 }
2658}
2659
2668function dol_ucwords($string, $encoding = "UTF-8")
2669{
2670 if (function_exists('mb_convert_case')) {
2671 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2672 } else {
2673 return ucwords($string);
2674 }
2675}
2676
2677
2683function getCallerInfoString()
2684{
2685 $backtrace = debug_backtrace();
2686 $msg = "";
2687 if (count($backtrace) >= 1) {
2688 $pos = 1;
2689 if (count($backtrace) == 1) {
2690 $pos = 0;
2691 }
2692 $trace = $backtrace[$pos];
2693 if (isset($trace['file'], $trace['line'])) {
2694 $msg = " From {$trace['file']}:{$trace['line']}.";
2695 }
2696 }
2697 return $msg;
2698}
2699
2722function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2723{
2724 global $conf, $user, $debugbar;
2725
2726 // If syslog module enabled
2727 if (!isModEnabled('syslog')) {
2728 return;
2729 }
2730
2731 // Check if we are into execution of code of a website
2732 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2733 global $website, $websitekey;
2734 if (is_object($website) && !empty($website->ref)) {
2735 $suffixinfilename .= '_website_' . $website->ref;
2736 } elseif (!empty($websitekey)) {
2737 $suffixinfilename .= '_website_' . $websitekey;
2738 }
2739 }
2740
2741 // Check if we have a forced suffix
2742 if (defined('USESUFFIXINLOG')) {
2743 $suffixinfilename .= constant('USESUFFIXINLOG');
2744 }
2745
2746 if ($ident < 0) {
2747 foreach ($conf->loghandlers as $loghandlerinstance) {
2748 $loghandlerinstance->setIdent($ident);
2749 }
2750 }
2751
2752 if (!empty($message)) {
2753 // Test log level
2754 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2755 $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');
2756
2757 if (!array_key_exists($level, $logLevels)) {
2758 dol_syslog('Error Bad Log Level ' . $level, LOG_ERR);
2759 $level = LOG_ERR;
2760 }
2761 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2762 return;
2763 }
2764
2765 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2766 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2767 }
2768
2769 // If adding log inside HTML page is required
2770 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2771 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))
2772 ) {
2773 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2774 $osuser = " " . sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2775
2776 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S") . " " . sprintf("%-7s", $logLevels[$level]) . " " . $ospid . " " . $osuser . " " . $message;
2777 }
2778
2779 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2780 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2781 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2782 print "\n\n<!-- Log start\n";
2783 print dol_escape_htmltag($message) . "\n";
2784 print "Log end -->\n";
2785 }
2786
2787 $data = array(
2788 'message' => $message,
2789 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2790 'level' => $level,
2791 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2792 'ip' => '',
2793 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2794 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2795 );
2796
2797 // For log, we want the reliable IP first.
2798 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2799 if (!empty($remoteip)) {
2800 $data['ip'] = $remoteip;
2801 // This is when server run behind a reverse proxy
2802 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2803 // $data['ip'] is last
2804 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2805 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2806 $data['ip'] = '';
2807 $foundremoteip = 0;
2808 $j = 0;
2809 foreach ($tmpips as $tmpip) {
2810 $tmpip = trim($tmpip);
2811 if (strtolower($tmpip) == strtolower($remoteip)) {
2812 $foundremoteip = 1;
2813 }
2814 if (empty($data['ip'])) {
2815 $data['ip'] = $tmpip;
2816 } else {
2817 $j++;
2818 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2819 }
2820 }
2821 if (!$foundremoteip) {
2822 $j++;
2823 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2824 }
2825 $data['ip'] .= (($j > 0) ? ']' : '');
2826 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2827 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2828 $data['ip'] = '';
2829 $foundremoteip = 0;
2830 $j = 0;
2831 foreach ($tmpips as $tmpip) {
2832 $tmpip = trim($tmpip);
2833 if (strtolower($tmpip) == strtolower($remoteip)) {
2834 $foundremoteip = 1;
2835 }
2836 if (empty($data['ip'])) {
2837 $data['ip'] = $tmpip;
2838 } else {
2839 $j++;
2840 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2841 }
2842 }
2843 if (!$foundremoteip) {
2844 $j++;
2845 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2846 }
2847 $data['ip'] .= (($j > 0) ? ']' : '');
2848 }
2849 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2850 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2851 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2852 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2853 // 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).
2854 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2855 } else {
2856 $data['ip'] = '???';
2857 }
2858
2859 if (!empty($_SERVER['USERNAME'])) {
2860 // 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).
2861 $data['osuser'] = (string) $_SERVER['USERNAME'];
2862 } elseif (!empty($_SERVER['LOGNAME'])) {
2863 // 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).
2864 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2865 }
2866
2867 // Loop on each log handler and send output
2868 foreach ($conf->loghandlers as $loghandlerinstance) {
2869 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2870 continue;
2871 }
2872 $loghandlerinstance->export($data, $suffixinfilename);
2873 }
2874 unset($data);
2875 }
2876
2877 if ($ident > 0) {
2878 foreach ($conf->loghandlers as $loghandlerinstance) {
2879 $loghandlerinstance->setIdent($ident);
2880 }
2881 }
2882}
2883
2895function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2896{
2897 global $langs, $db;
2898
2899 $form = new Form($db);
2900
2901 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2902 if (empty($templatenameforexport)) {
2903 $templatenameforexport = 'website_' . $website->ref;
2904 }
2905
2906 $out = '';
2907 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="' . dol_escape_htmltag($buttonstring) . '"/>';
2908
2909 // for generate popup
2910 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2911 $out .= 'jQuery(document).ready(function () {';
2912 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2913 $out .= ' var dialogHtml = \'';
2914
2915 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2916 $dialogcontent .= ' <div style="margin-top: 20px;">';
2917 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>' . $langs->trans("ExportSiteLabel") . '...</label><br>';
2918 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2919 $dialogcontent .= ' </div>';
2920 $dialogcontent .= ' <br>';
2921 $dialogcontent .= ' <div style="margin-top: 20px;">';
2922 $dialogcontent .= ' <strong>' . $langs->trans("ExportSiteGitLabel") . ' ' . $form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '') . '</strong><br>';
2923 $dialogcontent .= ' <form action="' . dol_escape_htmltag($overwriteGitUrl) . '" method="POST">';
2924 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2925 $dialogcontent .= ' <input type="hidden" name="token" value="' . newToken() . '">';
2926 $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>';
2927 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2928 $dialogcontent .= ' </form>';
2929 $dialogcontent .= ' </div>';
2930 $dialogcontent .= ' </div>';
2931
2932 $out .= dol_escape_js($dialogcontent);
2933
2934 $out .= '\';';
2935
2936
2937 // Add the content of the dialog to the body of the page
2938 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2939 $out .= ' if ($dialog.length > 0) {
2940 $dialog.remove();
2941 }
2942 jQuery("body").append(dialogHtml);';
2943
2944 // Configuration of popup
2945 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2946 $out .= ' autoOpen: false,';
2947 $out .= ' modal: true,';
2948 $out .= ' height: 290,';
2949 $out .= ' width: "40%",';
2950 $out .= ' title: "' . dol_escape_js($label) . '",';
2951 $out .= ' });';
2952
2953 // Simulate a click on the original "submit" input to export the site.
2954 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2955 $out .= ' console.log("Clic on exportsite.");';
2956 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2957 $out .= ' console.log("element founded:", target.length > 0);';
2958 $out .= ' if (target.length > 0) { target.click(); }';
2959 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2960 $out .= ' });';
2961
2962 // open popup
2963 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2964 $out .= ' return false;';
2965 $out .= ' });';
2966 $out .= '});';
2967 $out .= '</script>';
2968
2969 return $out;
2970}
2971
2972
2989function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
2990{
2991 global $conf;
2992
2993 if (strpos($url, '?') > 0) {
2994 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
2995 } else {
2996 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
2997 }
2998
2999 if (preg_match('/^https/i', $url)) {
3000 $urltoopen = $url;
3001 } else {
3002 $urltoopen = DOL_URL_ROOT . $url;
3003 }
3004
3005 $out = '';
3006
3007 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
3008 $out .= '<!-- a link for button to open url into a dialog popup -->';
3009 $out .= '<a ' . ($accesskey ? ' accesskey="' . $accesskey . '"' : '') . ' class="cursorpointer reposition button_' . $name . ($morecss ? ' ' . $morecss : '') . '"' . $disabled . ' title="' . dol_escape_htmltag($label) . '"';
3010 if (empty($conf->use_javascript_ajax)) {
3011 $out .= ' href="' . $urltoopen . '" target="_blank"';
3012 } elseif ($jsonopen) {
3013 $out .= ' href="#" onclick="' . $jsonopen . '"';
3014 } else {
3015 $out .= ' href="#"';
3016 }
3017 $out .= '>' . $buttonstring . '</a>';
3018
3019 if (!empty($conf->use_javascript_ajax)) {
3020 // Add code to open url using the popup.
3021 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
3022 $out .= '<div id="idfordialog' . $name . '" class="hidden">' . (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '') . '</div>';
3023
3024 $out .= '<!-- Add js code to open dialog popup on dialog -->';
3025 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">
3026 jQuery(document).ready(function () {
3027 jQuery(".button_' . $name . '").click(function () {
3028 console.log(\'Open popup with jQuery(...).dialog() on URL ' . dol_escape_js($urltoopen) . '\');
3029 var $tmpdialog = $(\'#idfordialog' . $name . '\');
3030 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog' . $name . '" style="border: 0px;" src="' . $urltoopen . '" width="100%" height="98%"></iframe>\');
3031 $tmpdialog.dialog({
3032 autoOpen: false,
3033 modal: true,
3034 height: (window.innerHeight - 150),
3035 width: \'80%\',
3036 title: \'' . dol_escape_js($label) . '\',
3037 open: function (event, ui) {
3038 console.log("open popup name=' . $name . '");
3039 },
3040 close: function (event, ui) {
3041 console.log("Popup is closed, run jsonclose = ' . $jsonclose . '");
3042 ' . (empty($jsonclose) ? '' : $jsonclose . ';') . '
3043 }
3044 });
3045
3046 $tmpdialog.dialog(\'open\');
3047 return false;
3048 });
3049 });
3050 </script>';
3051 }
3052 return $out;
3053}
3054
3071function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
3072{
3073 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
3074}
3075
3093function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
3094{
3095 global $conf, $langs, $hookmanager;
3096
3097 // Show title
3098 $showtitle = 1;
3099 if (!empty($conf->dol_optimize_smallscreen)) {
3100 $showtitle = 0;
3101 }
3102
3103 $out = "\n" . '<!-- dol_fiche_head - dol_get_fiche_head -->';
3104
3105 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3106 $out .= '<div class="tabs' . ($picto ? '' : ' nopaddingleft') . '" data-role="controlgroup" data-type="horizontal">' . "\n";
3107 }
3108
3109 // Show right part
3110 if ($morehtmlright) {
3111 $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.
3112 }
3113
3114 // Show tabs
3115
3116 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
3117 $maxkey = -1;
3118 if (is_array($links) && !empty($links)) {
3119 $keys = array_keys($links);
3120 if (count($keys)) {
3121 $maxkey = max($keys);
3122 }
3123 }
3124
3125 // Show tabs
3126 // if =0 we don't use the feature
3127 if (empty($limittoshow)) {
3128 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
3129 }
3130 if (!empty($conf->dol_optimize_smallscreen)) {
3131 $limittoshow = 2;
3132 }
3133
3134 $displaytab = 0;
3135 $nbintab = 0;
3136 $popuptab = 0;
3137 $outmore = '';
3138 for ($i = 0; $i <= $maxkey; $i++) {
3139 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3140 // If active tab is already present
3141 if ($i >= $limittoshow) {
3142 $limittoshow--;
3143 }
3144 }
3145 }
3146
3147 for ($i = 0; $i <= $maxkey; $i++) {
3148 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3149 $isactive = true;
3150 } else {
3151 $isactive = false;
3152 }
3153
3154 if ($i < $limittoshow || $isactive) {
3155 // Output entry with a visible tab
3156 $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])) . ' -->';
3157
3158 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3159 if (!empty($links[$i][0])) {
3160 $out .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3161 } else {
3162 $out .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3163 }
3164 } elseif (!empty($links[$i][1])) {
3165 //print "x $i $active ".$links[$i][2]." z";
3166 $out .= '<div class="tab tab' . ($isactive ? 'active' : 'unactive') . '" style="margin: 0 !important">';
3167
3168 if (!empty($links[$i][0])) {
3169 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
3170 $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) . '">';
3171 }
3172
3173 if ($displaytab == 0 && $picto) {
3174 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
3175 }
3176
3177 $out .= $links[$i][1];
3178 if (!empty($links[$i][0])) {
3179 $out .= '</a>' . "\n";
3180 }
3181 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
3182 $out .= '</div>';
3183 }
3184
3185 $out .= '</div>';
3186 } else {
3187 // Add entry into the combo popup with the other tabs
3188 if (!$popuptab) {
3189 $popuptab = 1;
3190 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
3191 }
3192 $outmore_content = '';
3193
3194 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3195 if (!empty($links[$i][0])) {
3196 $outmore_content .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3197 } else {
3198 $outmore_content .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3199 }
3200 } elseif (!empty($links[$i][1])) {
3201 $outmore_content .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="wordwrap inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
3202 $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.
3203 $outmore_content .= '</a>' . "\n";
3204 }
3205 if ($outmore_content !== '') {
3206 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
3207 }
3208
3209 $nbintab++;
3210 }
3211
3212 $displaytab = $i + 1;
3213 }
3214 if ($popuptab) {
3215 $outmore .= '</div>';
3216 }
3217
3218 if ($popuptab) { // If there is some tabs not shown
3219 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3220 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3221 $widthofpopup = 240;
3222
3223 $tabsname = $moretabssuffix;
3224 if (empty($tabsname)) {
3225 $tabsname = str_replace("@", "", $picto);
3226 }
3227 $out .= '<div id="moretabs' . $tabsname . '" class="inline-block tabsElem valignmiddle">';
3228 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3229 $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".
3230 }
3231 $out .= '<div id="moretabsList' . $tabsname . '" style="width: ' . $widthofpopup . 'px; position: absolute; ' . $left . ': -999em; text-align: ' . $left . '; margin:0px; padding:2px; z-index:10;">';
3232 $out .= $outmore;
3233 $out .= '</div>';
3234 $out .= '<div></div>';
3235 $out .= "</div>\n";
3236
3237 $out .= '<script nonce="' . getNonce() . '">';
3238 $out .= "$('#moretabs" . $tabsname . "').mouseenter( function() {
3239 var x = this.offsetLeft, y = this.offsetTop;
3240 console.log('mouseenter " . $left . " x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3241 if ((window.innerWidth - x) < " . ($widthofpopup + 10) . ") {
3242 $('#moretabsList" . $tabsname . "').css('" . $right . "','8px');
3243 }
3244 $('#moretabsList" . $tabsname . "').css('" . $left . "','auto');
3245 });
3246 ";
3247 $out .= "$('#moretabs" . $tabsname . "').mouseleave( function() { console.log('mouseleave " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','-999em');});";
3248 $out .= "</script>";
3249 }
3250
3251 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3252 $out .= "</div>\n";
3253 }
3254
3255 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3256 $out .= "\n" . '<div id="dragDropAreaTabBar" class="tabBar' . ($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '') . ($notab == -4 ? '' : ' tabBarWithBottom'))));
3257 $out .= ($morecssdiv ? ' ' . $morecssdiv : '');
3258 $out .= '">' . "\n";
3259 }
3260 if (!empty($dragdropfile)) {
3261 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3262 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3263 }
3264 $parameters = array('tabname' => $active, 'out' => $out);
3265 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3266 if ($reshook > 0) {
3267 $out = $hookmanager->resPrint;
3268 }
3269
3270 return $out;
3271}
3272
3280function dol_fiche_end($notab = 0)
3281{
3282 print dol_get_fiche_end($notab);
3283}
3284
3291function dol_get_fiche_end($notab = 0)
3292{
3293 if (!$notab || $notab == -1) {
3294 return "\n</div>\n";
3295 } else {
3296 return '';
3297 }
3298}
3299
3319function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3320{
3321 global $conf, $form, $user, $langs, $hookmanager, $action;
3322
3323 $error = 0;
3324
3325 $maxvisiblephotos = 1;
3326 $showimage = 1;
3327 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3328 // @phan-suppress-next-line PhanUndeclaredMethod
3329 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3330 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3331 $showbarcode = 0;
3332 }
3333 $modulepart = 'unknown';
3334
3335 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3336 $modulepart = $object->element;
3337 } elseif ($object->element == 'member') {
3338 $modulepart = 'memberphoto';
3339 } elseif ($object->element == 'user') {
3340 $modulepart = 'userphoto';
3341 }
3342
3343 if (class_exists("Imagick")) {
3344 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3345 $modulepart = $object->element;
3346 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3347 $modulepart = 'ficheinter';
3348 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3349 $modulepart = 'contract';
3350 } elseif ($object->element == 'order_supplier') {
3351 $modulepart = 'supplier_order';
3352 } elseif ($object->element == 'invoice_supplier') {
3353 $modulepart = 'supplier_invoice';
3354 }
3355 }
3356
3357 if ($object->element == 'product') {
3359 '@phan-var-force Product $object';
3360 $width = 80;
3361 $cssclass = 'photowithmargin photoref';
3362 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3363 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3364 if ($conf->browser->layout == 'phone') {
3365 $maxvisiblephotos = 1;
3366 }
3367 if ($showimage) {
3368 $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>';
3369 } else {
3370 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3371 $nophoto = '';
3372 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3373 } else { // Show no photo link
3374 $nophoto = '/public/theme/common/nophoto.png';
3375 $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>';
3376 }
3377 }
3378 } elseif ($object->element == 'category') {
3380 '@phan-var-force Categorie $object';
3381 $width = 80;
3382 $cssclass = 'photowithmargin photoref';
3383 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3384 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3385 if ($conf->browser->layout == 'phone') {
3386 $maxvisiblephotos = 1;
3387 }
3388 if ($showimage) {
3389 $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>';
3390 } else {
3391 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3392 $nophoto = '';
3393 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3394 } else { // Show no photo link
3395 $nophoto = '/public/theme/common/nophoto.png';
3396 $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>';
3397 }
3398 }
3399 } elseif ($object->element == 'bom') {
3401 '@phan-var-force Bom $object';
3402 $width = 80;
3403 $cssclass = 'photowithmargin photoref';
3404 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3405 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3406 if ($conf->browser->layout == 'phone') {
3407 $maxvisiblephotos = 1;
3408 }
3409 if ($showimage) {
3410 $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>';
3411 } else {
3412 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3413 $nophoto = '';
3414 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3415 } else { // Show no photo link
3416 $nophoto = '/public/theme/common/nophoto.png';
3417 $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>';
3418 }
3419 }
3420 } elseif ($object->element == 'ticket') {
3421 $width = 80;
3422 $cssclass = 'photoref';
3424 '@phan-var-force Ticket $object';
3425 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity] . '/' . $object->ref);
3426 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3427 if ($conf->browser->layout == 'phone') {
3428 $maxvisiblephotos = 1;
3429 }
3430
3431 if ($showimage) {
3432 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3433 if ($object->nbphoto > 0) {
3434 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $showphoto . '</div>';
3435 } else {
3436 $showimage = 0;
3437 }
3438 }
3439 if (!$showimage) {
3440 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3441 $nophoto = '';
3442 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3443 } else { // Show no photo link
3444 $nophoto = img_picto('No photo', 'object_ticket');
3445 $morehtmlleft .= '<!-- No photo to show -->';
3446 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3447 $morehtmlleft .= $nophoto;
3448 $morehtmlleft .= '</div></div>';
3449 }
3450 }
3451 } else {
3452 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3453 $phototoshow = '';
3454 // Check if a preview file is available
3455 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3456 $objectref = dol_sanitizeFileName($object->ref);
3457 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity]) . "/";
3458 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3459 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3460 $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
3461 } else {
3462 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3463 }
3464 if (empty($subdir)) {
3465 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3466 }
3467
3468 $filepath = $dir_output . $subdir . "/";
3469
3470 $filepdf = $filepath . $objectref . ".pdf";
3471 $relativepath = $subdir . '/' . $objectref . '.pdf';
3472
3473 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3474 $fileimage = $filepdf . '_preview.png';
3475 $relativepathimage = $relativepath . '_preview.png';
3476
3477 $pdfexists = file_exists($filepdf);
3478
3479 // If PDF file exists
3480 if ($pdfexists) {
3481 // Conversion du PDF en image png si fichier png non existent
3482 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3483 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3484 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3485 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3486 if ($ret < 0) {
3487 $error++;
3488 }
3489 }
3490 }
3491 }
3492
3493 if ($pdfexists && !$error) {
3494 $heightforphotref = 80;
3495 if (!empty($conf->dol_optimize_smallscreen)) {
3496 $heightforphotref = 60;
3497 }
3498 // If the preview file is found
3499 if (file_exists($fileimage)) {
3500 $phototoshow = '<div class="photoref">';
3501 $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithborder" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($relativepathimage) . '">';
3502 $phototoshow .= '</div>';
3503 }
3504 }
3505 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3506 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3507 }
3508
3509 if ($phototoshow) {
3510 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3511 $morehtmlleft .= $phototoshow;
3512 $morehtmlleft .= '</div>';
3513 }
3514 }
3515
3516 if (empty($phototoshow)) { // Show No photo link (picto of object)
3517 if ($object->element == 'action') {
3518 $width = 80;
3519 $cssclass = 'photorefcenter';
3520 $nophoto = img_picto('No photo', 'title_agenda');
3521 } else {
3522 $width = 14;
3523 $cssclass = 'photorefcenter';
3524 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3525 $prefix = 'object_';
3526 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3527 $picto = 'project'; // instead of projectpub
3528 }
3529 if (strpos($picto, 'fontawesome_') !== false) {
3530 $prefix = '';
3531 }
3532 $nophoto = img_picto('No photo', $prefix . $picto);
3533 }
3534 $morehtmlleft .= '<!-- No photo to show -->';
3535 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3536 $morehtmlleft .= $nophoto;
3537 $morehtmlleft .= '</div></div>';
3538 }
3539 }
3540
3541 // Show barcode
3542 if ($showbarcode) {
3543 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $form->showbarcode($object, 100, 'photoref valignmiddle') . '</div>';
3544 }
3545
3546 if ($object->element == 'societe') {
3548 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3549 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3550 } else {
3551 $morehtmlstatus .= $object->getLibStatut(6);
3552 }
3553 } elseif ($object->element == 'product') {
3555 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3556 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3557 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3558 } else {
3559 $morehtmlstatus .= '<span class="statusrefsell">' . $object->getLibStatut(6, 0) . '</span>';
3560 }
3561 $morehtmlstatus .= ' &nbsp; ';
3562 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3563 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3564 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'status_buy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3565 } else {
3566 $morehtmlstatus .= '<span class="statusrefbuy">' . $object->getLibStatut(6, 1) . '</span>';
3567 }
3568 } elseif (in_array($object->element, array('salary'))) {
3570 '@phan-var-force Salary $object';
3571 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3572 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3573 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3574 }
3575 $morehtmlstatus .= $tmptxt;
3576 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) {
3578 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3579 if (!isset($object->alreadypaid)) {
3580 $object->totalpaid = $object->getSommePaiement(0);
3581 $object->totalcreditnotes = $object->getSumCreditNotesUsed(0);
3582 $object->totaldeposits = $object->getSumDepositsUsed(0);
3583 $object->alreadypaid = $object->totalpaid + $object->totalcreditnotes + $object->totaldeposits;
3584 }
3585 $tmptxt = $object->getLibStatut(6, (float) $object->alreadypaid);
3586 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3587 $tmptxt = $object->getLibStatut(5, (float) $object->alreadypaid);
3588 }
3589 $morehtmlstatus .= $tmptxt;
3590 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid like for invoices
3592 '@phan-var-force ChargeSociales|Loan|Tva $object';
3593 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3594 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3595 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3596 }
3597 $morehtmlstatus .= $tmptxt;
3598 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3600 if ($object->status == 0) {
3601 $morehtmlstatus .= $object->getLibStatut(5);
3602 } else {
3603 $morehtmlstatus .= $object->getLibStatut(4);
3604 }
3605 } elseif ($object->element == 'facturerec') {
3607 '@phan-var-force FactureRec $object';
3608 if ($object->frequency == 0) {
3609 $morehtmlstatus .= $object->getLibStatut(2);
3610 } else {
3611 $morehtmlstatus .= $object->getLibStatut(5);
3612 }
3613 } elseif ($object->element == 'project_task') {
3615 $tmptxt = $object->getLibStatut(4);
3616 $morehtmlstatus .= $tmptxt;
3617 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3618 $tmptxt = $object->getLibStatut(6);
3619 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3620 $tmptxt = $object->getLibStatut(5);
3621 }
3622 $morehtmlstatus .= $tmptxt;
3623 }
3624
3625 // Say if object was dispatched/transferred "into accountancy"
3626 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3627 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3628 if (method_exists($object, 'getVentilExportCompta')) {
3629 $accounted = $object->getVentilExportCompta(1);
3630 $langs->load("accountancy");
3631 $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>';
3632 }
3633 }
3634
3635 // Add alias for thirdparty
3636 if (!empty($object->name_alias)) {
3638 '@phan-var-force Societe $object';
3639 $morehtmlref .= '<div class="refidno opacitymedium">' . dol_escape_htmltag($object->name_alias) . '</div>';
3640 }
3641
3642 // Add label
3643 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3645 if (!empty($object->label)) {
3646 $morehtmlref .= '<div class="refidno banner-object-label">' . $object->label . '</div>';
3647 }
3648 }
3649 // Show address and email
3650 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3651 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3652 if ($moreaddress) {
3653 $morehtmlref .= '<div class="refidno refaddress">';
3654 $morehtmlref .= $moreaddress;
3655 $morehtmlref .= '</div>';
3656 }
3657 }
3658 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)) {
3659 $morehtmlref .= '<div style="clear: both;"></div>';
3660 $morehtmlref .= '<div class="refidno opacitymedium">';
3661 $morehtmlref .= $langs->trans("TechnicalID") . ': ' . ((int) $object->id);
3662 $morehtmlref .= '</div>';
3663 }
3664
3665 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3666 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3667 if ($reshook < 0) {
3668 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3669 } elseif (empty($reshook)) {
3670 $morehtmlref .= $hookmanager->resPrint;
3671 } elseif ($reshook > 0) {
3672 $morehtmlref = $hookmanager->resPrint;
3673 }
3674
3675 // $morehtml is the right part (link "Back to list")
3676 // $morehtmlref is the part after the ref
3677 // $morehtmlleft is the picto or photo of banner
3678 // $morehtmlstatus is part under the status
3679 // $morehtmlright is part of htmlright
3680
3681 print '<div class="' . ($onlybanner ? 'arearefnobottom ' : 'arearef ') . 'heightref valignmiddle centpercent object-banner-tab-container" data-module-part="'.dolPrintHTMLForAttribute($modulepart).'">';
3682 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3683 print '</div>';
3684 print '<div class="underrefbanner clearboth"></div>';
3685}
3686
3696function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3697{
3698 global $langs;
3699 $ret = '';
3700 if ($fieldrequired) {
3701 $ret .= '<span class="fieldrequired">';
3702 }
3703 $ret .= '<label for="' . $fieldkey . '">';
3704 $ret .= $langs->trans($langkey);
3705 $ret .= '</label>';
3706 if ($fieldrequired) {
3707 $ret .= '</span>';
3708 }
3709 return $ret;
3710}
3711
3725function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3726{
3727 global $langs, $hookmanager;
3728
3729 $ret = '';
3730 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3731
3732 // See format of addresses on https://en.wikipedia.org/wiki/Address
3733 // Address
3734 if (empty($mode)) {
3735 $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)));
3736 }
3737 // Zip/Town/State
3738 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3739 // US: title firstname name \n address lines \n town, state, zip \n country
3740 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3741 $ret .= (($ret && $town) ? $sep : '') . $town;
3742
3743 if (!empty($object->state)) {
3744 $ret .= ($ret ? ($town ? ", " : $sep) : '') . $object->state;
3745 }
3746 if (!empty($object->zip)) {
3747 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '') . $object->zip;
3748 }
3749 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3750 // UK: title firstname name \n address lines \n town state \n zip \n country
3751 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3752 $ret .= ($ret ? $sep : '') . $town;
3753 if (!empty($object->state)) {
3754 $ret .= ($ret ? ", " : '') . $object->state;
3755 }
3756 if (!empty($object->zip)) {
3757 $ret .= ($ret ? $sep : '') . $object->zip;
3758 }
3759 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3760 // ES: title firstname name \n address lines \n zip town \n state \n country
3761 $ret .= ($ret ? $sep : '') . $object->zip;
3762 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3763 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3764 if (!empty($object->state)) {
3765 $ret .= $sep . $object->state;
3766 }
3767 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3768 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3769 // See https://www.sljfaq.org/afaq/addresses.html
3770 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3771 $ret .= ($ret ? $sep : '') . ($object->state ? $object->state . ', ' : '') . $town . ($object->zip ? ' ' : '') . $object->zip;
3772 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3773 // IT: title firstname name\n address lines \n zip town state_code \n country
3774 $ret .= ($ret ? $sep : '') . $object->zip;
3775 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3776 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3777 $ret .= (empty($object->state_code) ? '' : (' ' . $object->state_code));
3778 } else {
3779 // Other: title firstname name \n address lines \n zip town[, state] \n country
3780 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3781 $ret .= !empty($object->zip) ? (($ret ? $sep : '') . $object->zip) : '';
3782 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')) . $town) : '');
3783 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3784 $ret .= ($ret ? ", " : '') . $object->state;
3785 }
3786 }
3787
3788 if (!is_object($outputlangs)) {
3789 $outputlangs = $langs;
3790 }
3791 if ($withcountry) {
3792 $langs->load("dict");
3793 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '') . $outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country" . $object->country_code)));
3794 }
3795 if ($hookmanager) {
3796 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs, 'mode' => $mode, 'extralangcode' => $extralangcode);
3797 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3798 if ($reshook > 0) {
3799 $ret = '';
3800 }
3801 $ret .= $hookmanager->resPrint;
3802 }
3803
3804 return $ret;
3805}
3806
3807
3808
3818function dol_strftime($fmt, $ts = false, $is_gmt = false)
3819{
3820 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3821 return dol_print_date($ts, $fmt, $is_gmt);
3822 } else {
3823 return 'Error date outside supported range';
3824 }
3825}
3826
3849function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false, $decorate = 0)
3850{
3851 global $conf, $langs;
3852
3853 // If date undefined or "", we return ""
3854 if (dol_strlen((string) $time) == 0) {
3855 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3856 }
3857
3858 if ($tzoutput === 'auto') {
3859 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3860 }
3861
3862 // Clean parameters
3863 $to_gmt = false; // false if we want date in server timezone, true if we want to add offset
3864 $offsettz = $offsetdst = 0;
3865 if ($tzoutput) {
3866 $to_gmt = true; // For backward compatibility
3867 if (is_string($tzoutput)) {
3868 if ($tzoutput == 'tzserver') {
3869 $to_gmt = false;
3870 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3871 // @phan-suppress-next-line PhanPluginRedundantAssignment
3872 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3873 // @phan-suppress-next-line PhanPluginRedundantAssignment
3874 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3875 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3876 $to_gmt = true;
3877 // if no session (by example in cron) may use MAIN_DOLIBARR_USER_TIMEZONE instead UTC
3878 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? getDolGlobalString('MAIN_DOLIBARR_USER_TIMEZONE', 'UTC') : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3879
3880 if (class_exists('DateTimeZone')) {
3881 try {
3882 $user_date_tz = new DateTimeZone($offsettzstring);
3883 } catch (Exception $e) {
3884 // Bad value for $offsettzstring
3885 dol_syslog("DateInvalidTimeZoneException for timezone string '".$offsettzstring."'. Falling back to UTC.", LOG_ERR);
3886 $user_date_tz = new DateTimeZone('UTC'); // Force valid timezone as UTC
3887 }
3888 $user_dt = new DateTime();
3889 $user_dt->setTimezone($user_date_tz);
3890 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3891 $offsettz = $user_dt->getOffset(); // should include dst ?
3892 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3893 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3894 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3895 }
3896 }
3897 }
3898 }
3899 if (!is_object($outputlangs)) {
3900 $outputlangs = $langs;
3901 }
3902 if (!$format) {
3903 $format = 'daytextshort';
3904 }
3905
3906 // Do we have to reduce the length of date (year on 2 chars) to save space.
3907 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3908 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3909 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3910 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3911 if ($formatwithoutreduce != $format) {
3912 $format = $formatwithoutreduce;
3913 $reduceformat = 1;
3914 } // so format 'dayreduceformat' is processed like day
3915
3916 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3917 // TODO Add format daysmallyear and dayhoursmallyear
3918 if ($format == 'day') {
3919 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3920 } elseif ($format == 'hour') {
3921 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3922 } elseif ($format == 'hourduration') {
3923 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3924 } elseif ($format == 'daytext') {
3925 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3926 } elseif ($format == 'daytextshort') {
3927 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3928 } elseif ($format == 'dayhour') {
3929 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3930 } elseif ($format == 'dayhoursec') {
3931 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3932 } elseif ($format == 'dayhourtext') {
3933 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3934 } elseif ($format == 'dayhourtextshort') {
3935 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3936 } elseif ($format == 'dayhourlog') {
3937 // Format not sensitive to language
3938 $format = '%Y%m%d%H%M%S';
3939 } elseif ($format == 'dayhourlogsmall') {
3940 // Format not sensitive to language
3941 $format = '%y%m%d%H%M';
3942 } elseif ($format == 'dayhourldap') {
3943 $format = '%Y%m%d%H%M%SZ';
3944 } elseif ($format == 'dayhourxcard') {
3945 $format = '%Y%m%dT%H%M%SZ';
3946 } elseif ($format == 'dayxcard') {
3947 $format = '%Y%m%d';
3948 } elseif ($format == 'dayrfc') {
3949 $format = '%Y-%m-%d'; // DATE_RFC3339
3950 } elseif ($format == 'dayhourrfc') {
3951 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3952 } elseif ($format == 'standard') {
3953 $format = '%Y-%m-%d %H:%M:%S';
3954 }
3955
3956 if ($reduceformat) {
3957 $format = str_replace('%Y', '%y', $format);
3958 $format = str_replace('yyyy', 'yy', $format);
3959 }
3960
3961 // Clean format
3962 if (preg_match('/%b/i', $format)) { // There is some text to translate
3963 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3964 $format = str_replace('%b', '__b__', $format);
3965 $format = str_replace('%B', '__B__', $format);
3966 }
3967 if (preg_match('/%a/i', $format)) { // There is some text to translate
3968 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3969 $format = str_replace('%a', '__a__', $format);
3970 $format = str_replace('%A', '__A__', $format);
3971 }
3972
3973 // Analyze date
3974 $reg = array();
3975 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
3976 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString());
3977 return '';
3978 } 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
3979 // This part of code should not be used anymore.
3980 dol_syslog("Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString(), LOG_WARNING);
3981 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3982 $syear = (!empty($reg[1]) ? $reg[1] : '');
3983 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3984 $sday = (!empty($reg[3]) ? $reg[3] : '');
3985 $shour = (!empty($reg[4]) ? $reg[4] : '');
3986 $smin = (!empty($reg[5]) ? $reg[5] : '');
3987 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3988
3989 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3990
3991 if ($to_gmt) {
3992 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3993 } else {
3994 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3995 }
3996 $dtts = new DateTime();
3997 $dtts->setTimestamp($time);
3998 $dtts->setTimezone($tzo);
3999 $newformat = str_replace(
4000 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4001 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4002 $format
4003 );
4004 $ret = $dtts->format($newformat);
4005 $ret = str_replace(
4006 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4007 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4008 $ret
4009 );
4010 } else {
4011 // Date is a timestamps
4012 if ($time < 100000000000) { // Protection against bad date values
4013 $dtts = new DateTime();
4014 //var_dump($tzoutput.' '.$offsettzstring.' '.$offsettz.$offsetdst.' x '.$to_gmt);
4015 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
4016 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4017
4018 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4019 $dtts->setTimezone($tzo); // important: must be before the setTimestamp
4020 $dtts->setTimestamp($timetouse);
4021 } else {
4022 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4023
4024 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4025 $dtts->setTimestamp($timetouse); // TODO May be we can invert setTimestamp and setTimezone
4026 $dtts->setTimezone($tzo);
4027 }
4028
4029 $newformat = str_replace(
4030 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4031 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4032 $format
4033 );
4034
4035 $ret = $dtts->format($newformat);
4036 //var_dump($timetouse, $offsettz, $offsetdst, $tzo, $newformat, $ret);
4037 $ret = str_replace(
4038 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4039 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4040 $ret
4041 );
4042 } else {
4043 $ret = 'Bad value ' . $time . ' for date';
4044 }
4045 }
4046
4047 if (preg_match('/__b__/i', $format)) {
4048 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4049
4050 if ($to_gmt) {
4051 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4052 } else {
4053 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4054 }
4055 $dtts = new DateTime();
4056 $dtts->setTimestamp($timetouse);
4057 $dtts->setTimezone($tzo);
4058 $month = (int) $dtts->format("m");
4059 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
4060 if ($encodetooutput) {
4061 $monthtext = $outputlangs->transnoentities('Month' . $month);
4062 $monthtextshort = $outputlangs->transnoentities('MonthShort' . $month);
4063 } else {
4064 $monthtext = $outputlangs->transnoentitiesnoconv('Month' . $month);
4065 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort' . $month);
4066 }
4067 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
4068 $ret = str_replace('__b__', $monthtextshort, $ret);
4069 $ret = str_replace('__B__', $monthtext, $ret);
4070 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
4071 //return $ret;
4072 }
4073 if (preg_match('/__a__/i', $format)) {
4074 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
4075 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
4076
4077 if ($to_gmt) {
4078 $tzo = new DateTimeZone('UTC');
4079 } else {
4080 $tzo = new DateTimeZone(date_default_timezone_get());
4081 }
4082 $dtts = new DateTime();
4083 $dtts->setTimestamp($timetouse);
4084 $dtts->setTimezone($tzo);
4085 $w = $dtts->format("w");
4086 $dayweek = $outputlangs->transnoentitiesnoconv('Day' . $w);
4087
4088 $ret = str_replace('__A__', $dayweek, $ret);
4089 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
4090 }
4091
4092 if ($decorate) {
4093 $ret = preg_replace('/(\d\d:\d\d [AP]M)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4094 $ret = preg_replace('/(\d\d:\d\d)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4095 }
4096
4097 return $ret;
4098}
4099
4100
4121function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
4122{
4123 if ($timestamp === '') {
4124 return array();
4125 }
4126
4127 $datetimeobj = new DateTime();
4128 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
4129 if ($forcetimezone) {
4130 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
4131 }
4132 $arrayinfo = array(
4133 'year' => ((int) date_format($datetimeobj, 'Y')),
4134 'mon' => ((int) date_format($datetimeobj, 'm')),
4135 'mday' => ((int) date_format($datetimeobj, 'd')),
4136 'wday' => ((int) date_format($datetimeobj, 'w')),
4137 'yday' => ((int) date_format($datetimeobj, 'z')),
4138 'hours' => ((int) date_format($datetimeobj, 'H')),
4139 'minutes' => ((int) date_format($datetimeobj, 'i')),
4140 'seconds' => ((int) date_format($datetimeobj, 's')),
4141 '0' => $timestamp
4142 );
4143
4144 return $arrayinfo;
4145}
4146
4168function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
4169{
4170 global $conf;
4171 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
4172
4173 if ($gm === 'auto') {
4174 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
4175 }
4176 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
4177
4178 // Clean parameters
4179 if ($hour == -1 || empty($hour)) {
4180 $hour = 0;
4181 }
4182 if ($minute == -1 || empty($minute)) {
4183 $minute = 0;
4184 }
4185 if ($second == -1 || empty($second)) {
4186 $second = 0;
4187 }
4188
4189 // Check parameters
4190 if ($check) {
4191 if (!$month || !$day) {
4192 return '';
4193 }
4194 if ($day > 31) {
4195 return '';
4196 }
4197 if ($month > 12) {
4198 return '';
4199 }
4200 if ($hour < 0 || $hour > 24) {
4201 return '';
4202 }
4203 if ($minute < 0 || $minute > 60) {
4204 return '';
4205 }
4206 if ($second < 0 || $second > 60) {
4207 return '';
4208 }
4209 }
4210
4211 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
4212 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
4213 $localtz = new DateTimeZone($default_timezone);
4214 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
4215 // We use dol_tz_string first because it is more reliable.
4216 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
4217 try {
4218 $localtz = new DateTimeZone($default_timezone);
4219 } catch (Exception $e) {
4220 dol_syslog("Warning dol_tz_string contains an invalid value " . json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
4221 $default_timezone = @date_default_timezone_get();
4222 }
4223 } elseif (strrpos($gm, "tz,") !== false) {
4224 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
4225 try {
4226 $localtz = new DateTimeZone($timezone);
4227 } catch (Exception $e) {
4228 dol_syslog("Warning passed timezone contains an invalid value " . $timezone, LOG_WARNING);
4229 }
4230 }
4231
4232 if (empty($localtz)) {
4233 $localtz = new DateTimeZone('UTC');
4234 }
4235 $dt = new DateTime('now', $localtz);
4236 $dt->setDate((int) $year, (int) $month, (int) $day);
4237 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4238 $date = $dt->getTimestamp(); // should include daylight saving time
4239
4240 return $date;
4241}
4242
4243
4254function dol_now($mode = 'gmt')
4255{
4256 $ret = 0;
4257
4258 if ($mode === 'auto') {
4259 $mode = 'gmt';
4260 }
4261
4262 if ($mode == 'gmt') {
4263 $ret = time(); // Time for now at greenwich.
4264 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4265 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
4266 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4267 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4268 // } elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4269 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4270 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4271 // $ret=dol_now('gmt')+($tzsecond*3600);
4272 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4273 // Time for now with user timezone added
4274 // print 'time: '.time();
4275 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4276 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4277 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4278 }
4279
4280 return $ret;
4281}
4282
4283
4292function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4293{
4294 global $conf, $langs;
4295 $level = 1024;
4296
4297 if (!empty($conf->dol_optimize_smallscreen)) {
4298 $shortunit = 1;
4299 }
4300
4301 // Set value text
4302 if (empty($shortvalue) || $size < ($level * 10)) {
4303 $ret = $size;
4304 $textunitshort = $langs->trans("b");
4305 $textunitlong = $langs->trans("Bytes");
4306 } else {
4307 $ret = round($size / $level, 0);
4308 $textunitshort = $langs->trans("Kb");
4309 $textunitlong = $langs->trans("KiloBytes");
4310 }
4311 // Use long or short text unit
4312 if (empty($shortunit)) {
4313 $ret .= ' ' . $textunitlong;
4314 } else {
4315 $ret .= ' ' . $textunitshort;
4316 }
4317
4318 return $ret;
4319}
4320
4331function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4332{
4333 global $langs;
4334
4335 if (empty($url)) {
4336 return '';
4337 }
4338
4339 $linkstart = '<a href="';
4340 if (!preg_match('/^http/i', $url)) {
4341 $linkstart .= 'http://';
4342 }
4343 $linkstart .= $url;
4344 $linkstart .= '"';
4345 if ($target) {
4346 $linkstart .= ' target="' . $target . '"';
4347 }
4348 $linkstart .= ' title="' . $langs->trans("URL") . ': ' . $url . '"';
4349 $linkstart .= '>';
4350
4351 $link = '';
4352 if (!preg_match('/^http/i', $url)) {
4353 $link .= 'http://';
4354 }
4355 $link .= dol_trunc($url, $max);
4356
4357 $linkend = '</a>';
4358
4359 if ($morecss == 'float') { // deprecated
4360 return '<div class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '') . $link . '</div>';
4361 } else {
4362 return $linkstart . '<span class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '') . $link . '</span>' . $linkend;
4363 }
4364}
4365
4379function dol_print_email($email, $contactid = 0, $socid = 0, $addlink = 0, $max = 0, $showinvalid = 2, $withpicto = 0, $morecss = 'paddingrightonly')
4380{
4381 global $user, $langs, $hookmanager;
4382
4383 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4384 //$showinvalid = 1; $email = 'rrrrr';
4385
4386 $newemail = dol_escape_htmltag($email);
4387
4388 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4389 $withpicto = 0;
4390 }
4391
4392 if (empty($email)) {
4393 return '&nbsp;';
4394 }
4395
4396 if ($addlink == 1) {
4397 $newemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="';
4398 if (!preg_match('/^mailto:/i', $email)) {
4399 $newemail .= 'mailto:';
4400 }
4401 $newemail .= $email;
4402 $newemail .= '" target="_blank">';
4403
4404 $newemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4405
4406 if ($max > 0) {
4407 $newemail .= dol_trunc($email, $max);
4408 } else {
4409 $newemail .= $email;
4410 }
4411 $newemail .= '</a>';
4412
4413 if ($showinvalid) {
4414 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4415 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4416 $emailonly = CMailFile::getValidAddress($email, 2);
4417 if (!isValidEmail($emailonly)) {
4418 $langs->load("errors");
4419 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $emailonly), '', 'paddingrightonly');
4420 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4421 $langs->load("errors");
4422 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly), '', 'paddingrightonly');
4423 }
4424 }
4425
4426 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4427 $type = 'AC_EMAIL';
4428 $linktoaddaction = '';
4429 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4430 $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>';
4431 }
4432 if ($linktoaddaction) {
4433 $newemail = '<div>' . $newemail . ' ' . $linktoaddaction . '</div>';
4434 }
4435 }
4436 } elseif ($addlink === 'thirdparty') {
4437 $tmpnewemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="' . DOL_URL_ROOT . '/societe/card.php?socid=' . $socid . '&action=presend&mode=init#formmailbeforetitle">';
4438 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4439 if ($withpicto == 1) {
4440 $tmpnewemail .= $newemail;
4441 }
4442 $tmpnewemail .= '</a>';
4443
4444 $newemail = $tmpnewemail;
4445 } else {
4446 $newemail = ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '') . $newemail;
4447
4448 if ($showinvalid) {
4449 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4450 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4451 $emailonly = CMailFile::getValidAddress($email, 2);
4452 if (!isValidEmail($emailonly)) {
4453 $langs->load("errors");
4454 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $email));
4455 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4456 $langs->load("errors");
4457 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly));
4458 }
4459 }
4460 }
4461
4462 //$rep = '<div class="nospan" style="margin-right: 10px">';
4463 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4464 //$rep .= '</div>';
4465 $rep = $newemail;
4466
4467 if ($hookmanager) {
4468 $parameters = array('cid' => $contactid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4469
4470 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4471 if ($reshook > 0) {
4472 $rep = '';
4473 }
4474 $rep .= $hookmanager->resPrint;
4475 }
4476
4477 return $rep;
4478}
4479
4485function getArrayOfSocialNetworks()
4486{
4487 global $db;
4488
4489 $socialnetworks = array();
4490 // Enable caching of array
4491 require_once DOL_DOCUMENT_ROOT . '/core/lib/memory.lib.php';
4492 $cachekey = dol_sanitizeKeyCode(str_replace(',', '_', 'socialnetworks_'.getEntity('c_socialnetworks')));
4493 $dataretrieved = dol_getcache($cachekey);
4494
4495 if (!is_null($dataretrieved)) {
4496 $socialnetworks = $dataretrieved;
4497 } else {
4498 $sql = "SELECT rowid, code, label, url, icon, active FROM " . MAIN_DB_PREFIX . "c_socialnetworks";
4499 $sql .= " WHERE entity IN (" . getEntity('c_socialnetworks').")";
4500
4501 $resql = $db->query($sql);
4502 if ($resql) {
4503 while ($obj = $db->fetch_object($resql)) {
4504 $socialnetworks[$obj->code] = array(
4505 'rowid' => $obj->rowid,
4506 'label' => $obj->label,
4507 'url' => $obj->url,
4508 'icon' => $obj->icon,
4509 'active' => $obj->active,
4510 );
4511 }
4512 }
4513 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4514 }
4515
4516 return (is_array($socialnetworks) ? $socialnetworks : array());
4517}
4518
4529function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4530{
4531 global $hookmanager, $langs, $user;
4532
4533 $htmllink = $value;
4534
4535 if (empty($value)) {
4536 return '&nbsp;';
4537 }
4538
4539 if (!empty($type)) {
4540 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4541 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4542 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4543 if ($type == 'skype') {
4544 $htmllink .= dol_escape_htmltag($value);
4545 $htmllink .= '&nbsp; <a href="skype:';
4546 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4547 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4548 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4549 $htmllink .= '</a><a href="skype:';
4550 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4551 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4552 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4553 $htmllink .= '</a>';
4554 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4555 $addlink = 'AC_SKYPE';
4556 $link = '';
4557 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4558 $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>';
4559 }
4560 $htmllink .= ($link ? ' ' . $link : '');
4561 }
4562 } else {
4563 if (!empty($dictsocialnetworks[$type]['url'])) {
4564 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4565 if ($tmpvirginurl) {
4566 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4567 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4568
4569 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4570 if ($tmpvirginurl3) {
4571 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4572 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4573 }
4574
4575 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4576 if ($tmpvirginurl2) {
4577 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4578 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4579 }
4580 }
4581 if (preg_match('/^https?:\/\//i', $value)) {
4582 $link = $value;
4583 } else {
4584 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4585 }
4586 $valuetoshow = $value;
4587 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4588 if (preg_match('/^https?:\/\//i', $link)) {
4589 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4590 } else {
4591 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4592 }
4593 } else {
4594 $htmllink .= dol_escape_htmltag($value);
4595 }
4596 }
4597 $htmllink .= '</div>';
4598 } else {
4599 $langs->load("errors");
4600 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4601 }
4602
4603 if ($hookmanager) {
4604 $parameters = array(
4605 'value' => $value,
4606 'cid' => $contactid,
4607 'socid' => $socid,
4608 'type' => $type,
4609 'dictsocialnetworks' => $dictsocialnetworks,
4610 );
4611
4612 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4613 if ($reshook > 0) {
4614 $htmllink = '';
4615 }
4616 $htmllink .= $hookmanager->resPrint;
4617 }
4618
4619 return $htmllink;
4620}
4621
4631function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4632{
4633 global $mysoc;
4634
4635 if (empty($profID) || empty($profIDtype)) {
4636 return '';
4637 }
4638 if (empty($countrycode)) {
4639 $countrycode = $mysoc->country_code;
4640 }
4641 $newProfID = $profID;
4642 $id = substr($profIDtype, -1);
4643 $ret = '';
4644 if (strtoupper($countrycode) == 'FR') {
4645 // France
4646 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4647
4648 if ($id == 1 && dol_strlen($newProfID) == 9) {
4649 // SIREN (ex: 123 123 123)
4650 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3);
4651 }
4652 if ($id == 2 && dol_strlen($newProfID) == 14) {
4653 // SIRET (ex: 123 123 123 12345)
4654 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3) . ' ' . substr($newProfID, 9, 5);
4655 }
4656 if ($id == 3 && dol_strlen($newProfID) == 5) {
4657 // NAF/APE (ex: 69.20Z)
4658 $newProfID = substr($newProfID, 0, 2) . '.' . substr($newProfID, 2, 3);
4659 }
4660 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4661 // TVA intracommunautaire (ex: FR12 123 123 123)
4662 $newProfID = substr($newProfID, 0, 4) . ' ' . substr($newProfID, 4, 3) . ' ' . substr($newProfID, 7, 3) . ' ' . substr($newProfID, 10, 3);
4663 }
4664 }
4665 if (!empty($addcpButton)) {
4666 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4667 } else {
4668 $ret = $newProfID;
4669 }
4670 return $ret;
4671}
4672
4688function dol_print_phone($phone, $countrycode = '', $contactid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4689{
4690 global $conf, $user, $langs, $mysoc, $hookmanager;
4691
4692 // Clean phone parameter
4693 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4694 if (empty($phone)) {
4695 return '';
4696 }
4697 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4698 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4699 }
4700 if (empty($countrycode) && is_object($mysoc)) {
4701 $countrycode = $mysoc->country_code;
4702 }
4703
4704 // Short format for small screens
4705 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4706 $separ = '';
4707 }
4708
4709 $newphone = $phone;
4710 $newphonewa = $phone;
4711 if (strtoupper($countrycode) == "FR") {
4712 // France
4713 if (dol_strlen($phone) == 10) {
4714 $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);
4715 } elseif (dol_strlen($phone) == 7) {
4716 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2);
4717 } elseif (dol_strlen($phone) == 9) {
4718 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2);
4719 } elseif (dol_strlen($phone) == 11) {
4720 $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);
4721 } elseif (dol_strlen($phone) == 12) {
4722 $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);
4723 } elseif (dol_strlen($phone) == 13) {
4724 $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);
4725 }
4726 } elseif (strtoupper($countrycode) == "CA") {
4727 if (dol_strlen($phone) == 10) {
4728 $newphone = ($separ != '' ? '(' : '') . substr($newphone, 0, 3) . ($separ != '' ? ')' : '') . $separ . substr($newphone, 3, 3) . ($separ != '' ? '-' : '') . substr($newphone, 6, 4);
4729 }
4730 } elseif (strtoupper($countrycode) == "PT") { //Portugal
4731 if (dol_strlen($phone) == 13) { //ex: +351_ABC_DEF_GHI
4732 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4733 }
4734 } elseif (strtoupper($countrycode) == "SR") { //Suriname
4735 if (dol_strlen($phone) == 10) { //ex: +597_ABC_DEF
4736 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3);
4737 } elseif (dol_strlen($phone) == 11) { //ex: +597_ABC_DEFG
4738 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
4739 }
4740 } elseif (strtoupper($countrycode) == "DE") { //Allemagne
4741 if (dol_strlen($phone) == 14) { //ex: +49_ABCD_EFGH_IJK
4742 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 4) . $separ . substr($newphone, 11, 3);
4743 } elseif (dol_strlen($phone) == 13) { //ex: +49_ABC_DEFG_HIJ
4744 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 4) . $separ . substr($newphone, 10, 3);
4745 }
4746 } elseif (strtoupper($countrycode) == "ES") { //Espagne
4747 if (dol_strlen($phone) == 12) { //ex: +34_ABC_DEF_GHI
4748 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4749 }
4750 } elseif (strtoupper($countrycode) == "BF") { // Burkina Faso
4751 if (dol_strlen($phone) == 12) { //ex : +22 A BC_DE_FG_HI
4752 $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);
4753 }
4754 } elseif (strtoupper($countrycode) == "RO") { // Roumanie
4755 if (dol_strlen($phone) == 12) { //ex : +40 AB_CDE_FG_HI
4756 $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);
4757 }
4758 } elseif (strtoupper($countrycode) == "TR") { //Turquie
4759 if (dol_strlen($phone) == 13) { //ex : +90 ABC_DEF_GHIJ
4760 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4761 }
4762 } elseif (strtoupper($countrycode) == "US") { //Etat-Unis
4763 if (dol_strlen($phone) == 12) { //ex: +1 ABC_DEF_GHIJ
4764 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4765 }
4766 } elseif (strtoupper($countrycode) == "MX") { //Mexique
4767 if (dol_strlen($phone) == 12) { //ex: +52 ABCD_EFG_HI
4768 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4769 } elseif (dol_strlen($phone) == 11) { //ex: +52 AB_CD_EF_GH
4770 $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);
4771 } elseif (dol_strlen($phone) == 13) { //ex: +52 ABC_DEF_GHIJ
4772 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4773 }
4774 } elseif (strtoupper($countrycode) == "ML") { //Mali
4775 if (dol_strlen($phone) == 12) { //ex: +223 AB_CD_EF_GH
4776 $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);
4777 }
4778 } elseif (strtoupper($countrycode) == "TH") { //Thaïlande
4779 if (dol_strlen($phone) == 11) { //ex: +66_ABC_DE_FGH
4780 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4781 } elseif (dol_strlen($phone) == 12) { //ex: +66_A_BCD_EF_GHI
4782 $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);
4783 }
4784 } elseif (strtoupper($countrycode) == "MU") {
4785 //Maurice
4786 if (dol_strlen($phone) == 11) { //ex: +230_ABC_DE_FG
4787 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4788 } elseif (dol_strlen($phone) == 12) { //ex: +230_ABCD_EF_GH
4789 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4790 }
4791 } elseif (strtoupper($countrycode) == "ZA") { //Afrique du sud
4792 if (dol_strlen($phone) == 12) { //ex: +27_AB_CDE_FG_HI
4793 $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);
4794 }
4795 } elseif (strtoupper($countrycode) == "SY") { //Syrie
4796 if (dol_strlen($phone) == 12) { //ex: +963_AB_CD_EF_GH
4797 $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);
4798 } elseif (dol_strlen($phone) == 13) { //ex: +963_AB_CD_EF_GHI
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, 3);
4800 }
4801 } elseif (strtoupper($countrycode) == "AE") { //Emirats Arabes Unis
4802 if (dol_strlen($phone) == 12) { //ex: +971_ABC_DEF_GH
4803 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4804 } elseif (dol_strlen($phone) == 13) { //ex: +971_ABC_DEF_GHI
4805 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4806 } elseif (dol_strlen($phone) == 14) { //ex: +971_ABC_DEF_GHIK
4807 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 4);
4808 }
4809 } elseif (strtoupper($countrycode) == "DZ") { //Algérie
4810 if (dol_strlen($phone) == 13) { //ex: +213_ABC_DEF_GHI
4811 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4812 }
4813 } elseif (strtoupper($countrycode) == "BE") { //Belgique
4814 if (dol_strlen($phone) == 11) { //ex: +32_ABC_DE_FGH
4815 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4816 } elseif (dol_strlen($phone) == 12) { //ex: +32_ABC_DEF_GHI
4817 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4818 }
4819 } elseif (strtoupper($countrycode) == "PF") { //Polynésie française
4820 if (dol_strlen($phone) == 12) { //ex: +689_AB_CD_EF_GH
4821 $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);
4822 }
4823 } elseif (strtoupper($countrycode) == "CO") { //Colombie
4824 if (dol_strlen($phone) == 13) { //ex: +57_ABC_DEF_GH_IJ
4825 $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);
4826 }
4827 } elseif (strtoupper($countrycode) == "JO") { //Jordanie
4828 if (dol_strlen($phone) == 12) { //ex: +962_A_BCD_EF_GH
4829 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 1) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4830 }
4831 } elseif (strtoupper($countrycode) == "JM") { //Jamaïque
4832 if (dol_strlen($newphone) == 12) { //ex: +1867_ABC_DEFG
4833 $newphone = substr($newphone, 0, 5) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4834 }
4835 } elseif (strtoupper($countrycode) == "MG") { //Madagascar
4836 if (dol_strlen($phone) == 13) { //ex: +261_AB_CD_EFG_HI
4837 $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);
4838 }
4839 } elseif (strtoupper($countrycode) == "GB") { //Royaume uni
4840 if (dol_strlen($phone) == 13) { //ex: +44_ABCD_EFG_HIJ
4841 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4842 }
4843 } elseif (strtoupper($countrycode) == "CH") { //Suisse
4844 if (dol_strlen($phone) == 12) { //ex: +41_AB_CDE_FG_HI
4845 $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);
4846 } elseif (dol_strlen($phone) == 15) { // +41_AB_CDE_FGH_IJKL
4847 $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);
4848 }
4849 } elseif (strtoupper($countrycode) == "TN") { //Tunisie
4850 if (dol_strlen($phone) == 12) { //ex: +216_AB_CDE_FGH
4851 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4852 }
4853 } elseif (strtoupper($countrycode) == "GF") { //Guyane francaise
4854 if (dol_strlen($phone) == 13) { //ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4855 $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);
4856 }
4857 } elseif (strtoupper($countrycode) == "GP") { //Guadeloupe
4858 if (dol_strlen($phone) == 13) { //ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4859 $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);
4860 }
4861 } elseif (strtoupper($countrycode) == "MQ") { //Martinique
4862 if (dol_strlen($phone) == 13) { //ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4863 $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);
4864 }
4865 } elseif (strtoupper($countrycode) == "IT") { //Italie
4866 if (dol_strlen($phone) == 12) { //ex: +39_ABC_DEF_GHI
4867 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4868 } elseif (dol_strlen($phone) == 13) { //ex: +39_ABC_DEF_GH_IJ
4869 $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);
4870 }
4871 } elseif (strtoupper($countrycode) == "AU") {
4872 //Australie
4873 if (dol_strlen($phone) == 12) {
4874 //ex: +61_A_BCDE_FGHI
4875 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 4);
4876 }
4877 } elseif (strtoupper($countrycode) == "LU") {
4878 // Luxembourg
4879 if (dol_strlen($phone) == 10) { // fix 6 digits +352_AA_BB_CC
4880 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2);
4881 } elseif (dol_strlen($phone) == 11) { // fix 7 digits +352_AA_BB_CC_D
4882 $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);
4883 } elseif (dol_strlen($phone) == 12) { // fix 8 digits +352_AA_BB_CC_DD
4884 $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);
4885 } elseif (dol_strlen($phone) == 13) { // mobile +352_AAA_BB_CC_DD
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) == "PE") {
4889 // Peru
4890 if (dol_strlen($phone) == 7) { // fix 7 chiffres without code AAA_BBBB
4891 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4);
4892 } elseif (dol_strlen($phone) == 9) { // mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4893 $newphonewa = '+51' . $newphone;
4894 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3);
4895 } elseif (dol_strlen($phone) == 11) { // fix 11 chiffres +511_AAA_BBBB
4896 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
4897 } elseif (dol_strlen($phone) == 12) { // mobile +51_AAA_BBB_CCC
4898 $newphonewa = $newphone;
4899 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4900 }
4901 } elseif (strtoupper($countrycode) == "IN") { //India
4902 if (dol_strlen($phone) == 13) {
4903 if ($withpicto == 'phone') { //ex: +91_AB_CDEF_GHIJ
4904 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 4) . $separ . substr($newphone, 9, 4);
4905 } else { //ex: +91_ABCDE_FGHIJ
4906 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 5) . $separ . substr($newphone, 8, 5);
4907 }
4908 }
4909 }
4910
4911 $newphoneastart = $newphoneaend = '';
4912 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4913 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
4914 $newphoneastart = '<a href="tel:' . urlencode($phone) . '">';
4915 $newphoneaend .= '</a>';
4916 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4917 if (empty($user->clicktodial_loaded)) {
4918 $user->fetch_clicktodial();
4919 }
4920
4921 // Define urlmask
4922 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4923 if (!empty($user->clicktodial_url)) {
4924 $urlmask = $user->clicktodial_url;
4925 }
4926
4927 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4928 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4929 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4930 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4931 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4932 // Those lines are for substitution
4933 $substitarray = array(
4934 '__PHONEFROM__' => $clicktodial_poste,
4935 '__PHONETO__' => urlencode($phone),
4936 '__LOGIN__' => $clicktodial_login,
4937 '__PASS__' => $clicktodial_password
4938 );
4939 $url = make_substitutions($url, $substitarray);
4940 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4941 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4942 $newphoneastart = '<a href="' . $url . '" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4943 $newphoneaend = '</a>';
4944 } else {
4945 // Old method
4946 $newphoneastart = '<a href="' . $url . '"';
4947 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4948 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4949 }
4950 $newphoneastart .= '>';
4951 $newphoneaend .= '</a>';
4952 }
4953 }
4954
4955 //if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4956 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4957 $type = 'AC_TEL';
4958 $addlinktoagenda = '';
4959 if ($addlink == 'AC_FAX') {
4960 $type = 'AC_FAX';
4961 }
4962 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4963 $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>';
4964 }
4965 if ($addlinktoagenda) {
4966 $newphone = '<span>' . $newphone . ' ' . $addlinktoagenda . '</span>';
4967 }
4968 }
4969 }
4970
4971 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4972 // Link to Whatsapp
4973 $newphone .= ' <a href="https://wa.me/' . $newphonewa . '" target="_blank"'; // Use api to whatasapp contacts
4974 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4975 }
4976
4977 if (empty($titlealt)) {
4978 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4979 }
4980 $rep = '';
4981
4982 if ($hookmanager) {
4983 $parameters = array('countrycode' => $countrycode, 'cid' => $contactid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4984 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4985 $rep .= $hookmanager->resPrint;
4986 }
4987 if (empty($reshook)) {
4988 $picto = '';
4989 if ($withpicto) {
4990 if ($withpicto == 'fax') {
4991 $picto = 'phoning_fax';
4992 } elseif ($withpicto == 'phone') {
4993 $picto = 'phone';
4994 } elseif ($withpicto == 'mobile') {
4995 $picto = 'phoning_mobile';
4996 } else {
4997 $picto = '';
4998 }
4999 }
5000 if ($adddivfloat == 1) {
5001 $rep .= '<div class="nospan float' . ($morecss ? ' ' . $morecss : '') . '">';
5002 } elseif (empty($adddivfloat)) {
5003 $rep .= '<span' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
5004 }
5005
5006 $rep .= $newphoneastart;
5007 $rep .= ($withpicto ? img_picto($titlealt, $picto) : '');
5008 if ($separ != 'hidenum') {
5009 $rep .= ($withpicto ? ' ' : '') . $newphone;
5010 }
5011 $rep .= $newphoneaend;
5012
5013 if ($adddivfloat == 1) {
5014 $rep .= '</div>';
5015 } elseif (empty($adddivfloat)) {
5016 $rep .= '</span>';
5017 }
5018 }
5019
5020 return $rep;
5021}
5022
5031function dol_print_ip($ip, $mode = 0, $showname = 0)
5032{
5033 global $conf;
5034
5035 $ret = '';
5036 if (!isset($conf->cache['resolveips'])) {
5037 $conf->cache['resolveips'] = array();
5038 }
5039
5040 if ($mode != 2) {
5041 $countrycode = dolGetCountryCodeFromIp($ip);
5042 if ($countrycode) { // If success, countrycode is us, fr, ...
5043 if (file_exists(DOL_DOCUMENT_ROOT . '/theme/common/flags/' . $countrycode . '.png')) {
5044 $ret .= picto_from_langcode($countrycode);
5045 } else {
5046 $ret .= '(' . $countrycode . ')';
5047 }
5048 $ret .= '&nbsp;';
5049 } else {
5050 // Nothing
5051 }
5052 }
5053
5054 if (in_array($mode, [0, 2])) {
5055 $domain = '';
5056 if ($showname) {
5057 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
5058 $domain = gethostbyaddr($ip);
5059 $conf->cache['resolveips'][$ip] = $domain; // false or domain
5060 } else {
5061 $domain = $conf->cache['resolveips'][$ip];
5062 }
5063 }
5064 if ($domain) {
5065 $ret .= $domain;
5066 } else {
5067 $ret .= $ip;
5068 }
5069 }
5070
5071 return $ret;
5072}
5073
5086function getUserRemoteIP($trusted = 0)
5087{
5088 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
5089 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
5090 // 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)
5091 // This can happen if the proxy were added in the list of trusted proxy.
5092 return $ip;
5093 }
5094
5095 // Try to guess the real IP of client (but this may not be reliable)
5096 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
5097 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
5098 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
5099 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
5100 } else {
5101 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
5102 }
5103 } else {
5104 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
5105 }
5106 } else {
5107 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
5108 }
5109 return $ip;
5110}
5111
5120function isHTTPS()
5121{
5122 $isSecure = false;
5123 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
5124 $isSecure = true;
5125 } 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') {
5126 $isSecure = true;
5127 }
5128 return $isSecure;
5129}
5130
5137function dolGetCountryCodeFromIp($ip)
5138{
5139 $countrycode = '';
5140
5141 if (isModEnabled('geoipmaxmind')) {
5142 if (getDolGlobalString('GEOIP_VERSION') == 'php') {
5143 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5144 } else {
5145 $diroffile = getMultidirOutput(null, 'geoipmaxmind');
5146 $datafile = $diroffile . '/' . getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE_EMBEDDED');
5147 }
5148 //$ip='24.24.24.24';
5149 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
5150 if ($datafile) {
5151 try {
5152 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5153 $geoip = new DolGeoIP('country', $datafile);
5154 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
5155 $countrycode = $geoip->getCountryCodeFromIP($ip);
5156 } catch (Exception $e) {
5157 //print 'Error with GeoIP database: '.$e->getMessage();
5158 }
5159 }
5160 }
5161
5162 return $countrycode;
5163}
5164
5165
5172function dol_user_country()
5173{
5174 global $conf, $langs, $user;
5175
5176 //$ret=$user->xxx;
5177 $ret = '';
5178 if (isModEnabled('geoipmaxmind')) {
5179 $ip = getUserRemoteIP();
5180 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5181 //$ip='24.24.24.24';
5182 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
5183 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5184 $geoip = new DolGeoIP('country', $datafile);
5185 $countrycode = $geoip->getCountryCodeFromIP($ip);
5186 $ret = $countrycode;
5187 }
5188 return $ret;
5189}
5190
5203function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
5204{
5205 global $hookmanager;
5206
5207 $out = '';
5208
5209 if ($address) {
5210 if ($hookmanager) {
5211 $parameters = array('element' => $element, 'id' => $id);
5212 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
5213 $out .= $hookmanager->resPrint;
5214 }
5215 if (empty($reshook)) {
5216 if (empty($charfornl)) {
5217 $out .= nl2br((string) $address);
5218 } else {
5219 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
5220 }
5221
5222 // TODO Remove this block, we can add this using the hook now
5223 $showgmap = $showomap = 0;
5224 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
5225 $showgmap = 1;
5226 }
5227 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
5228 $showgmap = 1;
5229 }
5230 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
5231 $showgmap = 1;
5232 }
5233 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
5234 $showgmap = 1;
5235 }
5236 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
5237 $showomap = 1;
5238 }
5239 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
5240 $showomap = 1;
5241 }
5242 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
5243 $showomap = 1;
5244 }
5245 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
5246 $showomap = 1;
5247 }
5248 if ($showgmap) {
5249 $url = dol_buildpath('/google/gmaps.php?mode=' . $element . '&id=' . $id, 1);
5250 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5251 }
5252 if ($showomap) {
5253 $url = dol_buildpath('/openstreetmap/maps.php?mode=' . $element . '&id=' . $id, 1);
5254 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '_openstreetmap" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5255 }
5256 }
5257 }
5258 if ($noprint) {
5259 return $out;
5260 } else {
5261 print $out;
5262 return null;
5263 }
5264}
5265
5266
5276function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5277{
5278 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5279 return true;
5280 }
5281 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5282 return true;
5283 }
5284 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5285 return true;
5286 }
5287
5288 return false;
5289}
5290
5300function isValidMXRecord($domain)
5301{
5302 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5303 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5304 return 0;
5305 }
5306 if (function_exists('getmxrr')) {
5307 $mxhosts = array();
5308 $weight = array();
5309 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5310 if (count($mxhosts) > 1) {
5311 return 1;
5312 }
5313 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5314 return 1;
5315 }
5316
5317 return 0;
5318 }
5319 }
5320
5321 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5322 return -1;
5323}
5324
5332function isValidPhone($phone)
5333{
5334 return true;
5335}
5336
5337
5347function dolGetFirstLetters($s, $nbofchar = 1)
5348{
5349 $ret = '';
5350 $tmparray = explode(' ', $s);
5351 foreach ($tmparray as $tmps) {
5352 $ret .= dol_substr($tmps, 0, $nbofchar);
5353 }
5354
5355 return $ret;
5356}
5357
5358
5366function dol_strlen($string, $stringencoding = 'UTF-8')
5367{
5368 if (is_null($string)) {
5369 return 0;
5370 }
5371
5372 if (function_exists('mb_strlen')) {
5373 return mb_strlen($string, $stringencoding);
5374 } else {
5375 return strlen($string);
5376 }
5377}
5378
5389function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5390{
5391 global $langs;
5392
5393 if (empty($stringencoding)) {
5394 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5395 }
5396
5397 $ret = '';
5398 if (empty($trunconbytes)) {
5399 if (function_exists('mb_substr')) {
5400 $ret = mb_substr($string, $start, $length, $stringencoding);
5401 } else {
5402 $ret = substr($string, $start, $length);
5403 }
5404 } else {
5405 if (function_exists('mb_strcut')) {
5406 $ret = mb_strcut($string, $start, $length, $stringencoding);
5407 } else {
5408 $ret = substr($string, $start, $length);
5409 }
5410 }
5411 return $ret;
5412}
5413
5414
5428function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5429{
5430 global $conf;
5431
5432 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5433 return $string;
5434 }
5435
5436 if (empty($stringencoding)) {
5437 $stringencoding = 'UTF-8';
5438 }
5439 // reduce for small screen
5440 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5441 $size = round($size / 3);
5442 }
5443
5444 // We go always here
5445 if ($trunc == 'right') {
5446 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5447 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5448 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5449 return dol_substr($newstring, 0, $size, $stringencoding) . ($nodot ? '' : '…');
5450 } else {
5451 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5452 return $string;
5453 }
5454 } elseif ($trunc == 'middle') {
5455 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5456 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5457 $size1 = (int) round($size / 2);
5458 $size2 = (int) round($size / 2);
5459 return dol_substr($newstring, 0, $size1, $stringencoding) . '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5460 } else {
5461 return $string;
5462 }
5463 } elseif ($trunc == 'left') {
5464 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5465 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5466 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5467 return '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5468 } else {
5469 return $string;
5470 }
5471 } elseif ($trunc == 'wrap') {
5472 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5473 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5474 return dol_substr($newstring, 0, $size, $stringencoding) . "\n" . dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5475 } else {
5476 return $string;
5477 }
5478 } else {
5479 return 'BadParam3CallingDolTrunc';
5480 }
5481}
5482
5490function getPictoForType($key, $morecss = '')
5491{
5492 // Set array with type -> picto
5493 $type2picto = array(
5494 'varchar' => 'font',
5495 'text' => 'font',
5496 'html' => 'code',
5497 'int' => 'sort-numeric-down',
5498 'double' => 'sort-numeric-down',
5499 'price' => 'currency',
5500 'pricecy' => 'multicurrency',
5501 'password' => 'key',
5502 'boolean' => 'check-square',
5503 'date' => 'calendar',
5504 'datetime' => 'calendar',
5505 'duration' => 'hourglass',
5506 'phone' => 'phone',
5507 'mail' => 'email',
5508 'url' => 'url',
5509 'ip' => 'country',
5510 'select' => 'list',
5511 'sellist' => 'list',
5512 'stars' => 'fontawesome_star_fas',
5513 'radio' => 'check-circle',
5514 'checkbox' => 'list',
5515 'chkbxlst' => 'list',
5516 'link' => 'link',
5517 'icon' => "question",
5518 'point' => "country",
5519 'multipts' => 'country',
5520 'linestrg' => "country",
5521 'polygon' => "country",
5522 'separate' => 'minus'
5523 );
5524
5525 if (!empty($type2picto[$key])) {
5526 return img_picto('', $type2picto[$key], 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5527 }
5528
5529 return img_picto('', 'generic', 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5530}
5531
5532
5556function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5557{
5558 global $conf;
5559
5560 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5561 $url = DOL_URL_ROOT;
5562 $theme = isset($conf->theme) ? $conf->theme : null;
5563 $path = 'theme/' . $theme;
5564 if (empty($picto)) {
5565 $picto = 'generic';
5566 }
5567
5568 if ($titlealt) {
5569 $titlealt = str_replace(array('\n', "\n"), ' - ', $titlealt);
5570 }
5571
5572 // Define fullpathpicto to use into src
5573 if ($pictoisfullpath) {
5574 // Clean parameters
5575 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5576 $picto .= '.png';
5577 }
5578 $fullpathpicto = $picto;
5579 $reg = array();
5580 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5581 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5582 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5583 }
5584 } else {
5585 // $picto can not be null since replaced with 'generic' in that case
5586 // $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5587 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5588 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5589 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5590
5591 // Fix some values of $pictowithouttext
5592 $pictoconvertkey = array(
5593 'facture' => 'bill',
5594 'shipping' => 'shipment',
5595 'fichinter' => 'intervention',
5596 'agenda' => 'calendar',
5597 'invoice_supplier' => 'supplier_invoice',
5598 'order_supplier' => 'supplier_order');
5599 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5600 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5601 }
5602
5603 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5604 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5605 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5606 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5607
5608 // Compatibility with old fontawesome versions
5609 if ($pictowithouttext == 'file-o') {
5610 $pictowithouttext = 'file';
5611 }
5612
5613 $pictowithouttextarray = explode('_', $pictowithouttext);
5614 $marginleftonlyshort = 0;
5615
5616 if (!empty($pictowithouttextarray[1])) {
5617 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5618 $fakey = 'fa-' . $pictowithouttextarray[0];
5619 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5620 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5621 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5622 } else {
5623 $fakey = 'fa-' . $pictowithouttext;
5624 $faprefix = 'fas';
5625 $facolor = '';
5626 $fasize = '';
5627 }
5628
5629 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5630 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5631 $morestyle = '';
5632 $reg = array();
5633 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5634 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5635 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5636 }
5637 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5638 $morestyle = $reg[1];
5639 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5640 }
5641 $moreatt = trim($moreatt);
5642
5643 $enabledisablehtml = '<span class="' . $faprefix . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5644 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5645 $enabledisablehtml .= '</span>';
5646
5647 return $enabledisablehtml;
5648 }
5649
5650 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
5651 $fakey = $pictowithouttext;
5652 $facolor = '';
5653 $fasize = '';
5654 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5655 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'))) {
5656 $fa = 'far';
5657 }
5658 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'))) {
5659 $fa = 'fab';
5660 }
5661
5662 $arrayconvpictotofa = getImgPictoConv('fa');
5663
5664 if ($pictowithouttext == 'off') {
5665 $fakey = 'fa-square';
5666 $fasize = '1.3em';
5667 } elseif ($pictowithouttext == 'on') {
5668 $fakey = 'fa-check-square';
5669 $fasize = '1.3em';
5670 } elseif ($pictowithouttext == 'listlight') {
5671 $fakey = 'fa-download';
5672 $marginleftonlyshort = 1;
5673 } elseif ($pictowithouttext == 'printer') {
5674 $fakey = 'fa-print';
5675 $fasize = '1.2em';
5676 } elseif ($pictowithouttext == 'note') {
5677 $fakey = 'fa-sticky-note';
5678 $marginleftonlyshort = 1;
5679 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5680 $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');
5681 $fakey = 'fa-' . $convertarray[$pictowithouttext];
5682 if (preg_match('/selected/', $pictowithouttext)) {
5683 $facolor = '#888';
5684 }
5685 $marginleftonlyshort = 1;
5686 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5687 $fakey = 'fa-' . $arrayconvpictotofa[$pictowithouttext];
5688 } else {
5689 $fakey = 'fa-' . $pictowithouttext;
5690 }
5691
5692 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5693 $morecss .= ' em092';
5694 }
5695 if (in_array($pictowithouttext, array('conferenceorbooth', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5696 $morecss .= ' em088';
5697 }
5698 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5699 $morecss .= ' em080';
5700 }
5701
5702 // Define $marginleftonlyshort
5703 $arrayconvpictotomarginleftonly = array(
5704 'bank',
5705 'check',
5706 'delete',
5707 'generic',
5708 'grip',
5709 'grip_title',
5710 'jabber',
5711 'grip_title',
5712 'grip',
5713 'listlight',
5714 'note',
5715 'on',
5716 'off',
5717 'playdisabled',
5718 'printer',
5719 'resize',
5720 'sign-out',
5721 'stats',
5722 'switch_on',
5723 'switch_on_grey',
5724 'switch_on_red',
5725 'switch_off',
5726 'switch_off_grey',
5727 'switch_off_red',
5728 'uparrow',
5729 '1uparrow',
5730 '1downarrow',
5731 '1leftarrow',
5732 '1rightarrow',
5733 '1uparrow_selected',
5734 '1downarrow_selected',
5735 '1leftarrow_selected',
5736 '1rightarrow_selected'
5737 );
5738 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5739 $marginleftonlyshort = 0;
5740 }
5741
5742 // Add CSS
5743 $arrayconvpictotomorcess = array(
5744 'action' => 'infobox-action',
5745 'account' => 'infobox-bank_account',
5746 'accounting_account' => 'infobox-bank_account',
5747 'accountline' => 'infobox-bank_account',
5748 'accountancy' => 'infobox-bank_account',
5749 'admin' => 'opacitymedium',
5750 'asset' => 'infobox-bank_account',
5751 'bank_account' => 'infobox-bank_account',
5752 'bill' => 'infobox-commande',
5753 'billa' => 'infobox-commande',
5754 'billr' => 'infobox-commande',
5755 'billd' => 'infobox-commande',
5756 'bookcal' => 'infobox-portal',
5757 'margin' => 'infobox-bank_account',
5758 'conferenceorbooth' => 'infobox-project',
5759 'cash-register' => 'infobox-portal',
5760 'contract' => 'infobox-contrat',
5761 'check' => 'font-status4',
5762 'conversation' => 'infobox-contrat',
5763 'donation' => 'infobox-commande',
5764 'dolly' => 'infobox-commande',
5765 'dollyrevert' => 'flip infobox-order_supplier',
5766 'ecm' => 'infobox-action',
5767 'eventorganization' => 'infobox-project',
5768 'hrm' => 'infobox-adherent',
5769 'group' => 'infobox-adherent',
5770 'intervention' => 'infobox-contrat',
5771 'incoterm' => 'infobox-supplier_proposal',
5772 'intracommreport' => 'infobox-bank_account',
5773 'currency' => 'infobox-bank_account',
5774 'multicurrency' => 'infobox-bank_account',
5775 'members' => 'infobox-adherent',
5776 'member' => 'infobox-adherent',
5777 'money-bill-alt' => 'infobox-bank_account',
5778 'order' => 'infobox-commande',
5779 'user' => 'infobox-adherent',
5780 'users' => 'infobox-adherent',
5781 'error' => 'pictoerror',
5782 'warning' => 'pictowarning',
5783 'switch_on' => 'font-status4',
5784 'switch_on_warning' => 'font-status4 warning',
5785 'switch_on_red' => 'font-status8',
5786 'switch_off_warning' => 'font-status4 warning',
5787 'switch_off_red' => 'font-status8',
5788 'holiday' => 'infobox-holiday',
5789 'info' => 'opacityhigh',
5790 'info_black' => 'purple',
5791 'invoice' => 'infobox-commande',
5792 'knowledgemanagement' => 'infobox-contrat rotate90',
5793 'loan' => 'infobox-commande',
5794 'payment' => 'infobox-bank_account',
5795 'payment_vat' => 'infobox-bank_account',
5796 'poll' => 'infobox-portal',
5797 'pos' => 'infobox-bank_account',
5798 'project' => 'infobox-project',
5799 'projecttask' => 'infobox-project',
5800 'propal' => 'infobox-propal',
5801 'proposal' => 'infobox-propal',
5802 'private' => 'infobox-project',
5803 'reception' => 'flip infobox-order_supplier',
5804 'recruitmentjobposition' => 'infobox-adherent',
5805 'recruitmentcandidature' => 'infobox-adherent',
5806 'resource' => 'infobox-action',
5807 'salary' => 'infobox-commande',
5808 'shapes' => 'infobox-adherent',
5809 'shipment' => 'infobox-commande',
5810 'store' => 'infobox-portal',
5811 'stripe' => 'infobox-bank_account',
5812 'supplier_invoice' => 'infobox-order_supplier',
5813 'supplier_invoicea' => 'infobox-order_supplier',
5814 'supplier_invoiced' => 'infobox-order_supplier',
5815 'supplier' => 'infobox-order_supplier',
5816 'supplier_order' => 'infobox-order_supplier',
5817 'supplier_proposal' => 'infobox-supplier_proposal',
5818 'ticket' => 'infobox-contrat',
5819 'title_accountancy' => 'infobox-bank_account',
5820 'title_hrm' => 'infobox-holiday',
5821 'expensereport' => 'infobox-expensereport',
5822 'trip' => 'infobox-expensereport',
5823 'title_agenda' => 'infobox-action',
5824 'vat' => 'infobox-bank_account',
5825 'webportal' => 'infobox-portal',
5826 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5827 'list-alt' => 'imgforviewmode',
5828 'calendar' => 'imgforviewmode',
5829 'calendarweek' => 'imgforviewmode',
5830 'calendarmonth' => 'imgforviewmode',
5831 'calendarday' => 'imgforviewmode',
5832 'calendarperuser' => 'imgforviewmode',
5833 'calendarpertype' => 'imgforviewmode'
5834 );
5835 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5836 $morecss .= ($morecss ? ' ' : '') . $arrayconvpictotomorcess[$pictowithouttext];
5837 }
5838
5839 // Define $color
5840 $arrayconvpictotocolor = array(
5841 'address' => '#6c6aa8',
5842 'building' => '#6c6aa8',
5843 'bom' => '#a69944',
5844 'clone' => '#999',
5845 'cog' => '#999',
5846 'companies' => '#6c6aa8',
5847 'company' => '#6c6aa8',
5848 'contact' => '#6c6aa8',
5849 'cron' => '#555',
5850 'dynamicprice' => '#a69944',
5851 'edit' => '#444',
5852 'note' => '#999',
5853 'error' => '',
5854 'help' => '#bbb',
5855 'listlight' => '#999',
5856 'language' => '#555',
5857 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5858 'lock' => '#ddd',
5859 'lot' => '#a69944',
5860 'map-marker-alt' => '#aaa',
5861 'mrp' => '#a69944',
5862 'product' => '#a69944',
5863 'service' => '#a69944',
5864 'inventory' => '#a69944',
5865 'stock' => '#a69944',
5866 'movement' => '#a69944',
5867 'other' => '#ddd',
5868 'world' => '#986c6a',
5869 'partnership' => '#6c6aa8',
5870 'playdisabled' => '#ccc',
5871 'printer' => '#444',
5872 'projectpub' => '#986c6a',
5873 'resize' => '#444',
5874 'rss' => '#cba',
5875 //'shipment'=>'#a69944',
5876 'search-plus' => '#808080',
5877 'security' => '#999',
5878 'square' => '#888',
5879 'stop-circle' => '#888',
5880 'stats' => '#444',
5881 'superadmin' => '#600',
5882 'switch_off' => '#999',
5883 'technic' => '#999',
5884 'tick' => '#282',
5885 'timespent' => '#555',
5886 'uncheck' => '#800',
5887 'uparrow' => '#555',
5888 'user-cog' => '#999',
5889 'country' => '#aaa',
5890 'globe-americas' => '#aaa',
5891 'region' => '#aaa',
5892 'state' => '#aaa',
5893 'website' => '#304',
5894 'workstation' => '#a69944'
5895 );
5896 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5897 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5898 }
5899
5900 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5901 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5902 $morestyle = '';
5903 $reg = array();
5904 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5905 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5906 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5907 }
5908 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5909 $morestyle = $reg[1];
5910 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5911 }
5912 $moreatt = trim($moreatt);
5913
5914 $enabledisablehtml = '<span class="' . $fa . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5915 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5916 $enabledisablehtml .= '</span>';
5917
5918 return $enabledisablehtml;
5919 }
5920
5921 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5922 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/' . $theme; // If the theme does not have the same name as the module
5923 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5924 $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
5925 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5926 $path = $theme . '/theme/' . $theme; // If the theme have the same name as the module
5927 }
5928
5929 // If we ask an image into $url/$mymodule/img (instead of default path)
5930 $regs = array();
5931 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5932 $picto = $regs[1];
5933 $path = $regs[2]; // $path is $mymodule
5934 }
5935
5936 // Clean parameters
5937 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5938 $picto .= '.png';
5939 }
5940 // If alt path are defined, define url where img file is, according to physical path
5941 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5942 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5943 if ($type == 'main') {
5944 continue;
5945 }
5946 // This consumes a lot of time, that's why enabling alternative dir like "custom" dir should be avoid
5947 if (file_exists($dirroot . '/' . $path . '/img/' . $picto) && !empty($conf->file->dol_url_root)) {
5948 $url = DOL_URL_ROOT . $conf->file->dol_url_root[$type];
5949 break;
5950 }
5951 }
5952
5953 // $url is '' or '/custom', $path is current theme or
5954 $fullpathpicto = $url . '/' . $path . '/img/' . $picto;
5955 }
5956
5957 if ($srconly) {
5958 return $fullpathpicto;
5959 }
5960
5961 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5962 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
5963}
5964
5972function getImgPictoConv($mode = 'fa')
5973{
5974 global $conf;
5975
5976 if (empty($mode) || $mode == 'fa') {
5977 // Array when the fa picto key is different than the Dolibarr picto key.
5978 $arrayconvpictotofa = array(
5979 'account' => 'university',
5980 'accounting_account' => 'clipboard-list',
5981 'accountline' => 'receipt',
5982 'accountancy' => 'search-dollar',
5983 'action' => 'calendar-alt',
5984 'add' => 'plus-circle',
5985 'address' => 'address-book',
5986 'ai' => 'magic',
5987 'admin' => 'star',
5988 'asset' => 'money-check-alt',
5989 'autofill' => 'fill',
5990 'back' => 'arrow-left',
5991 'bank_account' => 'university',
5992 'bill' => 'file-invoice-dollar',
5993 'billa' => 'file-excel',
5994 'billr' => 'file-invoice-dollar',
5995 'billd' => 'file-medical',
5996 'blockedlog' => 'file-archive',
5997 'bookcal' => 'calendar-check',
5998 'supplier_invoice' => 'file-invoice-dollar',
5999 'supplier_invoicea' => 'file-excel',
6000 'supplier_invoicer' => 'file-invoice-dollar',
6001 'supplier_invoiced' => 'file-medical',
6002 'bom' => 'shapes',
6003 'card' => 'address-card',
6004 'chart' => 'chart-line',
6005 'company' => 'building',
6006 'contact' => 'address-book',
6007 'contract' => 'suitcase',
6008 'collab' => 'people-arrows',
6009 'conversation' => 'comments',
6010 'country' => 'globe-americas',
6011 'cron' => 'business-time',
6012 'cross' => 'times',
6013 'chevron-double-left' => 'angle-double-left',
6014 'chevron-double-right' => 'angle-double-right',
6015 'chevron-double-down' => 'angle-double-down',
6016 'chevron-double-top' => 'angle-double-up',
6017 'donation' => 'gift',
6018 'dynamicprice' => 'hand-holding-usd',
6019 'setup' => 'cog',
6020 'companies' => 'building',
6021 'products' => 'cube',
6022 'commercial' => 'suitcase',
6023 'invoicing' => 'coins',
6024 'accounting' => 'search-dollar',
6025 'category' => 'tag',
6026 'dollyrevert' => 'dolly',
6027 'file-o' => 'file',
6028 'generate' => 'plus-square',
6029 'hrm' => 'user-tie',
6030 'incoterm' => 'truck-loading',
6031 'margin' => 'calculator',
6032 'members' => 'user-friends',
6033 'ticket' => 'ticket-alt',
6034 'globe' => 'external-link-alt',
6035 'lot' => 'barcode',
6036 'email' => 'at',
6037 'establishment' => 'building',
6038 'edit' => 'pencil-alt',
6039 'entity' => 'globe',
6040 'graph' => 'chart-line',
6041 'grip_title' => 'arrows-alt',
6042 'grip' => 'arrows-alt',
6043 'help' => 'question-circle',
6044 'generic' => 'file',
6045 'holiday' => 'umbrella-beach',
6046 'info' => 'info-circle',
6047 'info_black' => 'info-circle',
6048 'inventory' => 'boxes',
6049 'intracommreport' => 'globe-europe',
6050 'jobprofile' => 'cogs',
6051 'knowledgemanagement' => 'ticket-alt',
6052 'label' => 'layer-group',
6053 'layout' => 'columns',
6054 'line' => 'bars',
6055 'loan' => 'money-bill-alt',
6056 'member' => 'user-alt',
6057 'meeting' => 'chalkboard-teacher',
6058 'mrp' => 'cubes',
6059 'next' => 'arrow-alt-circle-right',
6060 'trip' => 'wallet',
6061 'expensereport' => 'wallet',
6062 'group' => 'users',
6063 'movement' => 'people-carry',
6064 'sign-out' => 'sign-out-alt',
6065 'superadmin' => 'star',
6066 'switch_off' => 'toggle-off',
6067 'switch_off_grey' => 'toggle-off',
6068 'switch_off_warning' => 'toggle-off',
6069 'switch_off_red' => 'toggle-off',
6070 'switch_on' => 'toggle-on',
6071 'switch_on_grey' => 'toggle-on',
6072 'switch_on_warning' => 'toggle-on',
6073 'switch_on_red' => 'toggle-on',
6074 'check' => 'check',
6075 'bookmark' => 'star',
6076 'bank' => 'university',
6077 'close_title' => 'times',
6078 'delete' => 'trash',
6079 'filter' => 'filter',
6080 'list-alt' => 'list-alt',
6081 'calendarlist' => 'bars',
6082 'calendar' => 'calendar-alt',
6083 'calendarmonth' => 'calendar-alt',
6084 'calendarweek' => 'calendar-week',
6085 'calendarday' => 'calendar-day',
6086 'calendarperuser' => 'table',
6087 'calendarpertype' => 'table',
6088 'intervention' => 'ambulance',
6089 'invoice' => 'file-invoice-dollar',
6090 'order' => 'file-invoice',
6091 'error' => 'exclamation-triangle',
6092 'warning' => 'exclamation-triangle',
6093 'other' => 'square',
6094 'playdisabled' => 'play',
6095 'pdf' => 'file-pdf',
6096 'poll' => 'check-double',
6097 'pos' => 'cash-register',
6098 'preview' => 'binoculars',
6099 'project' => 'project-diagram',
6100 'projectpub' => 'project-diagram',
6101 'projecttask' => 'tasks',
6102 'propal' => 'file-signature',
6103 'proposal' => 'file-signature',
6104 'partnership' => 'handshake',
6105 'payment' => 'money-check-alt',
6106 'payment_vat' => 'money-check-alt',
6107 'pictoconfirm' => 'check-square',
6108 'phoning' => 'phone',
6109 'phoning_mobile' => 'mobile-alt',
6110 'phoning_fax' => 'fax',
6111 'previous' => 'arrow-alt-circle-left',
6112 'printer' => 'print',
6113 'product' => 'cube',
6114 'puce' => 'angle-right',
6115 'recent' => 'check-square',
6116 'reception' => 'dolly',
6117 'recruitmentjobposition' => 'id-card-alt',
6118 'recruitmentcandidature' => 'id-badge',
6119 'resize' => 'crop',
6120 'supplier_order' => 'dol-order_supplier',
6121 'supplier_proposal' => 'file-signature',
6122 'refresh' => 'redo',
6123 'region' => 'map-marked',
6124 'replacement' => 'exchange-alt',
6125 'resource' => 'laptop-house',
6126 'recurring' => 'history',
6127 'service' => 'concierge-bell',
6128 'skill' => 'shapes',
6129 'state' => 'map-marked-alt',
6130 'security' => 'key',
6131 'salary' => 'wallet',
6132 'shipment' => 'dolly',
6133 'stock' => 'box-open',
6134 'stats' => 'chart-bar',
6135 'split' => 'code-branch',
6136 'status' => 'stop-circle',
6137 'stripe' => 'stripe-s',
6138 'supplier' => 'building',
6139 'technic' => 'cogs',
6140 'tick' => 'check',
6141 'timespent' => 'clock',
6142 'title_setup' => 'tools',
6143 'title_accountancy' => 'money-check-alt',
6144 'title_bank' => 'university',
6145 'title_hrm' => 'umbrella-beach',
6146 'title_agenda' => 'calendar-alt',
6147 'uncheck' => 'times',
6148 'uparrow' => 'share',
6149 'url' => 'external-link-alt',
6150 'vat' => 'money-check-alt',
6151 'vcard' => 'arrow-alt-circle-down',
6152 'jabber' => 'comment',
6153 'website' => 'globe-americas',
6154 'workstation' => 'pallet',
6155 'webhook' => 'bullseye',
6156 'world' => 'globe',
6157 'private' => 'user-lock',
6158 'conferenceorbooth' => 'chalkboard-teacher',
6159 'eventorganization' => 'project-diagram',
6160 'webportal' => 'door-open'
6161 );
6162
6163 if ($conf->currency == 'EUR') {
6164 $arrayconvpictotofa['currency'] = 'euro-sign';
6165 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
6166 } else {
6167 $arrayconvpictotofa['currency'] = 'dollar-sign';
6168 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
6169 }
6170 } else {
6171 $arrayconvpictotofa = array();
6172 }
6173
6174 return $arrayconvpictotofa;
6175}
6176
6177
6192function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
6193{
6194 if (strpos($picto, '^') === 0) {
6195 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6196 } else {
6197 return img_picto($titlealt, 'object_' . $picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6198 }
6199}
6200
6212function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
6213{
6214 global $conf;
6215
6216 if (is_numeric($picto)) {
6217 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
6218 //$picto = $leveltopicto[$picto];
6219 return '<i class="fa fa-weather-level' . $picto . '"></i>';
6220 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6221 $picto .= '.png';
6222 }
6223
6224 $path = DOL_URL_ROOT . '/theme/' . $conf->theme . '/img/weather/' . $picto;
6225
6226 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
6227}
6228
6240function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
6241{
6242 global $conf;
6243
6244 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6245 $picto .= '.png';
6246 }
6247
6248 if ($pictoisfullpath) {
6249 $path = $picto;
6250 } else {
6251 $path = DOL_URL_ROOT . '/theme/common/' . $picto;
6252
6253 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
6254 $themepath = DOL_DOCUMENT_ROOT . '/theme/' . $conf->theme . '/img/' . $picto;
6255
6256 if (file_exists($themepath)) {
6257 $path = $themepath;
6258 }
6259 }
6260 }
6261
6262 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
6263}
6264
6278function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
6279{
6280 global $langs;
6281
6282 if (empty($titlealt) || $titlealt == 'default') {
6283 if ($numaction == '-1' || $numaction == 'ST_NO') {
6284 $numaction = -1;
6285 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
6286 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
6287 $numaction = 0;
6288 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
6289 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
6290 $numaction = 1;
6291 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
6292 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
6293 $numaction = 2;
6294 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
6295 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
6296 $numaction = 3;
6297 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
6298 } else {
6299 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus ' . $numaction);
6300 $numaction = 0;
6301 }
6302 }
6303 if (!is_numeric($numaction)) {
6304 $numaction = 0;
6305 }
6306
6307 return img_picto($titlealt, (empty($picto) ? 'stcomm' . $numaction . '.png' : $picto), $moreatt);
6308}
6309
6317function img_edit_add($titlealt = 'default', $other = '')
6318{
6319 global $langs;
6320
6321 if ($titlealt == 'default') {
6322 $titlealt = $langs->trans('Add');
6323 }
6324
6325 return img_picto($titlealt, 'edit_add.png', $other);
6326}
6334function img_edit_remove($titlealt = 'default', $other = '')
6335{
6336 global $langs;
6337
6338 if ($titlealt == 'default') {
6339 $titlealt = $langs->trans('Remove');
6340 }
6341
6342 return img_picto($titlealt, 'edit_remove.png', $other);
6343}
6344
6353function img_edit($titlealt = 'default', $float = 0, $other = '')
6354{
6355 global $langs;
6356
6357 if ($titlealt == 'default') {
6358 $titlealt = $langs->trans('Modify');
6359 }
6360
6361 return img_picto($titlealt, 'edit', ($float ? 'style="float: ' . ($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right') . '"' : "") . ($other ? ' ' . $other : ''));
6362}
6363
6372function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
6373{
6374 global $langs;
6375
6376 if ($titlealt == 'default') {
6377 $titlealt = $langs->trans('View');
6378 }
6379
6380 $moreatt = ($float ? 'style="float: right" ' : '') . $other;
6381
6382 return img_picto($titlealt, 'eye', $moreatt);
6383}
6384
6393function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
6394{
6395 global $langs;
6396
6397 if ($titlealt == 'default') {
6398 $titlealt = $langs->trans('Delete');
6399 }
6400
6401 return img_picto($titlealt, 'delete', $other, 0, 0, 0, '', $morecss);
6402}
6403
6411function img_printer($titlealt = "default", $other = '')
6412{
6413 global $langs;
6414 if ($titlealt == "default") {
6415 $titlealt = $langs->trans("Print");
6416 }
6417 return img_picto($titlealt, 'printer', $other);
6418}
6419
6427function img_split($titlealt = 'default', $other = 'class="pictosplit"')
6428{
6429 global $langs;
6430
6431 if ($titlealt == 'default') {
6432 $titlealt = $langs->trans('Split');
6433 }
6434
6435 return img_picto($titlealt, 'split', $other);
6436}
6437
6445function img_help($usehelpcursor = 1, $usealttitle = 1)
6446{
6447 global $langs;
6448
6449 if ($usealttitle) {
6450 if (is_string($usealttitle)) {
6451 $usealttitle = dol_escape_htmltag($usealttitle);
6452 } else {
6453 $usealttitle = $langs->trans('Info');
6454 }
6455 }
6456
6457 return img_picto($usealttitle, 'info', 'style="vertical-align: middle;' . ($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')) . '"');
6458}
6459
6466function img_info($titlealt = 'default')
6467{
6468 global $langs;
6469
6470 if ($titlealt == 'default') {
6471 $titlealt = $langs->trans('Informations');
6472 }
6473
6474 return img_picto($titlealt, 'info', 'style="vertical-align: middle;"');
6475}
6476
6485function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6486{
6487 global $langs;
6488
6489 if ($titlealt == 'default') {
6490 $titlealt = $langs->trans('Warning');
6491 }
6492
6493 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6494 return img_picto($titlealt, 'warning', 'class="' . $morecss . '"' . ($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' ' . $moreatt) : ''));
6495}
6496
6503function img_error($titlealt = 'default')
6504{
6505 global $langs;
6506
6507 if ($titlealt == 'default') {
6508 $titlealt = $langs->trans('Error');
6509 }
6510
6511 return img_picto($titlealt, 'error');
6512}
6513
6521function img_next($titlealt = 'default', $moreatt = '')
6522{
6523 global $langs;
6524
6525 if ($titlealt == 'default') {
6526 $titlealt = $langs->trans('Next');
6527 }
6528
6529 //return img_picto($titlealt, 'next.png', $moreatt);
6530 return '<span class="fa fa-chevron-right paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6531}
6532
6540function img_previous($titlealt = 'default', $moreatt = '')
6541{
6542 global $langs;
6543
6544 if ($titlealt == 'default') {
6545 $titlealt = $langs->trans('Previous');
6546 }
6547
6548 //return img_picto($titlealt, 'previous.png', $moreatt);
6549 return '<span class="fa fa-chevron-left paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6550}
6551
6560function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6561{
6562 global $langs;
6563
6564 if ($titlealt == 'default') {
6565 $titlealt = $langs->trans('Down');
6566 }
6567
6568 return img_picto($titlealt, ($selected ? '1downarrow_selected' : '1downarrow'), 'class="imgdown' . ($moreclass ? " " . $moreclass : "") . '"');
6569}
6570
6579function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6580{
6581 global $langs;
6582
6583 if ($titlealt == 'default') {
6584 $titlealt = $langs->trans('Up');
6585 }
6586
6587 return img_picto($titlealt, ($selected ? '1uparrow_selected' : '1uparrow'), 'class="imgup' . ($moreclass ? " " . $moreclass : "") . '"');
6588}
6589
6598function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6599{
6600 global $langs;
6601
6602 if ($titlealt == 'default') {
6603 $titlealt = $langs->trans('Left');
6604 }
6605
6606 return img_picto($titlealt, ($selected ? '1leftarrow_selected' : '1leftarrow'), $moreatt);
6607}
6608
6617function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6618{
6619 global $langs;
6620
6621 if ($titlealt == 'default') {
6622 $titlealt = $langs->trans('Right');
6623 }
6624
6625 return img_picto($titlealt, ($selected ? '1rightarrow_selected' : '1rightarrow'), $moreatt);
6626}
6627
6635function img_allow($allow, $titlealt = 'default')
6636{
6637 global $langs;
6638
6639 if ($titlealt == 'default') {
6640 $titlealt = $langs->trans('Active');
6641 }
6642
6643 if ($allow == 1) {
6644 return img_picto($titlealt, 'tick');
6645 }
6646
6647 return '-';
6648}
6649
6657function img_credit_card($brand, $morecss = 'fa-2x inline-block valignmiddle')
6658{
6659 if (is_null($morecss)) {
6660 $morecss = 'fa-2x';
6661 }
6662
6663 if ($brand == 'visa' || $brand == 'Visa') {
6664 $brand = 'cc-visa';
6665 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6666 $brand = 'cc-mastercard';
6667 } elseif ($brand == 'amex' || $brand == 'American Express') {
6668 $brand = 'cc-amex';
6669 } elseif ($brand == 'discover' || $brand == 'Discover') {
6670 $brand = 'cc-discover';
6671 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6672 $brand = 'cc-jcb';
6673 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6674 $brand = 'cc-diners-club';
6675 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6676 $brand = 'credit-card';
6677 }
6678
6679 return '<span class="fa fa-' . $brand . ' fa-fw' . ($morecss ? ' ' . $morecss : '') . '"></span>';
6680}
6681
6690function img_mime($file, $titlealt = '', $morecss = '')
6691{
6692 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
6693
6694 $mimetype = dol_mimetype($file, '', 1);
6695 //$mimeimg = dol_mimetype($file, '', 2);
6696 $mimefa = dol_mimetype($file, '', 4);
6697
6698 if (empty($titlealt)) {
6699 $titlealt = 'Mime type: ' . $mimetype;
6700 }
6701
6702 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6703 return '<i class="fa fa-' . $mimefa . ' ' . (preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ') . ($morecss ? ' ' . $morecss : '') . '"' . ($titlealt ? ' title="' . dolPrintHTMLForAttribute($titlealt) . '"' : '') . '></i>';
6704}
6705
6706
6714function img_search($titlealt = 'default', $other = '')
6715{
6716 global $langs;
6717
6718 if ($titlealt == 'default') {
6719 $titlealt = $langs->trans('Search');
6720 }
6721
6722 $img = img_picto($titlealt, 'search', $other, 0, 1);
6723
6724 $input = '<input type="image" class="liste_titre" name="button_search" src="' . $img . '" ';
6725 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6726
6727 return $input;
6728}
6729
6737function img_searchclear($titlealt = 'default', $other = '')
6738{
6739 global $langs;
6740
6741 if ($titlealt == 'default') {
6742 $titlealt = $langs->trans('Search');
6743 }
6744
6745 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6746
6747 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="' . $img . '" ';
6748 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6749
6750 return $input;
6751}
6752
6765function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6766{
6767 global $conf, $langs;
6768
6769 if ($infoonimgalt) {
6770 $result = img_picto($text, 'info', 'class="' . ($morecss ? ' ' . $morecss : '') . '"');
6771 } else {
6772 if (empty($conf->use_javascript_ajax)) {
6773 $textfordropdown = '';
6774 }
6775
6776 $class = (empty($admin) ? 'undefined' : ((string) $admin == '1' ? 'info' : $admin));
6777 $fa = 'info-circle';
6778 if ($picto == 'warning') {
6779 $fa = 'exclamation-triangle';
6780 }
6781 $result = ($nodiv ? '' : '<div class="wordbreak ' . $class . ($morecss ? ' ' . $morecss : '') . ($textfordropdown ? ' hidden' : '') . '">');
6782 $result .= img_picto((string) $admin ? $langs->trans('InfoAdmin') : $langs->trans('Note'), $fa);
6783 $result .= ' ';
6784 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6785 $result .= ($nodiv ? '' : '</div>');
6786
6787 if ($textfordropdown) {
6788 $tmpresult = '<span class="' . $class . 'text opacitymedium cursorpointer">' . $langs->trans($textfordropdown) . ' ' . img_picto($langs->trans($textfordropdown), '1downarrow') . '</span>';
6789 $tmpresult .= '<script nonce="' . getNonce() . '" type="text/javascript">
6790 jQuery(document).ready(function() {
6791 jQuery(".' . $class . 'text").click(function() {
6792 console.log("toggle text");
6793 jQuery(".' . $class . '").toggle();
6794 });
6795 });
6796 </script>';
6797
6798 $result = $tmpresult . $result;
6799 }
6800 }
6801
6802 return $result;
6803}
6804
6805
6817function dol_print_error($db = null, $error = '', $errors = null)
6818{
6819 global $conf, $langs, $user, $argv;
6820 global $dolibarr_main_prod;
6821
6822 $out = '';
6823 $syslog = '';
6824
6825 // If error occurs before the $lang object was loaded
6826 if (!$langs) {
6827 require_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
6828 $langs = new Translate('', $conf);
6829 $langs->load("main");
6830 }
6831
6832 // Load translation files required by the error messages
6833 $langs->loadLangs(array('main', 'errors'));
6834
6835 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6836 $out .= $langs->trans("DolibarrHasDetectedError") . ".<br>\n";
6837 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6838 $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";
6839 }
6840 $out .= $langs->trans("InformationToHelpDiagnose") . ":<br>\n";
6841
6842 $out .= "<b>" . $langs->trans("Date") . ":</b> " . dol_print_date(time(), 'dayhourlog') . "<br>\n";
6843 $out .= "<b>" . $langs->trans("Dolibarr") . ":</b> " . DOL_VERSION . " - https://www.dolibarr.org<br>\n";
6844 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6845 $out .= "<b>" . $langs->trans("LevelOfFeature") . ":</b> " . getDolGlobalInt('MAIN_FEATURES_LEVEL') . "<br>\n";
6846 }
6847 if ($user instanceof User) {
6848 $out .= "<b>" . $langs->trans("Login") . ":</b> " . $user->login . "<br>\n";
6849 }
6850 if (function_exists("phpversion")) {
6851 $out .= "<b>" . $langs->trans("PHP") . ":</b> " . phpversion() . "<br>\n";
6852 }
6853 $out .= "<b>" . $langs->trans("Server") . ":</b> " . (isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '') . "<br>\n";
6854 if (function_exists("php_uname")) {
6855 $out .= "<b>" . $langs->trans("OS") . ":</b> " . php_uname() . "<br>\n";
6856 }
6857 $out .= "<b>" . $langs->trans("UserAgent") . ":</b> " . (isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '') . "<br>\n";
6858 $out .= "<br>\n";
6859 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6860 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6861 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6862 $out .= "<br>\n";
6863 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6864 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6865 } else { // Mode CLI
6866 $out .= '> ' . $langs->transnoentities("ErrorInternalErrorDetected") . ":\n" . $argv[0] . "\n";
6867 $syslog .= "pid=" . dol_getmypid();
6868 }
6869
6870 if (!empty($conf->modules)) {
6871 $out .= "<b>" . $langs->trans("Modules") . ":</b> " . implode(', ', $conf->modules) . "<br>\n";
6872 }
6873
6874 if (is_object($db)) {
6875 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6876 $out .= "<b>" . $langs->trans("DatabaseTypeManager") . ":</b> " . $db->type . "<br>\n";
6877 $lastqueryerror = $db->lastqueryerror();
6878 if (!utf8_check($lastqueryerror)) {
6879 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6880 }
6881 $out .= "<b>" . $langs->trans("RequestLastAccessInError") . ":</b> " . ($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6882 $out .= "<b>" . $langs->trans("ReturnCodeLastAccessInError") . ":</b> " . ($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6883 $out .= "<b>" . $langs->trans("InformationLastAccessInError") . ":</b> " . ($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
6884 $out .= "<br>\n";
6885 } else { // Mode CLI
6886 // No dol_escape_htmltag for output, we are in CLI mode
6887 $out .= '> ' . $langs->transnoentities("DatabaseTypeManager") . ":\n" . $db->type . "\n";
6888 $out .= '> ' . $langs->transnoentities("RequestLastAccessInError") . ":\n" . ($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6889 $out .= '> ' . $langs->transnoentities("ReturnCodeLastAccessInError") . ":\n" . ($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6890 $out .= '> ' . $langs->transnoentities("InformationLastAccessInError") . ":\n" . ($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
6891 }
6892 $syslog .= ", sql=" . $db->lastquery();
6893 $syslog .= ", db_error=" . $db->lasterror();
6894 }
6895
6896 if ($error || $errors) {
6897 // Merge all into $errors array
6898 if (is_array($error) && is_array($errors)) {
6899 $errors = array_merge($error, $errors);
6900 } elseif (is_array($error)) { // deprecated, use second parameters
6901 $errors = $error;
6902 } elseif (is_array($errors) && !empty($error)) {
6903 $errors = array_merge(array($error), $errors);
6904 } elseif (!empty($error)) {
6905 $errors = array_merge(array($error), array($errors));
6906 }
6907
6908 $langs->load("errors");
6909
6910 foreach ($errors as $msg) {
6911 if (empty($msg)) {
6912 continue;
6913 }
6914 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6915 $out .= "<b>" . $langs->trans("Message") . ":</b> " . dol_escape_htmltag($msg) . "<br>\n";
6916 } else { // Mode CLI
6917 $out .= '> ' . $langs->transnoentities("Message") . ":\n" . $msg . "\n";
6918 }
6919 $syslog .= ", msg=" . $msg;
6920 }
6921 }
6922 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6923 xdebug_print_function_stack();
6924 $out .= '<b>XDebug information:</b>' . "<br>\n";
6925 $out .= 'File: ' . xdebug_call_file() . "<br>\n";
6926 $out .= 'Line: ' . xdebug_call_line() . "<br>\n";
6927 $out .= 'Function: ' . xdebug_call_function() . "<br>\n";
6928 $out .= "<br>\n";
6929 }
6930
6931 // Return a http header with error code if possible
6932 if (!headers_sent()) {
6933 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6934 top_httphead();
6935 }
6936 //http_response_code(500); // If we use 500, message is not output with some command line tools
6937 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
6938 }
6939
6940 if (empty($dolibarr_main_prod)) {
6941 print $out;
6942 } else {
6943 if (empty($langs->defaultlang)) {
6944 $langs->setDefaultLang();
6945 }
6946 $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.
6947 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6948 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";
6949 print $langs->trans("DolibarrHasDetectedError") . '. ';
6950 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6951 if (!defined("MAIN_CORE_ERROR")) {
6952 define("MAIN_CORE_ERROR", 1);
6953 }
6954 }
6955
6956 dol_syslog("Error " . $syslog, LOG_ERR);
6957}
6958
6969function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6970{
6971 global $langs;
6972
6973 if (empty($email)) {
6974 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6975 }
6976
6977 $langs->load("errors");
6978 $now = dol_now();
6979
6980 print '<br><div class="center login_main_message"><div class="' . $morecss . '">';
6981 print $langs->trans("ErrorContactEMail", $email, $prefixcode . '-' . dol_print_date($now, '%Y%m%d%H%M%S'));
6982 if ($errormessage) {
6983 print '<br><br>' . $errormessage;
6984 }
6985 if (is_array($errormessages) && count($errormessages)) {
6986 foreach ($errormessages as $mesgtoshow) {
6987 print '<br><br>' . $mesgtoshow;
6988 }
6989 }
6990 print '</div></div>';
6991}
6992
7009function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
7010{
7011 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
7012}
7013
7032function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
7033{
7034 global $langs, $form;
7035 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
7036
7037 if ($moreattrib == 'class="right"') {
7038 $prefix .= 'right '; // For backward compatibility
7039 }
7040
7041 $tooltip = (string) $tooltip; // In case $tooltip is null
7042
7043 $sortorder = strtoupper((string) $sortorder);
7044 $out = '';
7045 $sortimg = '';
7046
7047 $tag = 'th';
7048 if ($thead == 2) {
7049 $tag = 'div';
7050 }
7051
7052 $tmpsortfield = explode(',', (string) $sortfield);
7053 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
7054 $tmpfield = explode(',', $field);
7055 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
7056
7057 if (strpos((string) $tooltip, ':') !== false) {
7058 $tmptooltip = explode(':', (string) $tooltip);
7059 } else {
7060 $tmptooltip = array($tooltip);
7061 }
7062
7063 $wrapcolumntitle = (empty($forcenowrapcolumntitle) || (!empty($tmptooltip[2]) && $tmptooltip[2] == '-1'));
7064
7065 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle) {
7066 $prefix = 'wrapcolumntitle ' . $prefix;
7067 }
7068
7069 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
7070 // If field is used as sort criteria we use a specific css class liste_titre_sel
7071 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
7072 $liste_titre = 'liste_titre';
7073 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
7074 $liste_titre = 'liste_titre_sel';
7075 }
7076
7077 $tagstart = '<' . $tag . ' class="' . $prefix . $liste_titre . '" ' . $moreattrib;
7078 //$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)).'"' : '');
7079 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle && !dol_textishtml($name)) ? ' title="' . dolPrintHTMLForAttribute($langs->trans($name)) . '"' : '';
7080 $tagstart .= '>';
7081
7082 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7083 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7084 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7085 $options = preg_replace('/&+/i', '&', $options);
7086 if (!preg_match('/^&/', $options)) {
7087 $options = '&' . $options;
7088 }
7089
7090 $sortordertouseinlink = '';
7091 if ($field1 != $sortfield1) { // We are on another field than current sorted field
7092 if (preg_match('/^DESC/i', $sortorder)) {
7093 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7094 } else { // We reverse the var $sortordertouseinlink
7095 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7096 }
7097 } else { // We are on field that is the first current sorting criteria
7098 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
7099 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7100 } else {
7101 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7102 }
7103 }
7104 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
7105 $out .= '<a class="reposition" href="' . dolBuildUrl($file, ['sortfield' => $field, 'sortorder' => $sortordertouseinlink, 'begin' => $begin]) . $options . '"';
7106 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
7107 $out .= '>';
7108 }
7109 if ($tooltip && $tmptooltip[0]) {
7110 // You can also use 'TranslationString:[keyfortooltiponclick]:[tooltipdirection]' for a tooltip on click or to change tooltip position.
7111 $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]));
7112 } else {
7113 $out .= $langs->trans((string) $name);
7114 }
7115
7116 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7117 $out .= '</a>';
7118 }
7119
7120 if (empty($thead) && $field) { // If this is a sort field
7121 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7122 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7123 $options = preg_replace('/&+/i', '&', $options);
7124 if (!preg_match('/^&/', $options)) {
7125 $options = '&' . $options;
7126 }
7127
7128 if (!$sortorder || ($field1 != $sortfield1)) {
7129 // Nothing
7130 } else {
7131 if (preg_match('/^DESC/', $sortorder)) {
7132 $sortimg .= '<span class="nowrap">' . img_up("Z-A", 0, 'paddingright') . '</span>';
7133 }
7134 if (preg_match('/^ASC/', $sortorder)) {
7135 $sortimg .= '<span class="nowrap">' . img_down("A-Z", 0, 'paddingright') . '</span>';
7136 }
7137 }
7138 }
7139
7140 $tagend = '</' . $tag . '>';
7141
7142 $out = $tagstart . $sortimg . $out . $tagend;
7143
7144 return $out;
7145}
7146
7155function print_titre($title)
7156{
7157 dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
7158
7159 print '<div class="titre">' . $title . '</div>';
7160}
7161
7173function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
7174{
7175 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
7176}
7177
7192function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '', $morecssonpicto = 'widthpictotitle')
7193{
7194 $return = '';
7195
7196 if ($picto == 'setup') {
7197 $picto = 'generic';
7198 }
7199
7200 $return .= "\n";
7201 $return .= '<table ' . ($id ? 'id="' . $id . '" ' : '') . 'class="centpercent notopnoleftnoright table-fiche-title' . ($morecssontable ? ' ' . $morecssontable : '') . '">'; // margin bottom must be same than into print_barre_list
7202 $return .= '<tr class="toptitle">';
7203 if ($picto) {
7204 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">' . img_picto('', $picto, 'class="valignmiddle pictotitle'.($morecssonpicto ? ' '.$morecssonpicto : '').'"', $pictoisfullpath) . '</td>';
7205 }
7206 $return .= '<td class="nobordernopadding valignmiddle col-title">';
7207 $return .= '<div class="titre inline-block">';
7208 $return .= '<span class="inline-block valignmiddle">' . $title . '</span>'; // $title is already HTML sanitized content
7209 $return .= '</div>';
7210 $return .= '</td>';
7211 if (dol_strlen($morehtmlcenter)) {
7212 $return .= '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7213 }
7214 if (dol_strlen($morehtmlright)) {
7215 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">' . $morehtmlright . '</td>';
7216 }
7217 $return .= '</tr></table>' . "\n";
7218
7219 return $return;
7220}
7221
7245function 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 = '')
7246{
7247 global $conf, $langs;
7248
7249 $savlimit = $limit;
7250 $savtotalnboflines = $totalnboflines;
7251 if (is_numeric($totalnboflines)) {
7252 $totalnboflines = abs($totalnboflines);
7253 }
7254
7255 // Detect if there is a subtitle
7256 $subtitle = '';
7257 $tmparray = preg_split('/<br>/i', $title, 2);
7258 if (!empty($tmparray[1])) {
7259 $title = $tmparray[0];
7260 $subtitle = $tmparray[1];
7261 }
7262
7263 $page = (int) $page;
7264
7265 if ($picto == 'setup') {
7266 $picto = 'title_setup';
7267 }
7268 if (($conf->browser->name == 'ie') && $picto == 'generic') {
7269 $picto = 'title.gif';
7270 }
7271 if ($limit < 0) {
7272 $limit = $conf->liste_limit;
7273 }
7274
7275 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
7276 $nextpage = 1;
7277 } else {
7278 $nextpage = 0;
7279 }
7280 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
7281
7282 print "\n";
7283 print "<!-- Begin print_barre_liste -->\n";
7284 print '<table class="centpercent notopnoleftnoright table-fiche-title' . ($morecss ? ' ' . $morecss : '') . '">';
7285 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
7286
7287 // Left
7288
7289 if ($picto && $title) {
7290 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
7291 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
7292 print '</td>';
7293 }
7294
7295 print '<td class="nobordernopadding valignmiddle col-title">';
7296 print '<div class="titre inline-block">';
7297 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()
7298 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
7299 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="' . $langs->trans("NbRecordQualified") . '">(' . $totalnboflines . ')</span>';
7300 }
7301 print '</div>';
7302 if (!empty($subtitle)) {
7303 print '<br><div class="subtitle inline-block hideonsmartphone">' . $subtitle . '</div>';
7304 }
7305 print '</td>';
7306
7307 // Center
7308 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
7309 print '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7310 }
7311
7312 // Right
7313 print '<td class="nobordernopadding valignmiddle right col-right">';
7314 print '<input type="hidden" name="pageplusoneold" value="' . ((int) $page + 1) . '">';
7315 $query = [];
7316 parse_str($options, $query);
7317 if ($sortfield) {
7318 $query += ['sortfield' => $sortfield];
7319 }
7320 if ($sortorder) {
7321 $query += ['sortorder' => $sortorder];
7322 }
7323
7324 $options = '&' . http_build_query($query);
7325 if ($page) {
7326 $query = array_merge($query, ['page' => $page]);
7327 }
7328 // Show navigation bar
7329 $pagelist = '';
7330 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
7331 if ($totalnboflines) { // If we know total nb of lines
7332 // Define nb of extra page links before and after selected page + ... + first or last
7333 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
7334
7335 if ($limit > 0) {
7336 $nbpages = ceil($totalnboflines / $limit);
7337 } else {
7338 $nbpages = 1;
7339 }
7340 $cpt = ($page - $maxnbofpage);
7341 if ($cpt < 0) {
7342 $cpt = 0;
7343 }
7344
7345 if ($cpt >= 1) {
7346 if (empty($pagenavastextinput)) {
7347 $query['page'] = 0;
7348 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">1</a></li>';
7349 if ($cpt > 2) {
7350 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7351 } elseif ($cpt == 2) {
7352 $query['page'] = 0;
7353 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">2</a></li>';
7354 }
7355 }
7356 }
7357
7358 do {
7359 if ($pagenavastextinput) {
7360 if ($cpt == $page) {
7361 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="' . ($totalnboflines > 100 ? 'width40' : 'width25') . ' center pageplusone heightofcombo" name="pageplusone" value="' . ($page + 1) . '"></li>';
7362 $pagelist .= '/';
7363 }
7364 } else {
7365 if ($cpt == $page) {
7366 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . '</span></li>';
7367 } else {
7368 $query['page'] = $cpt;
7369 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($cpt + 1) . '</a></li>';
7370 }
7371 }
7372 $cpt++;
7373 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
7374
7375 if (empty($pagenavastextinput)) {
7376 if ($cpt < $nbpages) {
7377 if ($cpt < $nbpages - 2) {
7378 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7379 } elseif ($cpt == $nbpages - 2) {
7380 $query['page'] = ($nbpages - 2);
7381 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($nbpages - 1) . '</a></li>';
7382 }
7383 $query['page'] = ($nbpages - 1);
7384 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7385 }
7386 } else {
7387 $query['page'] = ($nbpages - 1);
7388 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7389 }
7390 } else {
7391 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . "</li>";
7392 }
7393 }
7394
7395 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
7396 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
7397 }
7398
7399 // js to autoselect page field on focus
7400 if ($pagenavastextinput) {
7401 print ajax_autoselect('.pageplusone');
7402 }
7403
7404 print '</td>';
7405 print '</tr>';
7406
7407 print "</table>\n";
7408
7409 // Center
7410 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
7411 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">' . $morehtmlcenter . '</div>';
7412 }
7413
7414 print "<!-- End title -->\n\n";
7415}
7416
7433function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
7434{
7435 global $conf, $langs;
7436
7437 print '<div class="pagination"><ul>';
7438 if ($beforearrows) {
7439 print '<li class="paginationbeforearrows">';
7440 print $beforearrows;
7441 print '</li>';
7442 }
7443
7444 if (empty($hidenavigation)) {
7445 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
7446 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
7447 $pagesizechoices .= ',5000:5000';
7448 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
7449 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
7450 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
7451 //$pagesizechoices .= ',2:2';
7452 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
7453 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
7454 }
7455
7456 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7457 print '<li class="pagination">';
7458 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 . '">';
7459 print '<datalist id="limitlist">';
7460 } else {
7461 print '<li class="paginationcombolimit valignmiddle">';
7462 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")) . '">';
7463 }
7464 $tmpchoice = explode(',', $pagesizechoices);
7465 $tmpkey = $limit . ':' . $limit;
7466 if (!in_array($tmpkey, $tmpchoice)) {
7467 $tmpchoice[$tmpkey] = $tmpkey;
7468 }
7469 $tmpkey = $conf->liste_limit . ':' . $conf->liste_limit;
7470 if (!in_array($tmpkey, $tmpchoice)) {
7471 $tmpchoice[$tmpkey] = $tmpkey;
7472 }
7473 asort($tmpchoice, SORT_NUMERIC);
7474 foreach ($tmpchoice as $val) {
7475 $selected = '';
7476 $tmp = explode(':', $val);
7477 $key = $tmp[0];
7478 $val = $tmp[1];
7479 if ($key != '' && $val != '') {
7480 if ((int) $key == (int) $limit) {
7481 $selected = ' selected="selected"';
7482 }
7483 print '<option name="' . $key . '"' . $selected . '>' . dol_escape_htmltag($val) . '</option>' . "\n";
7484 }
7485 }
7486 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7487 print '</datalist>';
7488 } else {
7489 print '</select>';
7490 print ajax_combobox("limit" . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
7491 //print ajax_combobox("limit");
7492 }
7493
7494 if ($conf->use_javascript_ajax) {
7495 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
7496 <script>
7497 jQuery(document).ready(function () {
7498 jQuery(".selectlimit").change(function() {
7499 console.log("We change limit so we submit the form");
7500 $(this).parents(\'form:first\').submit();
7501 });
7502 });
7503 </script>
7504 ';
7505 }
7506 print '</li>';
7507 }
7508 if ($page > 0) {
7509 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>';
7510 }
7511 if ($betweenarrows) {
7512 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7513 print $betweenarrows;
7514 print '<!--</div>-->';
7515 }
7516 if ($nextpage > 0) {
7517 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>';
7518 }
7519 if ($afterarrows) {
7520 print '<li class="paginationafterarrows">';
7521 print $afterarrows;
7522 print '</li>';
7523 }
7524 }
7525 print '</ul></div>' . "\n";
7526}
7527
7528
7540function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7541{
7542 $morelabel = '';
7543
7544 if (preg_match('/%/', $rate)) {
7545 $rate = str_replace('%', '', $rate);
7546 $addpercent = true;
7547 }
7548 $reg = array();
7549 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7550 $morelabel = ' (' . $reg[1] . ')';
7551 $rate = preg_replace('/\s*' . preg_quote($morelabel, '/') . '/', '', $rate);
7552 $morelabel = ' ' . ($html ? '<span class="opacitymedium">' : '') . '(' . $reg[1] . ')' . ($html ? '</span>' : '');
7553 }
7554 if (preg_match('/\*/', $rate)) {
7555 $rate = str_replace('*', '', $rate);
7556 $info_bits |= 1;
7557 }
7558
7559 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7560 if (!preg_match('/\//', $rate)) {
7561 $ret = price($rate, 0, '', 0, 0) . ($addpercent ? '%' : '');
7562 } else {
7563 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7564 $ret = $rate . ($addpercent ? '%' : '');
7565 }
7566 if (($info_bits & 1) && $usestarfornpr >= 0) {
7567 $ret .= ' *';
7568 }
7569 $ret .= $morelabel;
7570 return $ret;
7571}
7572
7573
7589function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7590{
7591 global $langs, $conf;
7592
7593 // Clean parameters
7594 if (empty($amount)) {
7595 $amount = 0; // To have a numeric value if amount not defined or = ''
7596 }
7597 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7598 if ($rounding == -1) {
7599 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7600 }
7601 $nbdecimal = $rounding;
7602
7603 if ($outlangs === 'none') {
7604 // Use international separators
7605 $dec = '.';
7606 $thousand = '';
7607 } else {
7608 // Output separators by default (french)
7609 $dec = ',';
7610 $thousand = ' ';
7611
7612 // If $outlangs not forced, we use use language
7613 if (!($outlangs instanceof Translate)) {
7614 $outlangs = $langs;
7615 }
7616
7617 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7618 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7619 }
7620 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7621 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7622 }
7623 if ($thousand == 'None') {
7624 $thousand = '';
7625 } elseif ($thousand == 'Space') {
7626 $thousand = ' ';
7627 }
7628 }
7629 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7630
7631 //print "amount=".$amount."-";
7632 $amount = str_replace(',', '.', $amount); // should be useless
7633 //print $amount."-";
7634 $data = explode('.', $amount);
7635 $decpart = isset($data[1]) ? $data[1] : '';
7636 $decpart = preg_replace('/0+$/i', '', $decpart); // Remove 0 at end of decimal part
7637 //print "decpart=".$decpart."<br>";
7638 $end = '';
7639
7640 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7641 if (dol_strlen($decpart) > $nbdecimal) {
7642 $nbdecimal = dol_strlen($decpart);
7643 }
7644
7645 // If nbdecimal is higher than max to show
7646 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7647 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7648 $nbdecimal = $nbdecimalmaxshown;
7649 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7650 // If output is truncated, we show ...
7651 $end = '...';
7652 }
7653 }
7654
7655 // If force rounding
7656 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7657 if ($forcerounding === 'MU') {
7658 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7659 } elseif ($forcerounding === 'MT') {
7660 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7661 } elseif ($forcerounding >= 0) {
7662 $nbdecimal = (int) $forcerounding;
7663 }
7664 }
7665
7666 // Format number
7667 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7668 // Add symbol of currency if requested
7669 $cursymbolbefore = $cursymbolafter = '';
7670 if ($currency_code && is_object($outlangs)) {
7671 if ($currency_code == 'auto') {
7672 $currency_code = $conf->currency;
7673 }
7674
7675 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7676 $listoflanguagesbefore = array('nl_NL');
7677 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7678 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7679 } else {
7680 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7681 $cursymbolafter .= ($tmpcur == $currency_code ? ' ' . $tmpcur : $tmpcur);
7682 }
7683 }
7684 $output = $cursymbolbefore . $output . $end . ($cursymbolafter ? ' ' : '') . $cursymbolafter;
7685 if ($form) {
7686 $output = preg_replace('/\s/', '&nbsp;', $output);
7687 $output = preg_replace('/\'/', '&#039;', $output);
7688 }
7689
7690 return $output;
7691}
7692
7718function price2num($amount, $rounding = '', $option = 0)
7719{
7720 global $langs;
7721
7722 // Clean parameters
7723 if (is_null($amount)) {
7724 $amount = '';
7725 }
7726
7727 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7728 // Numbers must be '1234.56'
7729 // Decimal delimiter for PHP and database SQL requests must be '.'
7730 $dec = ',';
7731 $thousand = ' ';
7732 if (is_null($langs)) { // $langs is not defined, we use english values.
7733 $dec = '.';
7734 $thousand = ',';
7735 } else {
7736 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7737 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7738 }
7739 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7740 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7741 }
7742 }
7743 if ($thousand == 'None') {
7744 $thousand = '';
7745 } elseif ($thousand == 'Space') {
7746 $thousand = ' ';
7747 }
7748 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7749
7750 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7751 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7752 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7753 if (!is_numeric($amount)) {
7754 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7755 }
7756
7757 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
7758 $amount = str_replace($thousand, '', $amount);
7759 }
7760
7761 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7762 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7763 // So if number was already a good number, it is converted into local Dolibarr setup.
7764 if (is_numeric($amount)) {
7765 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7766 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7767 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7768 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7769 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7770 }
7771 //print "QQ".$amount."<br>\n";
7772
7773 // Now make replaceents (the main goal of function)
7774
7775 if ($thousand != ',' && $thousand != '.') {
7776 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7777
7778 // REGEX: Find the integral and decimal parts.
7779 //
7780 // We require that the decimal point only appears once in $amount.
7781 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7782 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7783 // It covers two cases:
7784 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7785 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7786 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7787 $matches = array();
7788 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7789 $intPart = $matches['int'];
7790 $decPart = $matches['dec'];
7791
7792 // Remove all commas and dots from intPart
7793 $intPart = str_replace(['.', ','], '', $intPart);
7794
7795 // Combine intPart and decPart with a dot
7796 $amount = $intPart . $dec . $decPart;
7797 }
7798 }
7799
7800 $amount = str_replace(' ', '', $amount); // To avoid spaces
7801 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7802 $amount = str_replace($dec, '.', $amount);
7803
7804 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7805 }
7806 //print ' XX'.$amount.' '.$rounding;
7807
7808 // Now, $amount is a real PHP float number. We make a rounding if required.
7809 if ($rounding) {
7810 $nbofdectoround = '';
7811 if ($rounding == 'MU') {
7812 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7813 } elseif ($rounding == 'MT') {
7814 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7815 } elseif ($rounding == 'MS') {
7816 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK', 5);
7817 } elseif ($rounding == 'CU') {
7818 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7819 } elseif ($rounding == 'CT') {
7820 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7821 } elseif (is_numeric($rounding)) {
7822 $nbofdectoround = (int) $rounding;
7823 }
7824
7825 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7826 if (dol_strlen($nbofdectoround)) {
7827 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7828 } else {
7829 return 'ErrorBadParameterProvidedToFunction';
7830 }
7831 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7832
7833 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7834 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7835 if (is_numeric($amount)) {
7836 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7837 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7838 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7839 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7840 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7841 }
7842 //print "TT".$amount.'<br>';
7843
7844 // Always make replace because each math function (like round) replace
7845 // with local values and we want a number that has a SQL string format x.y
7846 if ($thousand != ',' && $thousand != '.') {
7847 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7848 }
7849
7850 $amount = str_replace(' ', '', $amount); // To avoid spaces
7851 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7852 $amount = str_replace($dec, '.', $amount);
7853
7854 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7855 }
7856
7857 return $amount;
7858}
7859
7872function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7873{
7874 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
7875
7876 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7877 $dimension *= 1000000;
7878 $unit -= 6;
7879 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7880 $dimension *= 1000;
7881 $unit -= 3;
7882 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7883 $dimension /= 1000000;
7884 $unit += 6;
7885 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7886 $dimension /= 1000;
7887 $unit += 3;
7888 }
7889 // Special case when we want output unit into pound or ounce
7890 /* TODO
7891 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7892 {
7893 $dimension = // convert dimension from standard unit into ounce or pound
7894 $unit = $forceunitoutput;
7895 }
7896 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7897 {
7898 $dimension = // convert dimension from standard unit into ounce or pound
7899 $unit = $forceunitoutput;
7900 }*/
7901
7902 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7903 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7904 $ret .= ' ' . measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
7905
7906 return $ret;
7907}
7908
7909
7922function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7923{
7924 global $db, $conf, $mysoc;
7925
7926 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7927 $thirdparty_seller = $mysoc;
7928 }
7929
7930 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);
7931
7932 $vatratecleaned = $vatrate;
7933 $reg = array();
7934 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7935 $vatratecleaned = trim($reg[1]);
7936 $vatratecode = $reg[2];
7937 }
7938
7939 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7940 {
7941 return 0;
7942 }*/
7943
7944 // Some test to guess with no need to make database access
7945 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7946 if ($local == 1) {
7947 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7948 return 0;
7949 }
7950 if ($thirdparty_seller->id == $mysoc->id) {
7951 if (!$thirdparty_buyer->localtax1_assuj) {
7952 return 0;
7953 }
7954 } else {
7955 if (!$thirdparty_seller->localtax1_assuj) {
7956 return 0;
7957 }
7958 }
7959 }
7960
7961 if ($local == 2) {
7962 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7963 if (!$mysoc->localtax2_assuj) {
7964 return 0; // If main vat is 0, IRPF may be different than 0.
7965 }
7966 if ($thirdparty_seller->id == $mysoc->id) {
7967 if (!$thirdparty_buyer->localtax2_assuj) {
7968 return 0;
7969 }
7970 } else {
7971 if (!$thirdparty_seller->localtax2_assuj) {
7972 return 0;
7973 }
7974 }
7975 }
7976 } else {
7977 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7978 return 0;
7979 }
7980 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7981 return 0;
7982 }
7983 }
7984
7985 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7986 if (in_array($mysoc->country_code, array('ES'))) {
7987 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7988 }
7989
7990 // Search local taxes
7991 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7992 if ($local == 1) {
7993 if ($thirdparty_seller != $mysoc) {
7994 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7995 return $thirdparty_seller->localtax1_value;
7996 }
7997 } else { // i am the seller
7998 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7999 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
8000 }
8001 }
8002 }
8003 if ($local == 2) {
8004 if ($thirdparty_seller != $mysoc) {
8005 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
8006 // TODO We should also return value defined on thirdparty only if defined
8007 return $thirdparty_seller->localtax2_value;
8008 }
8009 } else { // i am the seller
8010 if (in_array($mysoc->country_code, array('ES'))) {
8011 return $thirdparty_buyer->localtax2_value;
8012 } else {
8013 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
8014 }
8015 }
8016 }
8017 }
8018
8019 // By default, search value of local tax on line of common tax
8020 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
8021 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8022 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdparty_seller->country_code) . "'";
8023 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8024 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8025 if (!empty($vatratecode)) {
8026 $sql .= " AND t.code ='" . $db->escape($vatratecode) . "'"; // If we have the code, we use it in priority
8027 } else {
8028 $sql .= " AND t.recuperableonly = '" . $db->escape((string) $vatnpr) . "'";
8029 }
8030
8031 $resql = $db->query($sql);
8032
8033 if ($resql) {
8034 $obj = $db->fetch_object($resql);
8035 if ($obj) {
8036 if ($local == 1) {
8037 return $obj->localtax1;
8038 } elseif ($local == 2) {
8039 return $obj->localtax2;
8040 }
8041 }
8042 }
8043
8044 return 0;
8045}
8046
8047
8056function isOnlyOneLocalTax($local)
8057{
8058 $tax = get_localtax_by_third($local);
8059
8060 $valors = explode(":", $tax);
8061
8062 if (count($valors) > 1) {
8063 return false;
8064 } else {
8065 return true;
8066 }
8067}
8068
8075function get_localtax_by_third($local)
8076{
8077 global $db, $mysoc;
8078
8079 $sql = " SELECT t.localtax" . $local . " as localtax";
8080 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t INNER JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = t.fk_pays";
8081 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.active = 1 AND t.entity IN (" . getEntity('c_tva') . ") AND t.taux = (";
8082 $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";
8083 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.entity IN (" . getEntity('c_tva') . ") AND tt.active = 1)";
8084 $sql .= " AND t.localtax" . $local . "_type <> '0'";
8085 $sql .= " ORDER BY t.rowid DESC";
8086
8087 $resql = $db->query($sql);
8088 if ($resql) {
8089 $obj = $db->fetch_object($resql);
8090 if ($obj) {
8091 return $obj->localtax;
8092 } else {
8093 return '0';
8094 }
8095 }
8096
8097 return 'Error';
8098}
8099
8100
8112function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
8113{
8114 global $db;
8115
8116 dol_syslog("getTaxesFromId vat id or rate = " . $vatrate);
8117
8118 // Search local taxes
8119 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
8120 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
8121 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8122 if ($firstparamisid) {
8123 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8124 } else {
8125 $vatratecleaned = $vatrate;
8126 $vatratecode = '';
8127 $reg = array();
8128 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
8129 $vatratecleaned = $reg[1];
8130 $vatratecode = $reg[2];
8131 }
8132
8133 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8134 /*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 ??
8135 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
8136 $sql .= " WHERE t.fk_pays = c.rowid";
8137 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8138 $sql .= " AND c.code = '" . $db->escape($buyer->country_code) . "'";
8139 } else {
8140 $sql .= " AND c.code = '" . $db->escape($seller->country_code) . "'";
8141 }
8142 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8143 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8144 if ($vatratecode) {
8145 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8146 }
8147 }
8148
8149 $resql = $db->query($sql);
8150 if ($resql) {
8151 $obj = $db->fetch_object($resql);
8152 if ($obj) {
8153 return array(
8154 'rowid' => $obj->rowid,
8155 'code' => $obj->code,
8156 'rate' => $obj->rate,
8157 'localtax1' => $obj->localtax1,
8158 'localtax1_type' => $obj->localtax1_type,
8159 'localtax2' => $obj->localtax2,
8160 'localtax2_type' => $obj->localtax2_type,
8161 'npr' => $obj->npr,
8162 'accountancy_code_sell' => $obj->accountancy_code_sell,
8163 'accountancy_code_buy' => $obj->accountancy_code_buy
8164 );
8165 } else {
8166 return array();
8167 }
8168 } else {
8169 dol_print_error($db);
8170 }
8171
8172 return array();
8173}
8174
8191function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
8192{
8193 global $db, $mysoc;
8194
8195 dol_syslog("getLocalTaxesFromRate vatrate=" . $vatrate . " local=" . $local);
8196
8197 // Search local taxes
8198 $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";
8199 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8200 if ($firstparamisid) {
8201 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8202 } else {
8203 $vatratecleaned = $vatrate;
8204 $vatratecode = '';
8205 $reg = array();
8206 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
8207 $vatratecleaned = $reg[1];
8208 $vatratecode = $reg[2];
8209 }
8210
8211 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8212 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
8213 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
8214 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'"; // local tax in spain use the buyer country ??
8215 } else {
8216 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
8217 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'";
8218 }
8219 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8220 if ($vatratecode) {
8221 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8222 }
8223 }
8224
8225 $resql = $db->query($sql);
8226 if ($resql) {
8227 $obj = $db->fetch_object($resql);
8228
8229 if ($obj) {
8230 $vateratestring = $obj->rate . ($obj->code ? ' (' . $obj->code . ')' : '');
8231
8232 if ($local == 1) {
8233 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8234 } elseif ($local == 2) {
8235 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8236 } else {
8237 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);
8238 }
8239 }
8240 }
8241
8242 return array();
8243}
8244
8255function get_product_vat_for_country($idprod, $thirdpartytouseforcountry, $idprodfournprice = 0)
8256{
8257 global $db, $mysoc;
8258
8259 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8260
8261 $ret = 0;
8262 $found = 0;
8263
8264 if ($idprod > 0) {
8265 // Load product
8266 $product = new Product($db);
8267 $product->fetch($idprod);
8268
8269 if (($mysoc->country_code == $thirdpartytouseforcountry->country_code)
8270 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouseforcountry->country_code, array('FR', 'MC')))
8271 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouseforcountry->country_code, array('MQ', 'GP')))
8272 ) {
8273 // If country of thirdparty to consider is ours
8274 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
8275 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
8276 if ($result > 0) {
8277 $ret = $product->vatrate_supplier;
8278 if ($product->default_vat_code_supplier) {
8279 $ret .= ' (' . $product->default_vat_code_supplier . ')';
8280 }
8281 $found = 1;
8282 }
8283 }
8284 if (!$found) {
8285 $ret = $product->tva_tx; // Default sales vat of product
8286 if ($product->default_vat_code) {
8287 $ret .= ' (' . $product->default_vat_code . ')';
8288 }
8289 $found = 1;
8290 }
8291 } else {
8292 // TODO Read default product vat according to product and an other countrycode.
8293 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8294 }
8295 }
8296
8297 if (!$found) {
8298 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8299 // 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).
8300 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
8301 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8302 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8303 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8304 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8305 $sql .= $db->plimit(1);
8306
8307 $resql = $db->query($sql);
8308 if ($resql) {
8309 $obj = $db->fetch_object($resql);
8310 if ($obj) {
8311 $ret = $obj->vat_rate;
8312 if ($obj->default_vat_code) {
8313 $ret .= ' (' . $obj->default_vat_code . ')';
8314 }
8315 }
8316 $db->free($resql);
8317 } else {
8318 dol_print_error($db);
8319 }
8320 } else {
8321 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
8322 // '1.23'
8323 // or '1.23 (CODE)'
8324 $defaulttx = '';
8325 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8326 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8327 }
8328 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8329 $defaultcode = $reg[1];
8330 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8331 }*/
8332
8333 $ret = $defaulttx;
8334 }
8335 }
8336
8337 dol_syslog("get_product_vat_for_country: ret=" . $ret);
8338
8339 return $ret;
8340}
8341
8351function get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
8352{
8353 global $db, $mysoc;
8354
8355 if (!class_exists('Product')) {
8356 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8357 }
8358
8359 $ret = 0;
8360 $found = 0;
8361
8362 if ($idprod > 0) {
8363 // Load product
8364 $product = new Product($db);
8365 $result = $product->fetch($idprod);
8366
8367 if ($mysoc->country_code == $thirdpartytouseforcountry->country_code) { // If selling country is ours
8368 /* Not defined yet, so we don't use this
8369 if ($local==1) $ret=$product->localtax1_tx;
8370 elseif ($local==2) $ret=$product->localtax2_tx;
8371 $found=1;
8372 */
8373 } else {
8374 // TODO Read default product vat according to product and another countrycode.
8375 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8376 }
8377 }
8378
8379 if (!$found) {
8380 // If vat of product for the country not found or not defined, we return higher vat of country.
8381 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
8382 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8383 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8384 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8385 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
8386 $sql .= $db->plimit(1);
8387
8388 $resql = $db->query($sql);
8389 if ($resql) {
8390 $obj = $db->fetch_object($resql);
8391 if ($obj) {
8392 if ($local == 1) {
8393 $ret = $obj->localtax1;
8394 } elseif ($local == 2) {
8395 $ret = $obj->localtax2;
8396 }
8397 }
8398 } else {
8399 dol_print_error($db);
8400 }
8401 }
8402
8403 dol_syslog("get_product_localtax_for_country: ret=" . $ret);
8404 return $ret;
8405}
8406
8425function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8426{
8427 global $mysoc, $db, $hookmanager;
8428
8429 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8430
8431 // Note: possible values for tva_assuj are 0/1 or franchise/reel
8432 $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;
8433
8434 if (empty($thirdparty_seller->country_code)) {
8435 $thirdparty_seller->country_code = $mysoc->country_code;
8436 }
8437 $seller_country_code = $thirdparty_seller->country_code;
8438 $seller_in_cee = isInEEC($thirdparty_seller);
8439
8440 if (empty($thirdparty_buyer->country_code)) {
8441 $thirdparty_buyer->country_code = $mysoc->country_code;
8442 }
8443 $buyer_country_code = $thirdparty_buyer->country_code;
8444 $buyer_in_cee = isInEEC($thirdparty_buyer);
8445
8446 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'));
8447
8448 $vatvalue = 0;
8449 $vatrule = '';
8450
8451 // 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)
8452 // we use the buyer VAT.
8453 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8454 if ($seller_in_cee && $buyer_in_cee) {
8455 $isacompany = $thirdparty_buyer->isACompany();
8456 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8457 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8458 if (!isValidVATID($thirdparty_buyer)) {
8459 $isacompany = 0;
8460 }
8461 }
8462
8463 if (!$isacompany) {
8464 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
8465 $vatrule = 'VATRULE 0';
8466 }
8467 }
8468 }
8469
8470 // If seller does not use VAT, default VAT is 0. End of rule.
8471 if (empty($vatrule) && !$seller_use_vat) {
8472 //print 'VATRULE 1';
8473 // TODO get the VAT Code of exemption asked into setup if country isInEEC (from an array list of possible
8474 // values like VATEX-EU-132-*, VATEX-FR-FRANCHISE, VATEX-EU-AE...
8475 // When we had recorded it, we also added a corresponding entry into table of vat code if it does not exists yet.
8476 // Here we test if entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-132-xx)'
8477 // If not, we add it and we return '0 (VATEX-EU-132-xx)'
8478 $vatvalue = 0;
8479 $vatrule = 'VATRULE 1';
8480 }
8481
8482 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
8483 if (empty($vatrule) && !empty($thirdparty_buyer->state_id)) {
8484 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
8485 $sql .= " FROM " . $db->prefix() . "c_tva as t";
8486 $sql .= " INNER JOIN " . $db->prefix() . "c_departements as d ON t.fk_department_buyer = d.rowid";
8487 $sql .= " WHERE d.rowid = " . ((int) $thirdparty_buyer->state_id);
8488 $sql .= " AND t.active > 0";
8489 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
8490 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8491
8492 $res = $db->query($sql);
8493 if ($res) {
8494 if ($db->num_rows($res)) {
8495 $obj = $db->fetch_object($res);
8496
8497 $vatvalue = $obj->vat_default_rate . ' (' . $obj->vat_default_code . ')';
8498 $vatrule = 'VATRULE 2';
8499 }
8500 $db->free($res);
8501 }
8502 }
8503
8504 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
8505 if (empty($vatrule) && (
8506 ($seller_country_code == $buyer_country_code)
8507 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
8508 || (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.
8509 )) { // Warning ->country_code not always defined
8510 //print 'VATRULE 3';
8511 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8512
8513 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
8514 // Special case for india.
8515 //print 'VATRULE 3b';
8516 $reg = array();
8517 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
8518 // we must revert the C+S into I
8519 $tmpvat = str_replace("C+S", "I", $tmpvat);
8520 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8521 // we must revert the I into C+S
8522 $tmpvat = str_replace("I", "C+S", $tmpvat);
8523 }
8524 }
8525
8526 $vatvalue = $tmpvat;
8527 $vatrule = 'VATRULE 3b';
8528 }
8529
8530 // 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.
8531 // 'VATRULE 4' - Not supported
8532
8533 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8534 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8535 if (empty($vatrule) && ($seller_in_cee && $buyer_in_cee)) {
8536 $isacompany = $thirdparty_buyer->isACompany();
8537 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8538 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8539 if (!isValidVATID($thirdparty_buyer)) {
8540 $isacompany = 0;
8541 }
8542 }
8543
8544 if (!$isacompany) {
8545 //print 'VATRULE 5';
8546 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8547 $vatrule = 'VATRULE 5';
8548 } else {
8549 //print 'VATRULE 6';
8550 // TODO This is the case of VAT exemption 'VATEX-EU-IC'
8551 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-IC)'
8552 // If not, we add it and we return '0 (VATEX-EU-IC)'
8553 $vatvalue = 0;
8554 $vatrule = 'VATRULE 6';
8555 }
8556 }
8557
8558 // 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
8559 // 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
8560 if (empty($vatrule) && getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8561 $isacompany = $thirdparty_buyer->isACompany();
8562 if (!$isacompany) {
8563 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8564 $vatrule = 'VATRULE extra';
8565 //print 'VATRULE extra';
8566 }
8567 }
8568
8569 // Otherwise the VAT proposed by default=0. End of rule.
8570 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8571 //print 'VATRULE 7';
8572 // TODO This is the case of VAT exemption 'VATEX-EU-G'
8573 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-xxx)'
8574 // If not, we add it and we return '0 (VATEX-xxx)'
8575
8576 // Allow an external module to bypass the calculation of prices
8577 $parameters = array('vatvalue' => $vatvalue, 'vatrule' => $vatrule);
8578 $tmpobject = null;
8579 $tmpaction = '';
8580 // @phan-suppress-next-line PhanPluginConstantVariableNull
8581 $reshook = $hookmanager->executeHooks('get_default_tva', $parameters, $tmpobject, $tmpaction); // @phan-suppress-current-line PhanPluginConstantVariableNull
8582 if ($reshook > 0 && !empty($hookmanager->resArray['vatvalue'])) {
8583 $vatvalue = $hookmanager->resArray['vatvalue'];
8584 $vatrule = $hookmanager->resArray['vatrule']; // For information
8585 }
8586
8587 return $vatvalue;
8588}
8589
8590
8601function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8602{
8603 global $db;
8604
8605 if ($idprodfournprice > 0) {
8606 if (!class_exists('ProductFournisseur')) {
8607 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
8608 }
8609 $prodprice = new ProductFournisseur($db);
8610 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8611 return $prodprice->fourn_tva_npr;
8612 } elseif ($idprod > 0) {
8613 if (!class_exists('Product')) {
8614 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8615 }
8616 $prod = new Product($db);
8617 $prod->fetch($idprod);
8618 return $prod->tva_npr;
8619 }
8620
8621 return 0;
8622}
8623
8637function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8638{
8639 global $mysoc;
8640
8641 if (!is_object($thirdparty_seller)) {
8642 return -1;
8643 }
8644 if (!is_object($thirdparty_buyer)) {
8645 return -1;
8646 }
8647
8648 if (empty($thirdparty_seller->country_code)) {
8649 $thirdparty_seller->country_code = $mysoc->country_code;
8650 }
8651 $seller_country_code = $thirdparty_seller->country_code;
8652 //$seller_in_cee = isInEEC($thirdparty_seller);
8653
8654 if (empty($thirdparty_buyer->country_code)) {
8655 $thirdparty_buyer->country_code = $mysoc->country_code;
8656 }
8657 $buyer_country_code = $thirdparty_buyer->country_code;
8658 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8659
8660 if ($local == 1) { // Localtax 1
8661 if ($mysoc->country_code == 'ES') {
8662 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8663 return 0;
8664 }
8665 } else {
8666 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8667 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8668 return 0;
8669 }
8670 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8671 return 0;
8672 }
8673 }
8674 } elseif ($local == 2) { //I Localtax 2
8675 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8676 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8677 return 0;
8678 }
8679 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8680 return 0;
8681 }
8682 }
8683
8684 if ($seller_country_code == $buyer_country_code) {
8685 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8686 }
8687
8688 return 0;
8689}
8690
8699function yn($yesno, $format = 1, $color = 0)
8700{
8701 global $langs;
8702
8703 $result = 'unknown';
8704 $classname = '';
8705 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'
8706 $result = $langs->trans('yes');
8707 if ($format == 1 || $format == 3) {
8708 $result = $langs->trans("Yes");
8709 }
8710 if ($format == 2) {
8711 $result = '<input type="checkbox" value="1" checked disabled>';
8712 }
8713 if ($format == 3) {
8714 $result = '<input type="checkbox" value="1" checked disabled> ' . $result;
8715 }
8716 if ($format == 4 || !is_numeric($format)) {
8717 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8718 }
8719
8720 $classname = 'ok';
8721 } else {
8722 $result = $langs->trans("no");
8723 if ($format == 1 || $format == 3) {
8724 $result = $langs->trans("No");
8725 }
8726 if ($format == 2) {
8727 $result = '<input type="checkbox" value="0" disabled>';
8728 }
8729 if ($format == 3) {
8730 $result = '<input type="checkbox" value="0" disabled> ' . $result;
8731 }
8732 if ($format == 4 || !is_numeric($format)) {
8733 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8734 }
8735
8736 if ($color == 2) {
8737 $classname = 'ok';
8738 } else {
8739 $classname = 'error';
8740 }
8741 }
8742 if ($color) {
8743 return '<span class="' . $classname . '">' . $result . '</span>';
8744 }
8745 return $result;
8746}
8747
8766function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8767{
8768 if (empty($modulepart) && is_object($object)) {
8769 if (!empty($object->module)) {
8770 $modulepart = $object->module;
8771 } elseif (!empty($object->element)) {
8772 $modulepart = $object->element;
8773 }
8774 }
8775
8776 $path = '';
8777
8778 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8779 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8780 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8781 $arrayforoldpath['product'] = 2;
8782 }
8783
8784 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8785 $level = $arrayforoldpath[$modulepart];
8786 }
8787 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8788 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8789 if (empty($num) && is_object($object)) {
8790 $num = $object->id;
8791 }
8792 if (empty($alpha)) {
8793 $num = preg_replace('/([^0-9])/i', '', $num);
8794 } else {
8795 $num = preg_replace('/^.*\-/i', '', $num);
8796 }
8797 $num = substr("000" . $num, -$level);
8798 if ($level == 1) {
8799 $path = substr($num, 0, 1);
8800 }
8801 if ($level == 2) {
8802 $path = substr($num, 1, 1) . '/' . substr($num, 0, 1);
8803 }
8804 if ($level == 3) {
8805 $path = substr($num, 2, 1) . '/' . substr($num, 1, 1) . '/' . substr($num, 0, 1);
8806 }
8807 } else {
8808 // We will enhance here a common way of forging path for document storage.
8809 // In a future, we may distribute directories on several levels depending on setup and object.
8810 // Here, $object->id, $object->ref and $modulepart are required.
8811 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceof Societe) {
8812 // 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
8813 $path = dol_sanitizeFileName((string) $object->id);
8814 } else {
8815 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8816 }
8817 }
8818
8819 if (empty($withoutslash) && !empty($path)) {
8820 $path .= '/';
8821 }
8822
8823 return $path;
8824}
8825
8834function dol_mkdir($dir, $dataroot = '', $newmask = '')
8835{
8836 dol_syslog("functions.lib::dol_mkdir: dir=" . $dir, LOG_INFO);
8837
8838 $dir = dol_sanitizePathName($dir, '_', 0);
8839
8840 $dir_osencoded = dol_osencode($dir);
8841 if (@is_dir($dir_osencoded)) {
8842 return 0;
8843 }
8844
8845 $nberr = 0;
8846 $nbcreated = 0;
8847
8848 $ccdir = '';
8849 if (!empty($dataroot)) {
8850 // Remove data root from loop
8851 $dir = str_replace($dataroot . '/', '', $dir);
8852 $ccdir = $dataroot . '/';
8853 }
8854
8855 $cdir = explode("/", $dir);
8856 $num = count($cdir);
8857 for ($i = 0; $i < $num; $i++) {
8858 if ($i > 0) {
8859 $ccdir .= '/' . $cdir[$i];
8860 } else {
8861 $ccdir .= $cdir[$i];
8862 }
8863 $regs = array();
8864 if (preg_match("/^.:$/", $ccdir, $regs)) {
8865 continue; // If the Windows path is incomplete, continue with next directory
8866 }
8867
8868 // Attention, is_dir() can fail event if the directory exists
8869 // (i.e. according the open_basedir configuration)
8870 if ($ccdir) {
8871 $ccdir_osencoded = dol_osencode($ccdir);
8872 if (!@is_dir($ccdir_osencoded)) {
8873 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8874
8875 umask(0);
8876 $dirmaskdec = octdec((string) $newmask);
8877 if (empty($newmask)) {
8878 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8879 }
8880 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8881 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8882 // If the is_dir has returned a false information, we arrive here
8883 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '" . $ccdir . "' (no permission to write into parent or directory already exists).", LOG_WARNING);
8884 $nberr++;
8885 } else {
8886 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' created", LOG_DEBUG);
8887 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8888 $nbcreated++;
8889 }
8890 } else {
8891 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8892 }
8893 }
8894 }
8895 return ($nberr ? -$nberr : $nbcreated);
8896}
8897
8898
8906function dolChmod($filepath, $newmask = '')
8907{
8908 if (!empty($newmask)) {
8909 @chmod($filepath, octdec($newmask));
8910 } elseif (getDolGlobalString('MAIN_UMASK')) {
8911 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8912 }
8913}
8914
8915
8921function picto_required()
8922{
8923 return '<span class="fieldrequired">*</span>';
8924}
8925
8926
8943function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8944{
8945 if (is_null($stringtoclean)) {
8946 return '';
8947 }
8948
8949 if ($removelinefeed == 2) {
8950 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8951 }
8952 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8953
8954 // 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)
8955 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8956
8957 $temp = str_replace('< ', '__ltspace__', $temp);
8958 $temp = str_replace('<:', '__lttwopoints__', $temp);
8959
8960 if ($strip_tags) {
8961 $temp = strip_tags($temp);
8962 } else {
8963 // Remove '<' into remaining, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
8964 $pattern = "/<[^<>]+>/";
8965 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8966 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8967 // pass 2 - $temp after pass 2: 0000-021
8968 $tempbis = $temp;
8969 do {
8970 $temp = $tempbis;
8971 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8972 $tempbis = preg_replace($pattern, '', $tempbis);
8973 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8974 } while ($tempbis != $temp);
8975
8976 $temp = $tempbis;
8977
8978 // 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).
8979 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8980 }
8981
8982 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8983
8984 // Remove also carriage returns
8985 if ($removelinefeed == 1) {
8986 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8987 }
8988
8989 // And double spaces
8990 if ($removedoublespaces) {
8991 while (strpos($temp, " ") !== false) {
8992 $temp = str_replace(" ", " ", $temp);
8993 }
8994 }
8995
8996 $temp = str_replace('__ltspace__', '< ', $temp);
8997 $temp = str_replace('__lttwopoints__', '<:', $temp);
8998
8999 return trim($temp);
9000}
9001
9021function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
9022{
9023 if (empty($allowed_tags)) {
9024 $allowed_tags = array(
9025 // HTML 4
9026 "html",
9027 "head",
9028 "body",
9029 "article",
9030 "a",
9031 "abbr",
9032 "b",
9033 "blockquote",
9034 "br",
9035 "cite",
9036 "div",
9037 "dl",
9038 "dd",
9039 "dt",
9040 "em",
9041 "font",
9042 "img",
9043 "ins",
9044 "hr",
9045 "i",
9046 "li",
9047 "ol",
9048 "p",
9049 "q",
9050 "s",
9051 "span",
9052 "strike",
9053 "strong",
9054 "title",
9055 "table",
9056 "tr",
9057 "th",
9058 "td",
9059 "u",
9060 "ul",
9061 "sup",
9062 "sub",
9063 "blockquote",
9064 "pre",
9065 "h1",
9066 "h2",
9067 "h3",
9068 "h4",
9069 "h5",
9070 "h6",
9071
9072 // HTML 5
9073 "footer",
9074 "header",
9075 "menu",
9076 "menuitem",
9077 "nav",
9078 "section"
9079 );
9080 }
9081 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
9082 if ($allowiframe) {
9083 if (!in_array('iframe', $allowed_tags)) {
9084 $allowed_tags[] = "iframe";
9085 }
9086 }
9087 if ($allowlink) {
9088 if (!in_array('link', $allowed_tags)) {
9089 $allowed_tags[] = "link";
9090 }
9091 if (!in_array('meta', $allowed_tags)) {
9092 $allowed_tags[] = "meta";
9093 }
9094 }
9095 if ($allowscript) {
9096 if (!in_array('script', $allowed_tags)) {
9097 $allowed_tags[] = "script";
9098 }
9099 }
9100 if ($allowstyle) {
9101 if (!in_array('style', $allowed_tags)) {
9102 $allowed_tags[] = "style";
9103 }
9104 }
9105
9106 $allowed_tags_string = implode("><", $allowed_tags);
9107 $allowed_tags_string = '<' . $allowed_tags_string . '>';
9108
9109 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
9110
9111 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
9112
9113 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
9114 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
9115
9116 if ($allowphp) {
9117 $allowed_tags[] = "commentphp";
9118 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
9119 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
9120 }
9121
9122 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
9123 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
9124
9125 // Remove all HTML tags
9126 $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
9127
9128 if ($cleanalsosomestyles) { // Clean for remaining html tags
9129 $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
9130 }
9131 if ($removeclassattribute) { // Clean for remaining html tags
9132 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
9133 }
9134
9135 // Remove 'javascript:' that we should not find into a text
9136 // 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)).
9137 if ($cleanalsojavascript) {
9138 $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);
9139 }
9140
9141 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
9142
9143 if ($allowphp) {
9144 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
9145 }
9146
9147 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
9148
9149
9150 return $temp;
9151}
9152
9153
9166function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
9167{
9168 if (is_null($allowed_attributes)) {
9169 $allowed_attributes = array(
9170 // HTML 4
9171 "allow",
9172 "allowfullscreen",
9173 "alt",
9174 "async",
9175 "class",
9176 "contenteditable",
9177 "crossorigin",
9178 "data-html",
9179 "frameborder",
9180 "height",
9181 "href",
9182 "id",
9183 "name",
9184 "property",
9185 "rel",
9186 "src",
9187 "style",
9188 "target",
9189 "title",
9190 "type",
9191 "width",
9192
9193 // HTML5
9194 "footer",
9195 "header",
9196 "menu",
9197 "menuitem",
9198 "nav",
9199 "section"
9200 );
9201 }
9202 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
9203 if (!in_array("content", $allowed_attributes)) {
9204 $allowed_attributes[] = "content";
9205 }
9206 if (!in_array("http-equiv", $allowed_attributes)) {
9207 $allowed_attributes[] = "http-equiv";
9208 }
9209
9210 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
9211 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
9212 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . $stringtoclean . '</body></html>';
9213
9214 // Warning: loadHTML does not support HTML5 on old libxml versions.
9215 $dom = new DOMDocument('', 'UTF-8');
9216 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
9217 $savwarning = error_reporting();
9218 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
9219 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
9220 error_reporting($savwarning);
9221
9222 if ($dom instanceof DOMDocument) {
9223 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
9224 $el = $els->item($i);
9225 if (!$el instanceof DOMElement) {
9226 continue;
9227 }
9228 $attrs = $el->attributes;
9229 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
9230 //var_dump($attrs->item($ii));
9231 if (!empty($attrs->item($ii)->name)) {
9232 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
9233 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
9234 $els->item($i)->removeAttribute($attrs->item($ii)->name);
9235 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
9236 // If attribute is 'style'
9237 $valuetoclean = $attrs->item($ii)->value;
9238
9239 if (isset($valuetoclean)) {
9240 do {
9241 $oldvaluetoclean = $valuetoclean;
9242 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
9243 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
9244 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
9245 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
9246 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
9247 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
9248 }
9249
9250 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
9251 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
9252 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
9253 } while ($oldvaluetoclean != $valuetoclean);
9254 }
9255
9256 $attrs->item($ii)->value = $valuetoclean;
9257 }
9258 }
9259 }
9260 }
9261 }
9262
9263 $dom->encoding = 'UTF-8';
9264
9265 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
9266 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
9267
9268 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
9269 $return = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]*' . preg_quote('></head><body>', '/') . '/', '', $return);
9270 $return = preg_replace('/' . preg_quote('</body></html>', '/') . '$/', '', trim($return));
9271
9272 return trim($return);
9273 } else {
9274 return $stringtoclean;
9275 }
9276}
9277
9289function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
9290{
9291 $temp = $stringtoclean;
9292 foreach ($disallowed_tags as $tagtoremove) {
9293 $temp = preg_replace('/<\/?' . $tagtoremove . '>/', '', $temp);
9294 $temp = preg_replace('/<\/?' . $tagtoremove . '\s+[^>]*>/', '', $temp);
9295 }
9296
9297 if ($cleanalsosomestyles) {
9298 $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
9299 }
9300
9301 return $temp;
9302}
9303
9304
9314function dolCloseUnclosedHtmlTags($text)
9315{
9316 if (!is_string($text) || $text === '') {
9317 return $text;
9318 }
9319
9320 // Tags that never carry a closing tag
9321 $selfclosing = array('br', 'hr', 'img', 'input', 'meta', 'link', 'source', 'col', 'area', 'base', 'embed', 'param', 'track', 'wbr');
9322
9323 $opened = array();
9324 if (preg_match_all('/<\s*(\/?)([a-zA-Z][a-zA-Z0-9]*)[^>]*?(\/?)\s*>/', $text, $matches, PREG_SET_ORDER)) {
9325 foreach ($matches as $match) {
9326 $tag = strtolower($match[2]);
9327 if (in_array($tag, $selfclosing) || !empty($match[3])) {
9328 continue;
9329 }
9330 if (empty($match[1])) {
9331 $opened[] = $tag;
9332 } else {
9333 // Close the most recent matching opened tag, ignore a stray closing tag
9334 $idx = array_search($tag, array_reverse($opened, true), true);
9335 if ($idx !== false) {
9336 unset($opened[$idx]);
9337 }
9338 }
9339 }
9340 }
9341
9342 foreach (array_reverse($opened) as $tag) {
9343 $text .= '</'.$tag.'>';
9344 }
9345
9346 return $text;
9347}
9348
9358function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
9359{
9360 if ($nboflines == 1) {
9361 if (dol_textishtml($text)) {
9362 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
9363 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
9364 } else {
9365 if (isset($text)) {
9366 $firstline = preg_replace('/[\n\r].*/', '', $text);
9367 } else {
9368 $firstline = '';
9369 }
9370 }
9371 return $firstline . (isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
9372 } else {
9373 $ishtml = 0;
9374 if (dol_textishtml($text)) {
9375 $text = preg_replace('/\n/', '', $text);
9376 $ishtml = 1;
9377 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9378 } else {
9379 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9380 }
9381
9382 $text = strtr($text, $repTable);
9383 if ($charset == 'UTF-8') {
9384 $pattern = '/(<br[^>]*>)/Uu';
9385 } else {
9386 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9387 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9388 }
9389 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9390
9391 $firstline = '';
9392 $i = 0;
9393 $countline = 0;
9394 $lastaddediscontent = 1;
9395 while ($countline < $nboflines && isset($a[$i])) {
9396 if (preg_match('/<br[^>]*>/', $a[$i])) {
9397 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
9398 $firstline .= ($ishtml ? "<br>\n" : "\n");
9399 // Is it a br for a new line of after a printed line ?
9400 if (!$lastaddediscontent) {
9401 $countline++;
9402 }
9403 $lastaddediscontent = 0;
9404 }
9405 } else {
9406 $firstline .= $a[$i];
9407 $lastaddediscontent = 1;
9408 $countline++;
9409 }
9410 $i++;
9411 }
9412
9413 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
9414 //unset($a);
9415 $ret = $firstline . ($adddots ? '...' : '');
9416 //exit;
9417 return $ret;
9418 }
9419}
9420
9421
9433function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
9434{
9435 if (is_null($stringtoencode)) {
9436 return '';
9437 }
9438
9439 if (!$nl2brmode) {
9440 return nl2br($stringtoencode, $forxml);
9441 } else {
9442 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
9443 return $ret;
9444 }
9445}
9446
9456function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
9457{
9458 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
9459 // TODO using sandbox on inline html content is not possible yet with current browsers
9460 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
9461 //$s .= $stringtoencode;
9462 //$s .= '</body></html></iframe>';
9463 return $stringtoencode;
9464 } else {
9465 $out = $stringtoencode;
9466
9467 // First clean HTML content
9468 do {
9469 $oldstringtoclean = $out;
9470
9471 $outishtml = 0;
9472 if (dol_textishtml($out)) {
9473 $outishtml = 1;
9474 }
9475
9476 // HTML sanitizer by DOMDocument
9477 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
9478 try {
9479 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
9480 if (LIBXML_VERSION < 20900) {
9481 // Avoid load of external entities (security problem).
9482 // Required only if LIBXML_VERSION < 20900
9483 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
9484 libxml_disable_entity_loader(true);
9485 }
9486
9487 $dom = new DOMDocument();
9488 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
9489 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
9490 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
9491 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
9492 // I don't know what the xml encoding is the trick for
9493 if ($outishtml) {
9494 //$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>';
9495 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">' . $out . '</div></body></html>';
9496 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
9497 } else {
9498 //$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>';
9499 $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>';
9500 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
9501 }
9502
9503 // Note: <a href="https://__[aaa]__/aaa.html"> is transformed into <a href="https://__[aaa]__/aaa.html">
9504 // We don't want that, so we protect __[xxx]__ by replacing [ and ] before loadHTML and restore them after saveHTML
9505 $out = preg_replace_callback(
9506 '/__\[([0-9a-zA-Z_]+)\]__/',
9511 function ($m) {
9512 return '__BRACKETSTART' . $m[1] . 'BRACKETEND__';
9513 },
9514 $out
9515 );
9516
9517 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
9518
9519 $dom->encoding = 'UTF-8';
9520
9521 $out = trim($dom->saveHTML());
9522
9523 // Restore [ and ] that were protected before loadHTML
9524 $out = preg_replace_callback(
9525 '/__BRACKETSTART([0-9a-zA-Z_]+)BRACKETEND__/',
9530 function ($m) {
9531 return '__[' . $m[1] . ']__';
9532 },
9533 $out
9534 );
9535
9536 // Remove the trick added to solve pb with text in utf8 and text without parent tag
9537 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
9538 $out = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]+' . preg_quote('></head><body><div class="tricktoremove">', '/') . '/', '', $out);
9539 $out = preg_replace('/' . preg_quote('</div></body></html>', '/') . '$/', '', trim($out));
9540 //$out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
9541 //$out = preg_replace('/<\/div>$/', '', $out);
9542
9543 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
9544 $out = str_replace('<br>', '', $out);
9545 }
9546 } catch (Exception $e) {
9547 // If error, invalid HTML string with no way to clean it
9548 //print $e->getMessage();
9549 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9550 }
9551 }
9552
9553 // HTML sanitizer by Tidy
9554 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
9555 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
9556 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
9557 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
9558 try {
9559 //var_dump($out);
9560
9561 // Try cleaning using tidy
9562 if (extension_loaded('tidy') && class_exists("tidy")) {
9563 //print "aaa".$out."\n";
9564
9565 // See options at https://tidy.sourceforge.net/docs/quickref.html
9566 $config = array(
9567 'clean' => false,
9568 // 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;
9569 'quote-marks' => false,
9570 'doctype' => 'strict',
9571 'show-body-only' => true,
9572 "indent-attributes" => false,
9573 "vertical-space" => false,
9574 //'ident' => false, // Not always supported
9575 "wrap" => 0,
9576 'preserve-entities' => true
9577 // HTML5 tags
9578 //'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',
9579 //'new-blocklevel-tags' => 'footer header section menu menuitem'
9580 //'new-empty-tags' => 'command embed keygen source track wbr',
9581 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
9582 );
9583
9584 // Tidy
9585 $tidy = new tidy();
9586 $out = $tidy->repairString($out, $config, 'utf8');
9587
9588 //print "xxx".$out;exit;
9589 }
9590
9591 //var_dump($out);
9592 } catch (Exception $e) {
9593 // If error, invalid HTML string with no way to clean it
9594 //print $e->getMessage();
9595 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9596 }
9597 }
9598
9599 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
9600 // TODO $out = preg_replace('/[\x{2000}-\x{200D}\x{FEFF}]/u', ' ', $out);
9601 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
9602
9603 // Clean some html entities that are useless so text is cleaner
9604 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
9605
9606 // Ckeditor uses the numeric entity for apostrophe, so we force it to
9607 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
9608 $out = preg_replace('/&#39;/i', '&apos;', $out);
9609
9610 // 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).
9611 // 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
9612 // using a non conventionnal way to be encoded, to not have them sanitized just after)
9613 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
9614 $out = preg_replace_callback(
9615 '/&#(x?[0-9][0-9a-f]+;?)/i',
9620 static function ($m) {
9621 return realCharForNumericEntities($m);
9622 },
9623 $out
9624 );
9625 }
9626
9627 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
9628 $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'.
9629
9630 // Keep only some html tags and remove also some 'javascript:' strings
9631 if ($check == 'restricthtmlallowlinkscript') {
9632 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
9633 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
9634 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
9635 } elseif ($check == 'restricthtmlallowiframe') {
9636 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
9637 } else {
9638 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
9639 }
9640
9641 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
9642 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
9644 }
9645
9646 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
9647 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
9648 $out = preg_replace('/&apos;/i', "&#39;", $out);
9649
9650 // Now remove js
9651 // 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
9652 $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)>
9653 $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);
9654 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
9655 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
9656 $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);
9657 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
9658 // More not into the previous list
9659 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
9660 } while ($oldstringtoclean != $out);
9661
9662 // Check the limit of external links that are automatically executed in a Rich text content. We count:
9663 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
9664 // 'url(' to avoid inline style like background: url(http...
9665 // '<link' to avoid <link href="http...">
9666 $reg = array();
9667 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
9668 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
9669 $nblinks = count($reg[0]);
9670 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
9671 $out = 'ErrorTooManyLinksIntoHTMLString';
9672 }
9673
9674 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
9675 if ($nblinks > 0) {
9676 $out = 'ErrorHTMLLinksNotAllowed';
9677 }
9678 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
9679 // Refuse any links except it they are to the wrapper document.php or viewimage.php
9680 $nblinks = 0;
9681
9682 // Loop on each url in src= and url(
9683 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
9684
9686
9687 $matches = array();
9688 if (preg_match_all($pattern, $out, $matches)) {
9689 // URLs are into $matches[1] or $matches[2]
9690 $urls = array();
9691 foreach ($matches[1] as $tmpval) {
9692 if (!empty($tmpval)) {
9693 $urls[] = $tmpval;
9694 }
9695 }
9696 foreach ($matches[2] as $tmpval) {
9697 if (!empty($tmpval)) {
9698 $urls[] = $tmpval;
9699 }
9700 }
9701
9702 // Show URLs
9703 $firstexturl = '';
9704 $secondexturl = '';
9705 foreach ($urls as $url) {
9706 $urlok = 0;
9707 $parsedurl = parse_url($url);
9708 if (!empty($parsedurl)) {
9709 if (preg_match('/'.preg_quote($dolibarr_main_url_root, '/').'/', $url)
9710 //&& preg_match('/(document|viewimage)\.php$/', $parsedurl['path']) && preg_match('/modulepart=(media|mycompany)/', $parsedurl['query'])
9711 ) {
9712 $urlok = 1;
9713 }
9714 }
9715 if (!$urlok) {
9716 $nblinks++;
9717 if (empty($firstexturl)) {
9718 $firstexturl = $url;
9719 } elseif (empty($secondexturl)) {
9720 $secondexturl = $url;
9721 }
9722 //echo "Found url = ".$url . "\n";
9723 }
9724 }
9725 if ($nblinks > 0) {
9726 $out = 'ErrorHTMLExternalLinksNotAllowed (Example: '.$firstexturl.($secondexturl ? ' '.$secondexturl : '').')';
9727 }
9728 }
9729 }
9730
9731 return $out;
9732 }
9733}
9734
9755function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9756{
9757 if (is_null($stringtoencode)) {
9758 return '';
9759 }
9760
9761 $newstring = $stringtoencode;
9762 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9763 $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.
9764 if ($removelasteolbr) {
9765 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9766 }
9767 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9768 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9769 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9770 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9771 } else {
9772 if ($removelasteolbr) {
9773 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9774 }
9775 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9776 }
9777 // Other substitutions that htmlentities does not do
9778 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9779 return $newstring;
9780}
9781
9789function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9790{
9791 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9792 $ret = preg_replace('/' . "\r\n" . '<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9793 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\r\n" . '/i', "\r\n", $ret);
9794 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\n" . '/i', "\n", $ret);
9795 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9796 return $ret;
9797}
9798
9805function dol_htmlcleanlastbr($stringtodecode)
9806{
9807 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9808 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|' . "\n" . '|' . "\r" . ')+$/i', "", $ret);
9809 return $ret;
9810}
9811
9821function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9822{
9823 $newstring = $a;
9824 if ($keepsomeentities) {
9825 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9826 }
9827 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9828 if ($keepsomeentities) {
9829 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9830 }
9831 return $newstring;
9832}
9833
9845function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9846{
9847 return htmlentities($string, $flags, $encoding, $double_encode);
9848}
9849
9861function dol_string_is_good_iso($s, $clean = 0)
9862{
9863 $len = dol_strlen($s);
9864 $out = '';
9865 $ok = 1;
9866 for ($scursor = 0; $scursor < $len; $scursor++) {
9867 $ordchar = ord($s[$scursor]);
9868 //print $scursor.'-'.$ordchar.'<br>';
9869 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9870 $ok = 0;
9871 break;
9872 } elseif ($ordchar > 126 && $ordchar < 160) {
9873 $ok = 0;
9874 break;
9875 } elseif ($clean) {
9876 $out .= $s[$scursor];
9877 }
9878 }
9879 if ($clean) {
9880 return $out;
9881 }
9882 return $ok;
9883}
9884
9893function dol_nboflines($s, $maxchar = 0)
9894{
9895 if ($s == '') {
9896 return 0;
9897 }
9898 $arraystring = explode("\n", $s);
9899 $nb = count($arraystring);
9900
9901 return $nb;
9902}
9903
9904
9914function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9915{
9916 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9917 if (dol_textishtml($text)) {
9918 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9919 }
9920
9921 $text = strtr($text, $repTable);
9922 if ($charset == 'UTF-8') {
9923 $pattern = '/(<br[^>]*>)/Uu';
9924 } else {
9925 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9926 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9927 }
9928 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9929
9930 $nblines = (int) floor((count($a) + 1) / 2);
9931 // count possible auto line breaks
9932 if ($maxlinesize) {
9933 foreach ($a as $line) {
9934 if (dol_strlen($line) > $maxlinesize) {
9935 //$line_dec = html_entity_decode(strip_tags($line));
9936 $line_dec = html_entity_decode($line);
9937 if (dol_strlen($line_dec) > $maxlinesize) {
9938 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9939 $nblines += substr_count($line_dec, '\n');
9940 }
9941 }
9942 }
9943 }
9944
9945 unset($a);
9946 return $nblines;
9947}
9948
9957function dol_textishtml($msg, $option = 0)
9958{
9959 if (is_null($msg)) {
9960 return false;
9961 }
9962
9963 if ($option == 1) {
9964 if (preg_match('/<(html|link|script)/i', $msg)) {
9965 return true;
9966 } elseif (preg_match('/<body/i', $msg)) {
9967 return true;
9968 } elseif (preg_match('/<\/textarea/i', $msg)) {
9969 return true;
9970 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9971 return true;
9972 } elseif (preg_match('/<br/i', $msg)) {
9973 return true;
9974 }
9975 return false;
9976 } else {
9977 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9978 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9979 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9980 return true;
9981 } elseif (preg_match('/<\/textarea/i', $msg)) {
9982 return true;
9983 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9984 return true;
9985 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9986 return true;
9987 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9988 return true;
9989 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9990 return true;
9991 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9992 return true; // must accept <img src="http://example.com/aaa.png" />
9993 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9994 return true; // must accept <a href="http://example.com/aaa.png" />
9995 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9996 return true;
9997 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9998 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9999 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
10000 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
10001 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
10002 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
10003 return true; // Html entities numbers in hexa
10004 }
10005
10006 return false;
10007 }
10008}
10009
10024function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
10025{
10026 if (!empty($invert)) {
10027 $tmp = $text1;
10028 $text1 = $text2;
10029 $text2 = $tmp;
10030 }
10031
10032 $ret = '';
10033 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
10034 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
10035 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
10036 return $ret;
10037}
10038
10047function dol_concat($text1, $text2)
10048{
10049 return $text1.$text2;
10050}
10051
10065function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
10066{
10067 global $db, $conf, $mysoc, $user, $extrafields;
10068
10069 $substitutionarray = array();
10070
10071 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
10072 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
10073 // this will include signature content first and then replace var found into content of signature
10074 //var_dump($onlykey);
10075 $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()
10076 $usersignature = $user->signature;
10077 $substitutionarray = array_merge($substitutionarray, array(
10078 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
10079 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
10080 ));
10081
10082 if (is_object($user) && ($user instanceof User)) {
10083 $substitutionarray = array_merge($substitutionarray, array(
10084 '__USER_ID__' => (string) $user->id,
10085 '__USER_LOGIN__' => (string) $user->login,
10086 '__USER_EMAIL__' => (string) $user->email,
10087 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
10088 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
10089 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
10090 '__USER_FAX__' => (string) $user->office_fax,
10091 '__USER_LASTNAME__' => (string) $user->lastname,
10092 '__USER_FIRSTNAME__' => (string) $user->firstname,
10093 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
10094 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
10095 '__USER_JOB__' => (string) $user->job,
10096 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
10097 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
10098 ));
10099 }
10100 }
10101 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
10102 $substitutionarray = array_merge($substitutionarray, array(
10103 '__MYCOMPANY_NAME__' => $mysoc->name,
10104 '__MYCOMPANY_EMAIL__' => $mysoc->email,
10105 '__MYCOMPANY_URL__' => $mysoc->url,
10106 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
10107 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
10108 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
10109 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
10110 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
10111 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
10112 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
10113 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
10114 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
10115 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
10116 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
10117 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
10118 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
10119 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
10120 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
10121 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
10122 '__MYCOMPANY_VATNUMBER__' => $mysoc->tva_intra,
10123 '__MYCOMPANY_ZIP__' => $mysoc->zip,
10124 '__MYCOMPANY_TOWN__' => $mysoc->town,
10125 '__MYCOMPANY_STATE__' => $mysoc->state,
10126 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
10127 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
10128 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
10129 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
10130 ));
10131 }
10132
10133 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
10134 if ($onlykey) {
10135 $substitutionarray['__ID__'] = '__ID__';
10136 $substitutionarray['__REF__'] = '__REF__';
10137 $substitutionarray['__NEWREF__'] = '__NEWREF__';
10138 $substitutionarray['__LABEL__'] = '__LABEL__';
10139 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
10140 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
10141 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
10142 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
10143 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
10144
10145 if (isModEnabled("societe")) { // Most objects are concerned
10146 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
10147 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
10148 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
10149 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
10150 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
10151 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
10152 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
10153 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
10154 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
10155 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
10156 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
10157 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
10158 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
10159 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
10160 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
10161 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
10162 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
10163 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
10164 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
10165 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
10166 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
10167 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
10168 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
10169 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
10170 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
10171 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
10172 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
10173 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
10174 }
10175 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
10176 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
10177 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
10178 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
10179 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
10180 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
10181 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
10182 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
10183 }
10184 // add substitution variables for ticket
10185 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
10186 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
10187 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
10188 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
10189 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
10190 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
10191 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
10192 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
10193 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
10194 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
10195 }
10196
10197 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
10198 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
10199 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
10200 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
10201 }
10202 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
10203 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
10204 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
10205 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
10206 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
10207 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
10208 }
10209 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
10210 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
10211 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
10212 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
10213 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
10214 }
10215 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
10216 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
10217 }
10218 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
10219 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
10220 }
10221 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
10222 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
10223 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
10224 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
10225 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
10226 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
10227 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
10228
10229 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
10230 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
10231 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
10232 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
10233 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
10234
10235 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
10236 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
10237 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
10238 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
10239 }
10240 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
10241 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
10242 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
10243 }
10244 } else {
10245 '@phan-var-force Adherent|Delivery $object';
10247 $substitutionarray['__ID__'] = $object->id;
10248 $substitutionarray['__REF__'] = $object->ref;
10249 $substitutionarray['__NEWREF__'] = $object->newref;
10250 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
10251 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10252 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10253 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
10254 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
10255
10256 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
10257 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
10258 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
10259
10260 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
10261 $date_delivery = null;
10262 if (property_exists($object, 'date_delivery')) {
10263 $date_delivery = $object->date_delivery;
10264 } elseif (property_exists($object, 'delivery_date')) {
10265 $date_delivery = $object->delivery_date;
10266 }
10267 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10268 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
10269 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
10270 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
10271 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
10272 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
10273 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
10274 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
10275 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
10276
10277 // For backward compatibility (deprecated)
10278 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10279 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10280
10281 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10282 $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 : '')) : '');
10283 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
10284
10285 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
10286 '@phan-var-force Adherent $object';
10288 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
10289
10290 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
10291 if (method_exists($object, 'getCivilityLabel')) {
10292 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
10293 }
10294 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
10295 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
10296 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
10297 if (method_exists($object, 'getFullName')) {
10298 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
10299 }
10300 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
10301 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
10302 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
10303 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
10304 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
10305 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
10306 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
10307 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
10308 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
10309 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
10310 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
10311 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
10312 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
10313 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
10314 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
10315 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
10316
10317 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
10318 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
10319 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
10320 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
10321 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
10322 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
10323 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
10324 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
10325 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
10326 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
10327 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
10328 }
10329
10330 if (is_object($object) && $object->element == 'societe') {
10332 '@phan-var-force Societe $object';
10333 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
10334 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
10335 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
10336 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
10337 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
10338 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
10339 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
10340 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
10341 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
10342 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
10343 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
10344 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
10345 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
10346 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
10347 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
10348 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ? $object->country_id : '');
10349 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
10350 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
10351 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
10352 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
10353 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
10354 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
10355 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
10356 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
10357 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
10358 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
10359 } elseif (is_object($object) && is_object($object->thirdparty)) {
10360 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
10361 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
10362 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
10363 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
10364 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
10365 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
10366 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
10367 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
10368 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
10369 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
10370 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
10371 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
10372 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
10373 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ? $object->thirdparty->country_id : '');
10374 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
10375 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
10376 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
10377 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
10378 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
10379 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
10380 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
10381 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
10382 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
10383 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
10384 }
10385
10386 if (is_object($object) && $object->element == 'recruitmentcandidature') {
10387 '@phan-var-force RecruitmentCandidature $object';
10389 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
10390 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10391 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10392 }
10393 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
10394 '@phan-var-force ConferenceOrBoothAttendee $object';
10396 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
10397 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10398 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10399 }
10400
10401 if (is_object($object) && $object->element == 'project') {
10402 '@phan-var-force Project $object';
10404 $substitutionarray['__PROJECT_ID__'] = $object->id;
10405 $substitutionarray['__PROJECT_REF__'] = $object->ref;
10406 $substitutionarray['__PROJECT_NAME__'] = $object->title;
10407 } elseif (is_object($object)) {
10408 $project = null;
10409 if (!empty($object->project)) {
10410 $project = $object->project;
10411 }
10412 if (!is_null($project) && is_object($project)) {
10413 $substitutionarray['__PROJECT_ID__'] = $project->id;
10414 $substitutionarray['__PROJECT_REF__'] = $project->ref;
10415 $substitutionarray['__PROJECT_NAME__'] = $project->title;
10416 } else {
10417 // can substitute variables for project : uses lazy load in "make_substitutions" method
10418 $project_id = 0;
10419 if (!empty($object->fk_project) && $object->fk_project > 0) {
10420 $project_id = $object->fk_project;
10421 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
10422 $project_id = $object->fk_project;
10423 }
10424 if ($project_id > 0) {
10425 // path:class:method:id
10426 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10427 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10428 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10429 }
10430 }
10431 }
10432
10433 if (is_object($object) && $object->element == 'facture') {
10434 '@phan-var-force Facture $object';
10436 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
10437 }
10438 if (is_object($object) && $object->element == 'shipping') {
10439 '@phan-var-force Expedition $object';
10441 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
10442 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
10443 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
10444 }
10445 if (is_object($object) && $object->element == 'reception') {
10446 '@phan-var-force Reception $object';
10448 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
10449 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
10450 }
10451
10452 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
10453 '@phan-var-force Contrat $object';
10455 $dateplannedstart = '';
10456 $datenextexpiration = '';
10457 foreach ($object->lines as $line) {
10458 if ($line->date_start > $dateplannedstart) {
10459 $dateplannedstart = $line->date_start;
10460 }
10461 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
10462 $datenextexpiration = $line->date_end;
10463 }
10464 }
10465 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
10466 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
10467 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
10468
10469 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
10470 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
10471 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
10472 }
10473 // add substitution variables for ticket
10474 if (is_object($object) && $object->element == 'ticket') {
10475 '@phan-var-force Ticket $object';
10477 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
10478 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
10479 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
10480 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
10481 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
10482 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
10483 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
10484 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
10485 $userstat = new User($db);
10486 if ($object->fk_user_assign > 0) {
10487 $userstat->fetch($object->fk_user_assign);
10488 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10489 }
10490
10491 if ($object->fk_user_create > 0) {
10492 $userstat->fetch($object->fk_user_create);
10493 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10494 }
10495 }
10496
10497 // Create dynamic tags for __EXTRAFIELD_FIELD__
10498 if ($object->table_element && $object->id > 0) {
10499 if (!is_object($extrafields)) {
10500 $extrafields = new ExtraFields($db);
10501 }
10502 $extrafields->fetch_name_optionals_label($object->table_element, true);
10503
10504 if ($object->fetch_optionals() > 0) {
10505 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
10506 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
10507 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
10508 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_date($object->array_options['options_' . $key], 'day');
10509 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = dol_print_date($object->array_options['options_' . $key], 'day', 'tzserver', $outputlangs);
10510 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = dol_print_date($object->array_options['options_' . $key], 'dayrfc');
10511 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
10512 $datetime = $object->array_options['options_' . $key];
10513 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
10514 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
10515 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
10516 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
10517 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
10518 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_phone($object->array_options['options_' . $key]);
10519 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
10520 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
10521 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATED__'] = price($object->array_options['options_' . $key]); // For compatibility
10522 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATTED__'] = price($object->array_options['options_' . $key]);
10523 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
10524 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = !empty($object->array_options['options_' . $key]) ? $object->array_options['options_' . $key] : '';
10525 }
10526 }
10527 }
10528 }
10529 }
10530
10531 // Complete substitution array with the url to make online payment
10532 if (empty($substitutionarray['__REF__'])) {
10533 $paymenturl = '';
10534 } else {
10535 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
10536 require_once DOL_DOCUMENT_ROOT . '/core/lib/payments.lib.php';
10537 $outputlangs->loadLangs(array('paypal', 'other'));
10538
10539 $amounttouse = 0;
10540 $typeforonlinepayment = 'free';
10541 if (is_object($object) && $object->element == 'commande') {
10542 $typeforonlinepayment = 'order';
10543 }
10544 if (is_object($object) && $object->element == 'facture') {
10545 $typeforonlinepayment = 'invoice';
10546 }
10547 if (is_object($object) && $object->element == 'member') {
10548 $typeforonlinepayment = 'member';
10549 if (!empty($object->last_subscription_amount)) {
10550 $amounttouse = $object->last_subscription_amount;
10551 }
10552 }
10553 if (is_object($object) && $object->element == 'contrat') {
10554 $typeforonlinepayment = 'contract';
10555 }
10556 if (is_object($object) && $object->element == 'fichinter') {
10557 $typeforonlinepayment = 'ficheinter';
10558 }
10559
10560 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], (float) $amounttouse);
10561 $paymenturl = $url;
10562 }
10563
10564 if ($object->id > 0) {
10565 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
10566 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
10567
10568 // Show structured communication
10569 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
10570 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions_be.lib.php';
10571 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
10572 }
10573
10574 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
10575 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10576 } else {
10577 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
10578 }
10579 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
10580 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
10581 } else {
10582 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
10583 }
10584 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
10585 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
10586 } else {
10587 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
10588 }
10589 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
10590 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
10591 } else {
10592 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
10593 }
10594 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
10595 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
10596 } else {
10597 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
10598 }
10599 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
10600 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10601 } else {
10602 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
10603 }
10604
10605 if (is_object($object) && $object->element == 'propal') {
10606 '@phan-var-force Propal $object';
10608 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/comm/propal/card.php?id=" . $object->id;
10609 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10610 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
10611 }
10612 if (is_object($object) && $object->element == 'commande') {
10613 '@phan-var-force Commande $object';
10615 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT . "/commande/card.php?id=" . $object->id;
10616 }
10617 if (is_object($object) && $object->element == 'facture') {
10618 '@phan-var-force Facture $object';
10620 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT . "/compta/facture/card.php?id=" . $object->id;
10621 }
10622 if (is_object($object) && $object->element == 'contrat') {
10623 '@phan-var-force Contrat $object';
10625 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT . "/contrat/card.php?id=" . $object->id;
10626 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10627 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
10628 }
10629 if (is_object($object) && $object->element == 'fichinter') {
10630 '@phan-var-force Fichinter $object';
10632 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT . "/fichinter/card.php?id=" . $object->id;
10633 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10634 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
10635 }
10636 if (is_object($object) && $object->element == 'supplier_proposal') {
10637 '@phan-var-force SupplierProposal $object';
10639 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/supplier_proposal/card.php?id=" . $object->id;
10640 }
10641 if (is_object($object) && $object->element == 'invoice_supplier') {
10642 '@phan-var-force FactureFournisseur $object';
10644 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT . "/fourn/facture/card.php?id=" . $object->id;
10645 }
10646 if (is_object($object) && $object->element == 'payment_supplier') {
10647 '@phan-var-force PaiementFourn $object';
10649 //print_r($object);
10650 $liste_factures = [];
10651 $total = 0;
10652
10653 $sql = 'SELECT f.ref,f.multicurrency_code as f_mccode, pf.*
10654 FROM '.MAIN_DB_PREFIX.'paiementfourn_facturefourn as pf
10655 JOIN '.MAIN_DB_PREFIX.'facture_fourn as f ON pf.fk_facturefourn = f.rowid
10656 WHERE pf.fk_paiementfourn = '.((int) $object->id);
10657
10658 $resql = $db->query($sql);
10659 if ($resql) {
10660 while ($objp = $db->fetch_object($resql)) {
10661 $liste_factures[] = ' - '.$outputlangs->trans('Invoice').' '. $objp->ref.' '.$outputlangs->trans('AmountPayed').' '.price($objp->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $objp->multicurrency_code);
10662 }
10663 }
10664 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_LIST__'] = implode("\n", $liste_factures);
10665 ;
10666 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_TOTAL__'] = price($object->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $object->multicurrency_code ? $object->multicurrency_code : $conf->currency);
10667 }
10668 if (is_object($object) && $object->element == 'shipping') {
10669 '@phan-var-force Expedition $object';
10671 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT . "/expedition/card.php?id=" . $object->id;
10672 }
10673 }
10674
10675 if (is_object($object) && $object->element == 'action') {
10676 '@phan-var-force ActionComm $object';
10678 $substitutionarray['__EVENT_LABEL__'] = $object->label;
10679 $substitutionarray['__EVENT_DESCRIPTION__'] = $object->note;
10680 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action" . $object->type_code);
10681 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
10682 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
10683 $substitutionarray['__EVENT_DATE_TZUSER__'] = dol_print_date($object->datep, 'day', 'tzuserrel', $outputlangs);
10684 $substitutionarray['__EVENT_TIME_TZUSER__'] = dol_print_date($object->datep, 'hour', 'tzuserrel', $outputlangs);
10685 }
10686 }
10687 }
10688
10689 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
10690 '@phan-var-force Facture|FactureRec $object';
10692 include_once DOL_DOCUMENT_ROOT . '/core/lib/functionsnumtoword.lib.php';
10693
10694 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
10695 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
10696 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
10697 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
10698
10699 $already_payed_all = 0;
10700 if (is_object($object) && ($object instanceof Facture)) {
10701 $already_payed_all = $object->totalpaid + $object->totaldeposits + $object->totalcreditnotes;
10702 }
10703
10704 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
10705 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
10706 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
10707
10708 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
10709 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
10710 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
10711
10712 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
10713
10714 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10715 $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)) : '';
10716 $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)) : '';
10717
10718 $mysocuselocaltax1 = false;
10719 $mysocuselocaltax2 = false;
10720 if ($mysoc instanceof Societe && !empty($mysoc->country_code)) {
10721 $tmparray = $mysoc->useLocalTax(-1);
10722 $mysocuselocaltax1 = $tmparray[1];
10723 $mysocuselocaltax2 = $tmparray[2];
10724 }
10725
10726 // Local taxes
10727 if ($onlykey != 2 || $mysocuselocaltax1) {
10728 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
10729 }
10730 if ($onlykey != 2 || $mysocuselocaltax2) {
10731 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
10732 }
10733
10734 // Amount keys formatted in a currency
10735 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10736 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10737 $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) : '';
10738 $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)) : '';
10739 if ($onlykey != 2 || $mysocuselocaltax1) {
10740 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10741 }
10742 if ($onlykey != 2 || $mysocuselocaltax2) {
10743 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10744 }
10745 // Amount keys formatted in a currency (with the typo error for backward compatibility)
10746 if ($onlykey != 2) {
10747 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
10748 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
10749 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
10750 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
10751 if ($mysocuselocaltax1) {
10752 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
10753 }
10754 if ($mysoc->useLocalTax2) {
10755 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
10756 }
10757 }
10758
10759 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
10760 $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) : '';
10761 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
10762 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
10763 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
10764 // TODO Add other keys for foreign multicurrency
10765
10766 // For backward compatibility
10767 if ($onlykey != 2) {
10768 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
10769 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
10770 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10771 }
10772 }
10773
10774
10775 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10776 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
10777
10778 $now = dol_now();
10779
10780 $tmp = dol_getdate($now, true);
10781 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10782 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10783 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10784 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10785
10786 $daytext = $outputlangs->trans('Day' . $tmp['wday']);
10787
10788 $substitutionarray = array_merge($substitutionarray, array(
10789 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10790 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10791 '__DAY__' => (string) $tmp['mday'],
10792 '__DAY_TEXT__' => $daytext, // Monday
10793 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10794 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10795 '__MONTH__' => (string) $tmp['mon'],
10796 '__MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp['mon'])),
10797 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp['mon'])),
10798 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp['mon'])),
10799 '__YEAR__' => (string) $tmp['year'],
10800 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10801 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10802 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10803 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10804 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp3['month'])),
10805 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp3['month'])),
10806 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp3['month'])),
10807 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10808 '__NEXT_DAY__' => (string) $tmp4['day'],
10809 '__NEXT_MONTH__' => (string) $tmp5['month'],
10810 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp5['month'])),
10811 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp5['month'])),
10812 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp5['month'])),
10813 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10814 ));
10815 }
10816
10817 if (isModEnabled('multicompany')) {
10818 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10819 }
10820 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10821 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10822 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10823 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey') . ' (load also language file before)';
10824 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10825 }
10826
10827 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10828
10829 return $substitutionarray;
10830}
10831
10848function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10849{
10850 global $db, $langs;
10851
10852 if (!is_array($substitutionarray)) {
10853 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10854 }
10855
10856 if (empty($outputlangs)) {
10857 $outputlangs = $langs;
10858 }
10859
10860 // Is initial text HTML or simple text ?
10861 $msgishtml = 0;
10862 if (dol_textishtml($text, 1)) {
10863 $msgishtml = 1;
10864 }
10865
10866 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10867 if (is_object($outputlangs)) {
10868 $reg = array();
10869 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10870 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10871 $tmp = explode('|', $reg[1]);
10872 if (!empty($tmp[1])) {
10873 $outputlangs->load($tmp[1]);
10874 }
10875
10876 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10877
10878 if (empty($converttextinhtmlifnecessary)) {
10879 // convert $newval into HTML is necessary
10880 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10881 } else {
10882 if (! $msgishtml) {
10883 $valueishtml = dol_textishtml($value, 1);
10884 //var_dump("valueishtml=".$valueishtml);
10885
10886 if ($valueishtml) {
10887 $text = dol_htmlentitiesbr($text);
10888 $msgishtml = 1;
10889 }
10890 } else {
10891 $value = dol_nl2br((string) $value);
10892 }
10893
10894 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $value, $text);
10895 }
10896 }
10897 }
10898
10899 // Make substitution for constant keys.
10900 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10901 $reg = array();
10902 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10903 $originalkeyfound = $reg[1];
10904 $keyfound = preg_replace('/\|urlencode$/', '', $originalkeyfound);
10905
10906 if (isASecretKey($keyfound)) {
10907 $value = '*****forbidden*****';
10908 } else {
10909 $value = getDolGlobalString($keyfound);
10910 // Execute some functions on value of substitution key
10911 if (preg_match('/\|urlencode$/', $originalkeyfound)) {
10912 $value = urlencode($value);
10913 }
10914 }
10915
10916 if (empty($converttextinhtmlifnecessary)) {
10917 // convert $newval into HTML is necessary
10918 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10919 } else {
10920 if (! $msgishtml) {
10921 $valueishtml = dol_textishtml($value, 1);
10922
10923 if ($valueishtml) {
10924 $text = dol_htmlentitiesbr($text);
10925 $msgishtml = 1;
10926 }
10927 } else {
10928 $value = dol_nl2br((string) $value);
10929 }
10930
10931 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $value, $text);
10932 }
10933 }
10934
10935 // Make substitution for array $substitutionarray
10936 foreach ($substitutionarray as $key => $value) {
10937 if (!isset($value)) {
10938 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10939 }
10940
10941 if (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN') && ($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__')) {
10942 $value = ''; // Protection
10943 }
10944
10945 if (empty($converttextinhtmlifnecessary)) {
10946 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10947 } else {
10948 if (! $msgishtml) {
10949 $valueishtml = dol_textishtml($value, 1);
10950
10951 if ($valueishtml) {
10952 $text = dol_htmlentitiesbr($text);
10953 $msgishtml = 1;
10954 }
10955 } else {
10956 $value = dol_nl2br((string) $value);
10957 }
10958 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10959 }
10960 }
10961
10962 // TODO Implement the lazyload substitution
10963 /*
10964 add a loop to scan $substitutionarray:
10965 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.
10966 If no, we don't need to make replacement, so we do nothing.
10967 If yes, we can make the substitution:
10968
10969 include_once $path;
10970 $tmpobj = new $class($db);
10971 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10972 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10973 */
10974 $memory_object_list = array();
10975 foreach ($substitutionarray as $key => $value) {
10976 $lazy_load_arr = array();
10977 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10978 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10979 $key_to_substitute = $lazy_load_arr[1];
10980 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10981 $param_arr = explode(':', (string) $value);
10982 // path:class:method:id
10983 if (count($param_arr) == 4) {
10984 $path = $param_arr[0];
10985 $class = $param_arr[1];
10986 $method = $param_arr[2];
10987 $id = (int) $param_arr[3];
10988
10989 // load class file and init object list in memory
10990 if (!isset($memory_object_list[$class])) {
10991 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10992 require_once DOL_DOCUMENT_ROOT . $path;
10993 if (class_exists($class)) {
10994 $memory_object_list[$class] = array(
10995 'list' => array(),
10996 );
10997 }
10998 }
10999 }
11000
11001 // fetch object and set substitution
11002 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
11003 if (method_exists($class, $method)) {
11004 if (!isset($memory_object_list[$class]['list'][$id])) {
11005 $tmpobj = new $class($db);
11006 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11007 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
11008 $memory_object_list[$class]['list'][$id] = $tmpobj;
11009 } else {
11010 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
11011 $tmpobj = $memory_object_list[$class]['list'][$id];
11012 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11013 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
11014 }
11015
11016 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
11017 }
11018 }
11019 }
11020 }
11021 }
11022 }
11023 }
11024
11025 return $text;
11026}
11027
11040function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
11041{
11042 global $conf, $user;
11043
11044 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
11045
11046 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
11047
11048 // Check if there is external substitution to do, requested by plugins
11049 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
11050
11051 foreach ($dirsubstitutions as $reldir) {
11052 $dir = dol_buildpath($reldir, 0);
11053
11054 // Check if directory exists
11055 if (!dol_is_dir($dir)) {
11056 continue;
11057 }
11058
11059 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
11060 foreach ($substitfiles as $substitfile) {
11061 $reg = array();
11062 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
11063 $module = $reg[1];
11064
11065 dol_syslog("Library " . $substitfile['name'] . " found into " . $dir);
11066 // Include the user's functions file
11067 require_once $dir . $substitfile['name'];
11068 // Call the user's function, and only if it is defined
11069 $function_name = $module . "_" . $callfunc;
11070 if (function_exists($function_name)) {
11071 $function_name($substitutionarray, $outputlangs, $object, $parameters);
11072 }
11073 }
11074 }
11075 }
11076 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
11077 // to list all tags in odt template
11078 $tags = '';
11079 foreach ($substitutionarray as $key => $value) {
11080 $tags .= '{' . $key . '} => ' . $value . "\n";
11081 }
11082 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
11083 }
11084}
11085
11095function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
11096{
11097 print get_date_range($date_start, $date_end, $format, $outputlangs);
11098}
11099
11110function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
11111{
11112 global $langs;
11113
11114 $out = '';
11115
11116 if (!is_object($outputlangs)) {
11117 $outputlangs = $langs;
11118 }
11119
11120 if ($date_start && $date_end) {
11121 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11122 }
11123 if ($date_start && !$date_end) {
11124 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11125 }
11126 if (!$date_start && $date_end) {
11127 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11128 }
11129
11130 return $out;
11131}
11132
11141function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
11142{
11143 $ret = '';
11144 // If order not defined, we use the setup
11145 if ($nameorder < 0) {
11146 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
11147 }
11148 if ($nameorder == 1) {
11149 $ret .= $firstname;
11150 if ($firstname && $lastname) {
11151 $ret .= ' ';
11152 }
11153 $ret .= $lastname;
11154 } elseif ($nameorder == 2 || $nameorder == 3) {
11155 $ret .= $firstname;
11156 if (empty($ret) && $nameorder == 3) {
11157 $ret .= $lastname;
11158 }
11159 } else { // 0, 4 or 5
11160 $ret .= $lastname;
11161 if (empty($ret) && $nameorder == 5) {
11162 $ret .= $firstname;
11163 }
11164 if ($nameorder == 0) {
11165 if ($firstname && $lastname) {
11166 $ret .= ' ';
11167 }
11168 $ret .= $firstname;
11169 }
11170 }
11171 return $ret;
11172}
11173
11174
11187function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
11188{
11189 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
11190 if (!is_array($mesgs)) {
11191 $mesgs = trim((string) $mesgs);
11192 // If mesgs is a not an empty string
11193 if ($mesgs) {
11194 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
11195 return;
11196 }
11197 if ($attop) {
11198 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11199 } else {
11200 $_SESSION['dol_events'][$style][] = $mesgs;
11201 }
11202 }
11203 } else {
11204 // If mesgs is an array
11205 foreach ($mesgs as $mesg) {
11206 $mesg = trim((string) $mesg);
11207 if ($mesg) {
11208 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
11209 return;
11210 }
11211 if ($attop) {
11212 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11213 } else {
11214 $_SESSION['dol_events'][$style][] = $mesg;
11215 }
11216 }
11217 }
11218 }
11219}
11220
11234function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
11235{
11236 if (empty($mesg) && empty($mesgs)) {
11237 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
11238 } else {
11239 if ($messagekey) {
11240 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
11241 // TODO
11242 $mesg .= '';
11243 }
11244 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE" . $messagekey])) {
11245 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
11246 dol_print_error(null, 'Bad parameter style=' . $style . ' for setEventMessages');
11247 }
11248 if (empty($mesgs)) {
11249 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
11250 } else {
11251 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
11252 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
11253 }
11254 setEventMessage($mesgs, $style, $noduplicate, $attop);
11255 }
11256 }
11257 }
11258}
11259
11269function dol_htmloutput_events($disabledoutputofmessages = 0)
11270{
11271 // Show mesgs
11272 if (isset($_SESSION['dol_events']['mesgs'])) {
11273 if (empty($disabledoutputofmessages)) {
11274 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
11275 }
11276 unset($_SESSION['dol_events']['mesgs']);
11277 }
11278 // Show errors
11279 if (isset($_SESSION['dol_events']['errors'])) {
11280 if (empty($disabledoutputofmessages)) {
11281 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
11282 }
11283 unset($_SESSION['dol_events']['errors']);
11284 }
11285
11286 // Show warnings
11287 if (isset($_SESSION['dol_events']['warnings'])) {
11288 if (empty($disabledoutputofmessages)) {
11289 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
11290 }
11291 unset($_SESSION['dol_events']['warnings']);
11292 }
11293}
11294
11309function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
11310{
11311 global $conf, $langs;
11312
11313 $ret = 0;
11314 $return = '';
11315 $out = '';
11316 $divstart = $divend = '';
11317
11318 // If inline message with no format, we add it.
11319 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || defined('DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
11320 $divstart = '<div class="' . $style . ' clearboth">';
11321 $divend = '</div>';
11322 }
11323
11324 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
11325 $langs->load("errors");
11326 $out .= $divstart;
11327 if (is_array($mesgarray) && count($mesgarray)) {
11328 foreach ($mesgarray as $message) {
11329 $ret++;
11330 $out .= $langs->trans($message);
11331 if ($ret < count($mesgarray)) {
11332 $out .= "<br>\n";
11333 }
11334 }
11335 }
11336 if ($mesgstring) {
11337 $ret++;
11338 $out .= $langs->trans($mesgstring);
11339 }
11340 $out .= $divend;
11341 }
11342
11343 if ($out) {
11344 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
11345 $return = '<script nonce="' . getNonce() . '">
11346 $(document).ready(function() {
11347 /* jnotify(message, preset of message type, keepmessage) */
11348 $.jnotify("' . dol_escape_js($out) . '", "' . ($style == "ok" ? 3000 : $style) . '", ' . ($style == "ok" ? "false" : "true") . ',{ remove: function (){} } );
11349 });
11350 </script>';
11351 } else {
11352 $return = $out;
11353 }
11354 }
11355
11356 return $return;
11357}
11358
11370function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11371{
11372 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11373}
11374
11388function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
11389{
11390 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
11391 return;
11392 }
11393
11394 $iserror = 0;
11395 $iswarning = 0;
11396 if (is_array($mesgarray)) {
11397 foreach ($mesgarray as $val) {
11398 if ($val && preg_match('/class="error"/i', $val)) {
11399 $iserror++;
11400 break;
11401 }
11402 if ($val && preg_match('/class="warning"/i', $val)) {
11403 $iswarning++;
11404 break;
11405 }
11406 }
11407 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
11408 $iserror++;
11409 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
11410 $iswarning++;
11411 }
11412 if ($style == 'error' || $style == 'errors') {
11413 $iserror++;
11414 }
11415 if ($style == 'warning' || $style == 'warnings') {
11416 $iswarning++;
11417 }
11418
11419 if ($iserror || $iswarning) {
11420 // Remove div from texts
11421 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
11422 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
11423 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
11424 // Remove div from texts array
11425 if (is_array($mesgarray)) {
11426 $newmesgarray = array();
11427 foreach ($mesgarray as $val) {
11428 if (is_string($val)) {
11429 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
11430 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
11431 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
11432 $newmesgarray[] = $tmpmesgstring;
11433 } else {
11434 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
11435 }
11436 }
11437 $mesgarray = $newmesgarray;
11438 }
11439 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
11440 } else {
11441 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
11442 }
11443}
11444
11456function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11457{
11458 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11459}
11460
11481function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
11482{
11483 // Clean parameters
11484 $order = strtolower($order);
11485
11486 if (is_array($array)) {
11487 $sizearray = count($array);
11488 if ($sizearray > 0) {
11489 // Build a temp array with sorting key as value
11490 $temp = array();
11491 foreach (array_keys($array) as $key) {
11492 $tmpmultikey = explode(',', $index);
11493 $newindex = $tmpmultikey[0];
11494 if (is_object($array[$key])) {
11495 $temp[$key] = empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex;
11496 // Add other keys
11497 if (!empty($tmpmultikey[1])) {
11498 $newindex = $tmpmultikey[1];
11499 $temp[$key] .= '__' . (empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex);
11500 }
11501 } else {
11502 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11503 $temp[$key] = empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex];
11504 // Add other keys
11505 if (!empty($tmpmultikey[1])) {
11506 $newindex = $tmpmultikey[1];
11507 // @phan-suppress-next-line PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11508 $temp[$key] .= '__' . (empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex]);
11509 }
11510 }
11511 if ($natsort == -1) {
11512 $temp[$key] = '___' . $temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
11513 }
11514 }
11515 if (empty($natsort) || $natsort == -1) {
11516 if ($order == 'asc') {
11517 asort($temp);
11518 } else {
11519 arsort($temp);
11520 }
11521 } else {
11522 if ($case_sensitive) {
11523 natsort($temp);
11524 } else {
11525 natcasesort($temp); // natecasesort is not sensible to case
11526 }
11527 if ($order != 'asc') {
11528 $temp = array_reverse($temp, true);
11529 }
11530 }
11531
11532 $sorted = array();
11533
11534 foreach (array_keys($temp) as $key) {
11535 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
11536 }
11537
11538 return $sorted;
11539 }
11540 }
11541 return $array;
11542}
11543
11544
11552function utf8_check($str)
11553{
11554 $str = (string) $str; // Sometimes string is an int.
11555
11556 // We must use here a binary strlen function (so not dol_strlen)
11557 $strLength = strlen($str);
11558 for ($i = 0; $i < $strLength; $i++) {
11559 if (ord($str[$i]) < 0x80) {
11560 continue; // 0bbbbbbb
11561 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
11562 $n = 1; // 110bbbbb
11563 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
11564 $n = 2; // 1110bbbb
11565 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
11566 $n = 3; // 11110bbb
11567 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
11568 $n = 4; // 111110bb
11569 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
11570 $n = 5; // 1111110b
11571 } else {
11572 return false; // Does not match any model
11573 }
11574 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
11575 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
11576 return false;
11577 }
11578 }
11579 }
11580 return true;
11581}
11582
11590function utf8_valid($str)
11591{
11592 /* 2 other methods to test if string is utf8
11593 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
11594 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
11595 */
11596 return preg_match('//u', $str) ? true : false;
11597}
11598
11599
11606function ascii_check($str)
11607{
11608 if (function_exists('mb_check_encoding')) {
11609 //if (mb_detect_encoding($str, 'ASCII', true) return false;
11610 if (!mb_check_encoding($str, 'ASCII')) {
11611 return false;
11612 }
11613 } else {
11614 if (preg_match('/[^\x00-\x7f]/', $str)) {
11615 return false; // Contains a byte > 7f
11616 }
11617 }
11618
11619 return true;
11620}
11621
11622
11630function dol_osencode($str)
11631{
11632 $tmp = ini_get("unicode.filesystem_encoding");
11633 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
11634 $tmp = 'iso-8859-1'; // By default for windows
11635 }
11636 if (empty($tmp)) {
11637 $tmp = 'utf-8'; // By default for other
11638 }
11639 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
11640 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
11641 }
11642
11643 if ($tmp == 'iso-8859-1') {
11644 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
11645 }
11646 return $str;
11647}
11648
11649
11665function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
11666{
11667 global $conf;
11668
11669 // If key empty
11670 if ($key == '') {
11671 return 0;
11672 }
11673
11674 // Check in cache
11675 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
11676 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
11677 }
11678
11679 dol_syslog('dol_getIdFromCode (value for field ' . $fieldid . ' from key ' . $key . ' not found into cache)', LOG_DEBUG);
11680
11681 $sql = "SELECT " . $fieldid . " as valuetoget";
11682 $sql .= " FROM " . MAIN_DB_PREFIX . $tablename;
11683 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
11684 $sql .= " WHERE " . $fieldkey . " = " . ((int) $key);
11685 } else {
11686 $sql .= " WHERE " . $fieldkey . " = '" . $db->escape($key) . "'";
11687 }
11688 if (!empty($entityfilter)) {
11689 $sql .= " AND entity IN (" . getEntity($tablename) . ")";
11690 }
11691 if ($filters) {
11692 $sql .= $filters;
11693 }
11694
11695 $resql = $db->query($sql);
11696 if ($resql) {
11697 $obj = $db->fetch_object($resql);
11698 $valuetoget = '';
11699 if ($obj) {
11700 $valuetoget = $obj->valuetoget;
11701 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
11702 } else {
11703 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
11704 }
11705 $db->free($resql);
11706
11707 return $valuetoget;
11708 } else {
11709 return -1;
11710 }
11711}
11712
11722function isStringVarMatching($var, $regextext, $matchrule = 1)
11723{
11724 // Tolerate callers (custom modules, older code) that already pass a full regex with delimiters
11725 // like '/^(aaa|bbb)/' instead of the bare body. Without this, the function would build
11726 // '/^/^(aaa|bbb)//' which trips preg_match() with 'Unknown modifier ^'.
11727 $regextext = preg_replace('#^/\^?#', '', (string) $regextext);
11728 $regextext = preg_replace('#\$?/[imsxuADSUXJ]*$#', '', $regextext);
11729
11730 if ($matchrule == 1) {
11731 if ($var == 'mainmenu') {
11732 global $mainmenu;
11733 return (preg_match('/^' . $regextext . '/', $mainmenu));
11734 } elseif ($var == 'leftmenu') {
11735 global $leftmenu;
11736 return (preg_match('/^' . $regextext . '/', $leftmenu));
11737 } else {
11738 return 'This variable is not accessible with dol_eval';
11739 }
11740 } else {
11741 return 'This value '.$matchrule.' for param $matchrule is not yet implemented';
11742 }
11743}
11744
11745
11755function verifCond($strToEvaluate, $onlysimplestring = '1')
11756{
11757 //print $strToEvaluate."<br>\n";
11758 $rights = true;
11759 if (isset($strToEvaluate) && $strToEvaluate !== '') {
11760 //var_dump($strToEvaluate);
11761 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
11762 $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
11763 //var_dump($strToEvaluate, $rep);
11764 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
11765 //var_dump($rights);
11766 }
11767 return $rights;
11768}
11769
11784function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11785{
11786 if ($returnvalue != 1) {
11787 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
11788 }
11789
11790 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
11791 return dol_eval_new($s);
11792 } else {
11793 return dol_eval_standard($s, $hideerrors, $onlysimplestring);
11794 }
11795}
11796
11806function dol_eval_new($s)
11807{
11808 // Only this global variables can be read by eval function and returned to caller
11809 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11810 $db, $langs, $user, $website, $websitepage,
11811 $action, $mainmenu, $leftmenu,
11812 $mysoc,
11813 $objectoffield, // To allow the use of $objectoffield in computed fields
11814
11815 // Old variables used
11816 $object,
11817 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11818
11819 // PHP < 7.4.0
11820 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11821 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11822
11823 // PHP < 8.0.0
11824 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11825 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11826 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11827 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11828 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11829
11830 // PHP < 8.1.0
11831 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11832 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11833 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11834 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11835
11836 // PHP < 8.4.0
11837 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11838 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11839 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11840
11841 $prohibited_token_ids = [
11842 /*
11843 * Prohibited int tokens
11844 */
11845
11846 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11847 'T_ABSTRACT',
11848 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
11849 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
11850 'T_ATTRIBUTE',
11851 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11852 'T_BAD_CHARACTER',
11853 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11854 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11855 'T_CALLABLE',
11856 'T_CATCH',
11857 'T_CLASS',
11858 'T_CLOSE_TAG',
11859 'T_CONST',
11860 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11861 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11862 'T_DECLARE',
11863 'T_DIR',
11864 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11865 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11866 'T_ECHO',
11867 'T_ENDDECLARE',
11868 'T_ENUM',
11869 'T_EVAL',
11870 'T_EXIT',
11871 'T_EXTENDS',
11872 // 'T_FOR', 'T_FOREACH',
11873 'T_FILE',
11874 'T_FINAL',
11875 'T_FINALLY',
11876 'T_FN',
11877 'T_FUNCTION',
11878 'T_FUNC_C',
11879 'T_GLOBAL',
11880 'T_GOTO',
11881 'T_HALT_COMPILER',
11882 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11883 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11884 'T_IMPLEMENTS',
11885 'T_INCLUDE',
11886 'T_INCLUDE_ONCE',
11887 'T_INSTEADOF',
11888 'T_INTERFACE',
11889 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11890 'T_LINE',
11891 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11892 'T_METHOD_C',
11893 // 'T_NEW',
11894 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11895 'T_NAMESPACE',
11896 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11897 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11898 'T_OPEN_TAG',
11899 'T_OPEN_TAG_WITH_ECHO',
11900 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11901 'T_PRINT',
11902 'T_PRIVATE',
11903 'T_PROTECTED',
11904 'T_PUBLIC',
11905 // 'T_PROPERTY_C',
11906 'T_READONLY',
11907 'T_REQUIRE',
11908 'T_REQUIRE_ONCE',
11909 'T_RETURN',
11910 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11911 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11912 'T_STATIC',
11913 'T_THROW',
11914 'T_TRAIT',
11915 'T_TRAIT_C',
11916 'T_TRY',
11917 'T_UNSET',
11918 'T_UNSET_CAST',
11919 'T_USE',
11920 // 'T_VARIABLE',
11921 'T_VAR',
11922 // 'T_WHILE', 'T_WHITESPACE',
11923 // 'T_XOR_EQUAL',
11924 // 'T_YIELD', 'T_YIELD_FROM',
11925
11926 /*
11927 * Prohibited string tokens
11928 */
11929 ';',
11930 '`',
11931 ];
11932
11933 $prohibited_variables = [
11934 '$_COOKIE',
11935 '$_ENV',
11936 '$_FILES',
11937 '$GLOBALS',
11938 '$_GET',
11939 '$_POST',
11940 '$_REQUEST',
11941 '$_SERVER',
11942 '$_SESSION',
11943 ];
11944
11945 $prohibited_functions = [
11946 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11947 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11948 'override_function',
11949 'session_id',
11950 'session_create_id',
11951 'session_regenerate_id',
11952 'call_user_func',
11953 'call_user_func_array', // PREVENT calling forbidden functions
11954 'exec',
11955 'passthru',
11956 'shell_exec',
11957 'system',
11958 'proc_open',
11959 'popen',
11960 'dol_eval',
11961 'dol_eval_new',
11962 'dol_eval_standard',
11963 'dol_contctdesc',
11964 'executeCLI',
11965 'verifCond',
11966 'GETPOST', // Native Dolibarr functions
11967 'create_function',
11968 'assert',
11969 'mb_ereg_replace',
11970 'mb_eregi_replace', // function with eval capabilities
11971 'dol_compress_dir',
11972 'dol_decode',
11973 'dol_delete_file',
11974 'dol_delete_dir',
11975 'dol_delete_dir_recursive',
11976 'dol_copy',
11977 'archiveOrBackupFile', // more dolibarr functions
11978 'fopen',
11979 'file_put_contents',
11980 'fputs',
11981 'fputscsv',
11982 'fwrite',
11983 'fpassthru',
11984 'mkdir',
11985 'rmdir',
11986 'symlink',
11987 'touch',
11988 'unlink',
11989 'umask', // PHP functions related to file operations
11990 'invoke',
11991 'invokeArgs', // Method of ReflectionFunction to execute a function
11992 'filter_input',
11993 'filter_input_array',
11994 'GETPOST', // PREVENT CODE INJECTION
11995 ];
11996
11997 $prohibited_token_arrangements = [
11998 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11999 ' T_VARIABLE ( ',
12000 ' " ( ',
12001 ' \' ( ',
12002 ' T_CONSTANT_ENCAPSED_STRING ( ',
12003 ' ) ( ',
12004 ];
12005
12006 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
12007
12008 $tokens_arrangement = ' ';
12009
12010 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
12011 if (is_array($tokens[$i])) {
12012 $token_id = $tokens[$i][0];
12013 $token_value = $tokens[$i][1];
12014 $token_name = token_name($tokens[$i][0]);
12015 } else {
12016 $token_id = $tokens[$i];
12017 $token_value = $tokens[$i];
12018 $token_name = $tokens[$i];
12019 }
12020
12021 // Ignore whitespaces
12022 if (T_WHITESPACE === $token_id) {
12023 continue;
12024 }
12025
12026 // Keep history to check arrangements
12027 $tokens_arrangement .= "{$token_name} ";
12028
12029 // Prohibited Variables
12030 if (
12031 T_VARIABLE === $token_id
12032 && in_array($token_value, $prohibited_variables, true)
12033 ) {
12034 return "« {$token_value} » is prohibited in « {$s} »";
12035 }
12036
12037 // Prohibited Functions
12038 if (
12039 T_STRING === $token_id
12040 && in_array($token_value, $prohibited_functions, true)
12041 ) {
12042 return "« {$token_value} » is prohibited in « {$s} »";
12043 }
12044 }
12045
12046 // Prohibited Token IDs
12047 $maxi = count($prohibited_token_ids);
12048 for ($i = 0; $i < $maxi; ++$i) {
12049 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
12050 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
12051 }
12052 }
12053
12054 // Prohibited token arrangements
12055 $maxi = count($prohibited_token_arrangements);
12056 for ($i = 0; $i < $maxi; ++$i) {
12057 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
12058 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
12059 }
12060 }
12061
12062 // Return result
12063 try {
12064 return @eval("return {$s};") ?? '';
12065 } catch (Throwable $ex) {
12066 return "Exception during evaluation: " . $s . " - " . $ex->getMessage();
12067 }
12068}
12069
12083function dol_eval_standard($s, $hideerrors = 1, $onlysimplestring = '1')
12084{
12085 // Only this global variables can be read by eval function and returned to caller
12086 // The less we have, the better it is.
12087
12088 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()
12089 global $db, $langs, $user, $website, $websitepage;
12090 global $action, $mainmenu, $leftmenu;
12091 global $mysoc;
12092 global $objectoffield; // To allow the use of $objectoffield in computed fields
12093 global $object;
12094
12095 // Old variables (deprecated)
12096 if (getDolGlobalString('MAIN_ALLOW_OLD_VAR_OBJ_IN_DOL_EVAL')) {
12097 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $objectoffield
12098 }
12099
12100 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
12101 if ($onlysimplestring == '0') { // '0' is deprecated, we process it as the more secured '1'
12102 $onlysimplestring = '1';
12103 }
12104 if (!in_array($onlysimplestring, array('1', '2'))) {
12105 return "Bad call of dol_eval. Parameter onlysimplestring must be '1' or '2'.";
12106 }
12107 if (!is_scalar($s)) {
12108 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
12109 }
12110
12111 try {
12112 global $dolibarr_main_restrict_eval_methods;
12113
12114 // Set $dolibarr_main_restrict_eval_methods_array
12115 if (!isset($dolibarr_main_restrict_eval_methods)) {
12116 $dolibarr_main_restrict_eval_methods = 'getDolGlobalString, getDolGlobalInt, getDolCurrency, getDolEntity, getDolDBType, fetchNoCompute, hasRight, isAdmin, isModEnabled, isStringVarMatching, abs, min, max, round, dol_now, preg_match';
12117 }
12118 //print '$dolibarr_main_restrict_eval_methods = '.$dolibarr_main_restrict_eval_methods."\n";
12119 $dolibarr_main_restrict_eval_methods_array = explode(',', str_replace(" ", "", $dolibarr_main_restrict_eval_methods));
12120
12121 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
12122 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
12123 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$objectoffield->canvas == "patient@cabinetmed"'
12124 // 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"
12125
12126 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
12127 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
12128 if ($onlysimplestring == '2') {
12129 $specialcharsallowed .= '<[]'; // Later we check that < has space before and after
12130 }
12131 global $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12132 if (!empty($dolibarr_main_allow_unsecured_special_chars_in_dol_eval)) {
12133 $specialcharsallowed .= (string) $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12134 }
12135 if (preg_match('/[^a-z0-9\s' . preg_quote($specialcharsallowed, '/') . ']/i', $s)) {
12136 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): ' . $s;
12137 }
12138
12139 // Check if we found a | without a space before and after
12140 /* Disabled to allow preg_match('/(AAA|BBB)/')
12141 $tmps = str_replace(' || ', '__XXX__', $s);
12142 if (strpos($tmps, '|') !== false) {
12143 return 'Bad string syntax to evaluate (The char | can be used only when duplicated || with a space before and after): ' . $s;
12144 }
12145 */
12146
12147 // Check if there is PHP comments (can be used to obfuscate code)
12148 if (strpos($s, '/*') !== false || strpos($s, '//') !== false) {
12149 return 'Bad string syntax to evaluate (The comment string /* and // are not allowed): ' . $s;
12150 }
12151
12152 // Check if we found a ? without a space before and after
12153 $tmps = str_replace(' ? ', '__XXX__', $s);
12154 if (strpos($tmps, '?') !== false) {
12155 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): ' . $s;
12156 }
12157
12158 // Check if there is a < or <= without spaces after
12159 if (preg_match('/<=?[^\s]/', $s)) {
12160 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found a < or <= without space after): ' . $s;
12161 }
12162
12163 // Check if there is dynamic call (first we use black list patterns)
12164 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
12165 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;
12166 }
12167
12168 if (empty($dolibarr_main_restrict_eval_methods)) {
12169 // If $dolibarr_main_restrict_eval_methods was set to '', we must check if we try dynamic call
12170
12171 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
12172 $savescheck = '';
12173 $scheck = $s;
12174 while ($scheck && $savescheck != $scheck) {
12175 $savescheck = $scheck;
12176 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
12177 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
12178 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
12179 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12180 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12181 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
12182 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
12183 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
12184 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
12185 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
12186 }
12187 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
12188
12189 // Now test if it remains 1 open parenthesis.
12190 if (strpos($scheck, '(') !== false) {
12191 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found call of a function or method without using the direct name of the function): ' . $s;
12192 }
12193 }
12194
12195 if (strpos($s, '`') !== false) {
12196 return 'Bad string syntax to evaluate (backtick char is forbidden): ' . $s;
12197 }
12198
12199 // Disallow also concat operator
12200 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) {
12201 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
12202 return 'Bad string syntax to evaluate (dot char is forbidden if not strictly between 2 numbers): ' . $s;
12203 }
12204 }
12205
12206 // We exclude string using a $ character that are not an expected global or temporary vars, so that are not:
12207 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $var....
12208 $savescheck = '';
12209 $scheck = $s;
12210 while ($scheck && $savescheck != $scheck) {
12211 $savescheck = $scheck;
12212 $scheck = preg_replace('/\$conf->[a-z\_]+->enabled/', '__VARCONFENABLED__', $scheck); // Remove this once $user->module->enabled has been replaced everywhere with isModEnabled.
12213 $scheck = preg_replace('/\$user->id/', '__VARUSERID__', $scheck);
12214 $scheck = preg_replace('/\$user->hasRight/', '__VARUSERHASRIGHT__', $scheck);
12215 $scheck = preg_replace('/\$user->rights/', '__VARUSERHASRIGHT__', $scheck); // Remove this once $user->rights->xxx is replaced everywhere with $user->hasRight()
12216 $scheck = preg_replace('/\$user->admin/', '__VARUSERISADMIN__', $scheck); // Remove this once $user->admin is replaced everywhere with $user->isAdmin()
12217 $scheck = preg_replace('/\‍(\$db\‍)/', '__VARDB__', $scheck);
12218 $scheck = preg_replace('/\$langs/', '__VARLANGSTRANS__', $scheck);
12219 $scheck = preg_replace('/\$mysoc/', '__VARMYSOC__', $scheck);
12220 $scheck = preg_replace('/\$action/', '__VARACTION__', $scheck);
12221 $scheck = preg_replace('/\$mainmenu/', '__VARMAINMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12222 $scheck = preg_replace('/\$leftmenu/', '__VARLEFTMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12223 $scheck = preg_replace('/\$websitepage/', '__VARWEBSITEPAGE__', $scheck);
12224 $scheck = preg_replace('/\$website/', '__VARWEBSITE__', $scheck);
12225 $scheck = preg_replace('/\$objectoffield/', '__VAROBJECTOFFIELD__', $scheck);
12226 $scheck = preg_replace('/\$object/', '__VAROBJECT__', $scheck);
12227 $scheck = preg_replace('/\$var/', '__VARVAR__', $scheck);
12228
12229 // deprecated (now we use $objecf->canvas or $objectoffield->canvas)
12230 $scheck = preg_replace('/\$soc->canvas/', '__VARSOCCANVAS__', $scheck);
12231 $scheck = preg_replace('/\$obj->canvas/', '__VAROBJCANVAS__', $scheck);
12232
12233 // Now test if it remains one '$'
12234 if (strpos($scheck, '$') !== false) {
12235 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);
12236 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;
12237 }
12238 }
12239
12240 // We block use of php exec or php file functions
12241 $forbiddenphpstrings = array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction', 'SplFileObject', 'SplTempFileObject');
12242
12243 if (empty($dolibarr_main_restrict_eval_methods)) { // If forced to ''
12244 // 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)
12245 // 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
12246 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
12247 $forbiddenphpfunctions = array();
12248 $forbiddenphpmethods = array();
12249
12250 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
12251 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
12252 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
12253
12254 $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"));
12255 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
12256 $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"));
12257 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
12258 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
12259
12260 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
12261
12262 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
12263 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
12264 $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"));
12265 $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", ));
12266 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("putenv", "dl", "apache_child_terminate", "apache_setenv"));
12267 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("posix_kill", "posix_setuid", "posix_setgid"));
12268 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
12269 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
12270 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
12271 $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
12272 $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"));
12273 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
12274 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) { // We disallow all function that allow to obfuscate the real name of a function
12275 // @phpcs:ignore
12276 $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
12277 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concat", "dol_concatdesc")); // native dolibarr functions
12278 }
12279 // Remove from blacklist the function that are into the whitelist
12280 /*foreach ($forbiddenphpfunctions as $key => $forbiddenphpfunction) {
12281 if (in_array($forbiddenphpfunction, $dolibarr_main_restrict_eval_methods_array)) {
12282 unset($forbiddenphpfunctions[$key]);
12283 }
12284 }*/
12285
12286 $forbiddenphpmethods = array_merge($forbiddenphpmethods, array('invoke', 'invokeArgs')); // Methods of ReflectionFunction to execute a function
12287 // Remove from blacklist the function that are into the whitelist
12288 /*foreach ($forbiddenphpmethods as $key => $forbiddenphpmethod) {
12289 if (in_array($forbiddenphpmethod, $dolibarr_main_restrict_eval_methods_array)) {
12290 unset($forbiddenphpmethods[$key]);
12291 }
12292 }*/
12293
12294 $forbiddenphpregex = 'global\s*\$';
12295 $forbiddenphpregex .= '|';
12296 $forbiddenphpregex .= '\b(' . implode('|', $forbiddenphpfunctions) . ')\b';
12297
12298 $forbiddenphpmethodsregex = '->(' . implode('|', $forbiddenphpmethods) . ')';
12299
12300 // Now scan all forbidden patterns
12301 do {
12302 $oldstringtoclean = $s;
12303 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12304 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12305 $s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12306 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12307 } while ($oldstringtoclean != $s);
12308
12309 if (strpos($s, '__forbiddenstring__') !== false) {
12310 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12311 return 'Bad string syntax to evaluate: ' . $s;
12312 }
12313 }
12314
12315 if (!empty($dolibarr_main_restrict_eval_methods)) {
12316 // Accept only white-listed allowed function and classes
12317 // TODO Get all pattern '/([\s\w]+)\‍(/', then check that $reg[1] is a defined class or a function into a given list
12318 $pattern = '/([\s\w\'\]\"]+)\‍(/';
12319
12320 $matches = array();
12321 preg_match_all($pattern, $s, $matches);
12322
12323 if (count($matches)) {
12324 foreach ($matches[1] as $m) {
12325 $m = trim($m);
12326 if (empty($m)) {
12327 continue;
12328 }
12329 $reg = array();
12330 if (!preg_match('/new ([A-Z][\w]+)/i', $m, $reg)) {
12331 if (!in_array($m, $dolibarr_main_restrict_eval_methods_array)) {
12332 if ($m != "'" && $m != '"') {
12333 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12334 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;
12335 }
12336 }
12337 } else {
12338 if (!class_exists($reg[1])) {
12339 dol_syslog('Bad string syntax to evaluate: Class "'.$reg[1].'" does not exist. ' . $s, LOG_WARNING);
12340 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" does not exist. ' . $s;
12341 }
12342 $parents = class_parents($reg[1]); // Get list of parent classes of class we want to check
12343 if (!in_array('CommonObject', $parents)) { // Only classes that inherit CommonObject are ok. This forbid dangerous classes like ReflectionFunction, SplFileObject, ...
12344 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);
12345 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" is not allowed because only classes extended CommonObject can be used in dynamic evaluation. ' . $s;
12346 }
12347 }
12348 }
12349 }
12350
12351 $forbiddenphpregex = 'global\s*\$';
12352 $forbiddenphpregex .= '|'; // or
12353 $forbiddenphpregex .= '}\s*\[';
12354 $forbiddenphpregex .= '|'; // or
12355 $forbiddenphpregex .= '\‍)\s*\‍(';
12356
12357 // Now scan all forbidden patterns
12358 do {
12359 $oldstringtoclean = $s;
12360 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12361 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12362 //$s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12363 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12364 } while ($oldstringtoclean != $s);
12365
12366 if (strpos($s, '__forbiddenstring__') !== false) {
12367 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12368 return 'Bad string syntax to evaluate: ' . $s;
12369 }
12370 }
12371
12372 //print $s."<br>\n";
12373 ob_start(); // An evaluation has no reason to output data
12374 $isObBufferActive = true;
12375 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
12376 $tmpo = ob_get_clean();
12377 $isObBufferActive = false;
12378 if ($tmpo) {
12379 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
12380 }
12381 return $tmps;
12382 } catch (Error $e) {
12383 if ($isObBufferActive) {
12384 // Clean up buffer which was left behind due to exception.
12385 $tmpo = ob_get_clean();
12386 $isObBufferActive = false;
12387 }
12388 $error = 'dol_eval try/catch error : ';
12389 $error .= $e->getMessage();
12390 dol_syslog($error, LOG_WARNING);
12391 return 'Exception during evaluation: ' . $s;
12392 }
12393}
12394
12402function dol_validElement($element)
12403{
12404 return (trim($element) != '');
12405}
12406
12415function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
12416{
12417 if (empty($codelang)) {
12418 return '';
12419 }
12420
12421 if ($codelang == 'auto') {
12422 return '<span class="fa fa-language"></span>';
12423 }
12424
12425 $langtocountryflag = array(
12426 'ar_AR' => '',
12427 'ca_ES' => 'catalonia',
12428 'da_DA' => 'dk',
12429 'fr_CA' => 'mq',
12430 'sv_SV' => 'se',
12431 'sw_SW' => 'unknown',
12432 'AQ' => 'unknown',
12433 'CW' => 'unknown',
12434 'IM' => 'unknown',
12435 'JE' => 'unknown',
12436 'MF' => 'unknown',
12437 'BL' => 'unknown',
12438 'SX' => 'unknown'
12439 );
12440
12441 if (isset($langtocountryflag[$codelang])) {
12442 $flagImage = $langtocountryflag[$codelang];
12443 } else {
12444 $tmparray = explode('_', $codelang);
12445 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
12446 }
12447
12448 $morecss = '';
12449 $reg = array();
12450 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
12451 $morecss = $reg[1];
12452 $moreatt = "";
12453 }
12454
12455 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
12456 return '<span class="flag-sprite ' . strtolower($flagImage) . ($morecss ? ' ' . $morecss : '') . '"' . ($moreatt ? ' ' . $moreatt : '') . (!$notitlealt ? ' title="' . $codelang . '"' : '') . '></span>';
12457}
12458
12466function getLanguageCodeFromCountryCode($countrycode)
12467{
12468 global $mysoc;
12469
12470 if (empty($countrycode)) {
12471 return null;
12472 }
12473
12474 if (strtoupper($countrycode) == 'MQ') {
12475 return 'fr_CA';
12476 }
12477 if (strtoupper($countrycode) == 'SE') {
12478 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
12479 }
12480 if (strtoupper($countrycode) == 'CH') {
12481 if ($mysoc->country_code == 'FR') {
12482 return 'fr_CH';
12483 }
12484 if ($mysoc->country_code == 'DE') {
12485 return 'de_CH';
12486 }
12487 if ($mysoc->country_code == 'IT') {
12488 return 'it_CH';
12489 }
12490 }
12491
12492 // Locale list taken from:
12493 // http://stackoverflow.com/questions/3191664/
12494 // list-of-all-locales-and-their-short-codes
12495 $locales = array(
12496 'af-ZA',
12497 'am-ET',
12498 'ar-AE',
12499 'ar-BH',
12500 'ar-DZ',
12501 'ar-EG',
12502 'ar-IQ',
12503 'ar-JO',
12504 'ar-KW',
12505 'ar-LB',
12506 'ar-LY',
12507 'ar-MA',
12508 'ar-OM',
12509 'ar-QA',
12510 'ar-SA',
12511 'ar-SY',
12512 'ar-TN',
12513 'ar-YE',
12514 //'as-IN', // Moved after en-IN
12515 'ba-RU',
12516 'be-BY',
12517 'bg-BG',
12518 'bn-BD',
12519 //'bn-IN', // Moved after en-IN
12520 'bo-CN',
12521 'br-FR',
12522 'ca-ES',
12523 'co-FR',
12524 'cs-CZ',
12525 'cy-GB',
12526 'da-DK',
12527 'de-AT',
12528 'de-CH',
12529 'de-DE',
12530 'de-LI',
12531 'de-LU',
12532 'dv-MV',
12533 'el-GR',
12534 'en-AU',
12535 'en-BZ',
12536 'en-CA',
12537 'en-GB',
12538 'en-IE',
12539 'en-IN',
12540 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
12541 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
12542 'en-JM',
12543 'en-MY',
12544 'en-NZ',
12545 'en-PH',
12546 'en-SG',
12547 'en-TT',
12548 'en-US',
12549 'en-ZA',
12550 'en-ZW',
12551 'es-AR',
12552 'es-BO',
12553 'es-CL',
12554 'es-CO',
12555 'es-CR',
12556 'es-DO',
12557 'es-EC',
12558 'es-ES',
12559 'es-GT',
12560 'es-HN',
12561 'es-MX',
12562 'es-NI',
12563 'es-PA',
12564 'es-PE',
12565 'es-PR',
12566 'es-PY',
12567 'es-SV',
12568 'es-US',
12569 'es-UY',
12570 'es-VE',
12571 'et-EE',
12572 'eu-ES',
12573 'fa-IR',
12574 'fi-FI',
12575 'fo-FO',
12576 'fr-BE',
12577 'fr-CA',
12578 'fr-CH',
12579 'fr-FR',
12580 'fr-LU',
12581 'fr-MC',
12582 'fy-NL',
12583 'ga-IE',
12584 'gd-GB',
12585 'gl-ES',
12586 'gu-IN',
12587 'he-IL',
12588 'hi-IN',
12589 'hr-BA',
12590 'hr-HR',
12591 'hu-HU',
12592 'hy-AM',
12593 'id-ID',
12594 'ig-NG',
12595 'ii-CN',
12596 'is-IS',
12597 'it-CH',
12598 'it-IT',
12599 'ja-JP',
12600 'ka-GE',
12601 'kk-KZ',
12602 'kl-GL',
12603 'km-KH',
12604 'kn-IN',
12605 'ko-KR',
12606 'ky-KG',
12607 'lb-LU',
12608 'lo-LA',
12609 'lt-LT',
12610 'lv-LV',
12611 'mi-NZ',
12612 'mk-MK',
12613 'ml-IN',
12614 'mn-MN',
12615 'mr-IN',
12616 'ms-BN',
12617 'ms-MY',
12618 'mt-MT',
12619 'nb-NO',
12620 'ne-NP',
12621 'nl-BE',
12622 'nl-NL',
12623 'nn-NO',
12624 'oc-FR',
12625 'or-IN',
12626 'pa-IN',
12627 'pl-PL',
12628 'ps-AF',
12629 'pt-BR',
12630 'pt-PT',
12631 'rm-CH',
12632 'ro-MD',
12633 'ro-RO',
12634 'ru-RU',
12635 'rw-RW',
12636 'sa-IN',
12637 'se-FI',
12638 'se-NO',
12639 'se-SE',
12640 'si-LK',
12641 'sk-SK',
12642 'sl-SI',
12643 'sq-AL',
12644 'sv-FI',
12645 'sv-SE',
12646 'sw-KE',
12647 'ta-IN',
12648 'te-IN',
12649 'th-TH',
12650 'tk-TM',
12651 'tn-ZA',
12652 'tr-TR',
12653 'tt-RU',
12654 'ug-CN',
12655 'uk-UA',
12656 'ur-PK',
12657 'vi-VN',
12658 'wo-SN',
12659 'xh-ZA',
12660 'yo-NG',
12661 'zh-CN',
12662 'zh-HK',
12663 'zh-MO',
12664 'zh-SG',
12665 'zh-TW',
12666 'zu-ZA',
12667 );
12668
12669 $buildprimarykeytotest = strtolower($countrycode) . '-' . strtoupper($countrycode);
12670 if (in_array($buildprimarykeytotest, $locales)) {
12671 return strtolower($countrycode) . '_' . strtoupper($countrycode);
12672 }
12673
12674 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
12675 foreach ($locales as $locale) {
12676 $locale_language = locale_get_primary_language($locale);
12677 $locale_region = locale_get_region($locale);
12678 if (strtoupper($countrycode) == $locale_region) {
12679 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
12680 return strtolower($locale_language) . '_' . strtoupper($locale_region);
12681 }
12682 }
12683 } else {
12684 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
12685 }
12686
12687 return null;
12688}
12689
12720function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
12721{
12722 global $hookmanager, $db;
12723
12724 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
12725 foreach ($conf->modules_parts['tabs'][$type] as $value) {
12726 $values = explode(':', $value);
12727
12728 $reg = array();
12729 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
12730 if (count($values) !== 6) {
12731 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);
12732 continue;
12733 }
12734
12735 // new declaration with permissions:
12736 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12737 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12738 if ($values[0] != $type) {
12739 continue;
12740 }
12741
12742 $newtab = array();
12743 $postab = $h;
12744 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
12745 $str = $values[1];
12746 $posstart = strpos($str, '(');
12747 if ($posstart > 0) {
12748 $posend = strpos($str, ')');
12749 if ($posstart > 0) {
12750 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
12751 if (is_numeric($res1)) {
12752 $postab = (int) $res1;
12753 $values[1] = '+' . substr($str, $posend + 1);
12754 }
12755 }
12756 }
12757
12758 global $objectoffield; // So we can use $objectoffield int verifCond
12759 $objectoffield = $object;
12760
12761 if (!verifCond($values[4], '2')) {
12762 continue;
12763 }
12764
12765 if ($values[3]) {
12766 if ($filterorigmodule) { // If a filter of module origin has been requested
12767 if (strpos($values[3], '@')) { // This is an external module
12768 if ($filterorigmodule != 'external') {
12769 continue;
12770 }
12771 } else { // This looks a core module
12772 if ($filterorigmodule != 'core') {
12773 continue;
12774 }
12775 }
12776 }
12777 $langs->load($values[3]);
12778 }
12779
12780 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
12781 // If label is "SUBSTITUION_..."
12782 $substitutionarray = array();
12783 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
12784 $label = make_substitutions($reg[1], $substitutionarray);
12785 } else {
12786 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
12787 $labeltemp = explode(',', $values[2]);
12788 $label = $langs->trans($labeltemp[0]);
12789
12790 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
12791 dol_include_once($labeltemp[2]);
12792 $classtoload = $labeltemp[1];
12793 if (class_exists($classtoload)) {
12794 $obj = new $classtoload($db);
12795 $function = $labeltemp[3];
12796 if ($obj && $function && method_exists($obj, $function)) {
12797 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12798 $nbrec = $obj->$function($object->id, $obj);
12799 if (!empty($nbrec)) {
12800 $label .= '<span class="badge marginleftonlyshort">' . $nbrec . '</span>';
12801 }
12802 }
12803 }
12804 }
12805 }
12806 $url = preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]);
12807 $link = parse_url($url);
12808 $query = [];
12809 if (isset($link['query'])) {
12810 parse_str($link['query'], $query);
12811 }
12812 $newtab[0] = dolBuildUrl(dol_buildpath($link['path'], 1), $query);
12813 $newtab[1] = $label;
12814 $newtab[2] = str_replace('+', '', $values[1]);
12815 $h++;
12816
12817 // set tab at its position
12818 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
12819 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
12820 if ($values[0] != $type) {
12821 continue;
12822 }
12823 $tabname = str_replace('-', '', $values[1]);
12824 foreach ($head as $key => $val) {
12825 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
12826 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
12827 if ($head[$key][2] == $tabname && $condition) {
12828 unset($head[$key]);
12829 break;
12830 }
12831 }
12832 }
12833 }
12834 }
12835
12836 // No need to make a return $head. Var is modified as a reference
12837 if (!empty($hookmanager)) {
12838 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
12839 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
12840 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
12841 if ($reshook > 0) { // Hook ask to replace completely the array
12842 $head = $hookmanager->resArray;
12843 } else { // Hook
12844 $head = array_merge($head, $hookmanager->resArray);
12845 }
12846 $h = count($head);
12847 }
12848}
12849
12861function printCommonFooter($zone = 'private')
12862{
12863 global $conf, $hookmanager, $user, $langs;
12864 global $action;
12865 global $micro_start_time;
12866
12867 if ($zone == 'private') {
12868 print "\n" . '<!-- Common footer for private page -->' . "\n";
12869 } else {
12870 print "\n" . '<!-- Common footer for public page -->' . "\n";
12871 }
12872
12873 // A div to store page_y POST parameter so we can read it using javascript
12874 print "\n<!-- A div to store page_y POST parameter -->\n";
12875 print '<div id="page_y" style="display: none;">' . (GETPOST('page_y') ? GETPOST('page_y') : '') . '</div>' . "\n";
12876
12877 $parameters = array('zone' => $zone);
12878 $tmpobject = null;
12879 // @phan-suppress-next-line PhanPluginConstantVariableNull
12880 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters, $tmpobject, $action); // Note that $action and $object may have been modified by some hooks
12881 if (empty($reshook)) {
12882 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
12883 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
12884 }
12885
12886 print "\n";
12887 if (!empty($conf->use_javascript_ajax)) {
12888 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
12889 print '<script>' . "\n";
12890 print 'jQuery(document).ready(function() {' . "\n";
12891
12892 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
12893 print "\n";
12894 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */' . "\n";
12895 print 'jQuery("li.menuhider").click(function(event) {';
12896 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }' . "\n";
12897 print ' console.log("We click on .menuhider");' . "\n";
12898 print ' $("body").toggleClass("sidebar-collapse")' . "\n";
12899 print '});' . "\n";
12900 }
12901
12902 // Management of focus and mandatory for fields
12903 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"])))) {
12904 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */' . "\n";
12905 $relativepathstring = $_SERVER["PHP_SELF"];
12906 // Clean $relativepathstring
12907 if (constant('DOL_URL_ROOT')) {
12908 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
12909 }
12910 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
12911 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
12912 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
12913
12914 if (!empty($user->default_values[$relativepathstring]['focus'])) {
12915 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
12916 $qualified = 0;
12917 if ($defkey != '_noquery_') {
12918 $tmpqueryarraytohave = explode('&', $defkey);
12919 $foundintru = 0;
12920 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12921 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12922 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12923 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12924 $foundintru = 1;
12925 }
12926 }
12927 if (!$foundintru) {
12928 $qualified = 1;
12929 }
12930 //var_dump($defkey.'-'.$qualified);
12931 } else {
12932 $qualified = 1;
12933 }
12934
12935 if ($qualified) {
12936 print 'console.log("set the focus by executing jQuery(...).focus();")' . "\n";
12937 foreach ($defval as $paramkey => $paramval) {
12938 // Set focus on field
12939 print 'jQuery("input[name=\'' . $paramkey . '\']").focus();' . "\n";
12940 print 'jQuery("textarea[name=\'' . $paramkey . '\']").focus();' . "\n"; // TODO KO with ckeditor
12941 print 'jQuery("select[name=\'' . $paramkey . '\']").focus();' . "\n"; // Not really useful, but we keep it in case of.
12942 }
12943 }
12944 }
12945 }
12946 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12947 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12948 $qualified = 0;
12949 if ($defkey != '_noquery_') {
12950 $tmpqueryarraytohave = explode('&', $defkey);
12951 $foundintru = 0;
12952 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12953 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12954 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12955 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12956 $foundintru = 1;
12957 }
12958 }
12959 if (!$foundintru) {
12960 $qualified = 1;
12961 }
12962 //var_dump($defkey.'-'.$qualified);
12963 } else {
12964 $qualified = 1;
12965 }
12966
12967 if ($qualified) {
12968 print 'console.log("set the js code to manage fields that are set as mandatory");' . "\n";
12969
12970 foreach ($defval as $paramkey => $paramval) {
12971 // Solution 1: Add handler on submit to check if mandatory fields are empty
12972 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12973 print "form.on('submit', function(event) {
12974 var submitter = \$(this).find(':submit:focus').get(0);
12975 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12976
12977 if (buttonName == 'cancel') {
12978 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12979 return true;
12980 }
12981
12982 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');
12983
12984 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12985 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12986
12987 if (tmptypefield == 'textarea') {
12988 // We must instead check the content of ckeditor
12989 var tmpeditor = (typeof CKEDITOR !== 'undefined') ? CKEDITOR.instances['".dol_escape_js($paramkey)."'] : null;
12990 if (tmpeditor) {
12991 tmpvalue = tmpeditor.getData();
12992 console.log('For textarea tmpvalue is '+tmpvalue);
12993 }
12994 }
12995
12996 let tmpvalueisempty = false;
12997 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1 || tmpvalue === '-1') {
12998 tmpvalueisempty = true;
12999 }
13000 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
13001 tmpvalueisempty = true;
13002 }
13003 if (tmpvalueisempty && buttonName !== 'cancel') {
13004 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
13005 event.preventDefault(); // Stop submission of form to allow custom code to decide.
13006 event.stopPropagation(); // Stop other handlers.
13007
13008 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
13009
13010 return false;
13011 }
13012 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
13013 return true;
13014 });
13015 \n";
13016
13017 // Solution 2: Add property 'required' on input
13018 // so browser will check value and try to focus on it when submitting the form.
13019 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
13020 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
13021 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
13022 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
13023 //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";
13024 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
13025 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
13026 // Add 'field required' class on closest td for all input elements : input, textarea and select
13027 //print '}, 500);'; // 500 milliseconds delay
13028
13029 // Now set the class "fieldrequired"
13030 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
13031 }
13032
13033 // If we submit using the cancel button, we remove the required attributes
13034 print 'jQuery("input[name=\'cancel\']").click(function() {
13035 console.log("We click on cancel button so removed all required attribute");
13036 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
13037 });' . "\n";
13038 }
13039 }
13040 }
13041 }
13042
13043 print '});' . "\n";
13044
13045 // End of tuning
13046 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
13047 print "\n";
13048 print "/* JS CODE TO ENABLE to add memory info */\n";
13049 print 'window.console && console.log("';
13050 if (getDolGlobalString('MEMCACHED_SERVER')) {
13051 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER') . ' - ';
13052 }
13053 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
13054 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
13055 $micro_end_time = microtime(true);
13056 print ' - Build time: ' . ceil(1000 * ($micro_end_time - $micro_start_time)) . ' ms';
13057 }
13058
13059 if (function_exists("memory_get_usage")) {
13060 print ' - Mem: ' . memory_get_usage(); // Do not use true here, it seems it takes the peak amount
13061 }
13062 if (function_exists("memory_get_peak_usage")) {
13063 print ' - Real mem peak: ' . memory_get_peak_usage(true);
13064 }
13065 if (function_exists("zend_loader_file_encoded")) {
13066 print ' - Zend encoded file: ' . (zend_loader_file_encoded() ? 'yes' : 'no');
13067 }
13068 print '");' . "\n";
13069 }
13070
13071 print "\n" . '</script>' . "\n";
13072
13073 // Google Analytics
13074 // TODO Remove this, can be replaced with the hook printCommonFooter
13075 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
13076 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
13077 foreach ($tmptagarray as $tmptag) {
13078 print "\n";
13079 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
13080 print '
13081 <!-- Global site tag (gtag.js) - Google Analytics -->
13082 <script nonce="' . getNonce() . '" async src="https://www.googletagmanager.com/gtag/js?id=' . trim($tmptag) . '"></script>
13083 <script>
13084 window.dataLayer = window.dataLayer || [];
13085 function gtag(){dataLayer.push(arguments);}
13086 gtag(\'js\', new Date());
13087
13088 gtag(\'config\', \'' . trim($tmptag) . '\');
13089 </script>';
13090 print "\n";
13091 }
13092 }
13093 }
13094
13095 // Add Xdebug coverage of code
13096 if (defined('XDEBUGCOVERAGE')) {
13097 print_r(xdebug_get_code_coverage());
13098 }
13099
13100 // Output string from hooks
13101 if (!empty($hookmanager->resPrint)) {
13102 print $hookmanager->resPrint;
13103 }
13104
13105 // Add DebugBar data
13106 if ($user->hasRight('debugbar', 'read')) {
13107 global $debugbar;
13108 if ($debugbar instanceof DebugBar\DebugBar) {
13109 if (isset($debugbar['time'])) {
13110 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
13111 $debugbar['time']->stopMeasure('pageaftermaster');
13112 }
13113 print '<!-- Output debugbar data -->' . "\n";
13114 $renderer = $debugbar->getJavascriptRenderer();
13115 print $renderer->render();
13116 }
13117 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
13118 print "\n";
13119 print "<!-- Start of log output\n";
13120 //print '<div class="hidden">'."\n";
13121 foreach ($conf->logbuffer as $logline) {
13122 print $logline . "<br>\n";
13123 }
13124 //print '</div>'."\n";
13125 print "End of log output -->\n";
13126 }
13127 }
13128}
13129
13139function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
13140{
13141 if (is_null($string)) {
13142 return array();
13143 }
13144
13145 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
13146 // This is a regex string
13147 $newdelimiter = $delimiter;
13148 } else {
13149 // This is a simple string
13150 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
13151 $newdelimiter = preg_quote($delimiter, '/');
13152 }
13153
13154 if ($a = preg_split('/' . $newdelimiter . '/', $string)) {
13155 $ka = array();
13156 foreach ($a as $s) { // each part
13157 if ($s) {
13158 if ($pos = strpos($s, $kv)) { // key/value delimiter
13159 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
13160 } else { // key delimiter not found
13161 $ka[] = trim($s);
13162 }
13163 }
13164 }
13165 return $ka;
13166 }
13167
13168 return array();
13169}
13170
13178function dolExplodeKeepIfQuotes($input)
13179{
13180 // Use regexp to capture words and section in quotes
13181 $matches = array();
13182 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
13183
13184 // Merge result and delete empty values
13185
13186 $result = array_map(
13193 static function ($a, $b, $c) {
13194 if ($a !== '') {
13195 return $a;
13196 }
13197 if ($b !== '') {
13198 return $b;
13199 }
13200 if ($c !== '') {
13201 return $c;
13202 }
13203 return '';
13204 },
13205 $matches[1],
13206 $matches[2],
13207 $matches[3]
13208 );
13209 return array_values(array_filter(
13210 $result,
13217 static function ($val) {
13218 return $val !== '';
13219 }
13220 ));
13221}
13222
13223
13230function dol_set_focus($selector)
13231{
13232 print "\n" . '<!-- Set focus onto a specific field -->' . "\n";
13233 print '<script nonce="' . getNonce() . '">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("' . dol_escape_js($selector) . '").focus(); });</script>' . "\n";
13234}
13235
13236
13244function dol_getmypid()
13245{
13246 if (!function_exists('getmypid')) {
13247 return mt_rand(99900000, 99965535);
13248 } else {
13249 return getmypid(); // May be a number on 64 bits (depending on OS)
13250 }
13251}
13252
13275function natural_search($fields, $value, $mode = 0, $nofirstand = 0, $sqltoadd = '')
13276{
13277 global $db, $langs;
13278
13279 $value = trim($value);
13280
13281 if ($mode == 0) {
13282 $value = preg_replace('/\*/', '%', $value); // Replace * with %
13283 }
13284 if ($mode == 1) {
13285 $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
13286 }
13287
13288 $value = preg_replace('/\s*\|\s*/', '|', $value);
13289
13290 // Split criteria on ' ' but not if we are inside quotes.
13291 // For mode 3, the split is done later on the , only and not on the ' '.
13292 if ($mode != -3 && $mode != 3) {
13293 $crits = dolExplodeKeepIfQuotes($value);
13294 } else {
13295 $crits = array($value);
13296 }
13297
13298 $res = '';
13299 if (!is_array($fields)) {
13300 $fields = array($fields);
13301 }
13302 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
13303 foreach ($crits as $crit) { // Loop on each AND criteria
13304 $crit = trim($crit);
13305 $i2 = 0; // count the nb of valid criteria added for this this first criteria
13306 $newres = '';
13307
13308 foreach ($fields as $field) {
13309 if ($mode == 1) {
13310 $tmpcrits = explode('|', $crit);
13311 $i3 = 0; // count the nb of valid criteria added for this current field
13312 foreach ($tmpcrits as $tmpcrit) {
13313 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13314 continue;
13315 }
13316 $tmpcrit = trim($tmpcrit);
13317
13318 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13319
13320 $operator = '=';
13321 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
13322
13323 $reg = array();
13324 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
13325 if (!empty($reg[1])) {
13326 $operator = $reg[1];
13327 }
13328 if ($newcrit != '') {
13329 $numnewcrit = price2num($newcrit);
13330 if (is_numeric($numnewcrit)) {
13331 $newres .= $db->sanitize($field) . ' ' . $operator . ' ' . ((float) $numnewcrit); // should be a numeric
13332 } else {
13333 $newres .= '1 = 2'; // force false, we received a corrupted data
13334 }
13335 $i3++; // a criteria was added to string
13336 }
13337 }
13338 $i2++; // a criteria for 1 more field was added to string
13339 } elseif ($mode == 2 || $mode == -2) {
13340 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
13341 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -2 ? 'NOT ' : '');
13342 $newres .= $crit ? "IN (" . $db->sanitize($db->escape($crit)) . ")" : "IN (0)";
13343 if ($mode == -2) {
13344 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13345 }
13346 $i2++; // a criteria for 1 more field was added to string
13347 } elseif ($mode == 3 || $mode == -3) {
13348 $tmparray = explode(',', $crit);
13349 if (count($tmparray)) {
13350 $listofcodes = '';
13351 foreach ($tmparray as $val) {
13352 $val = trim($val);
13353 if ($val) { // TODO Test with if ($val !== '') {
13354 $listofcodes .= ($listofcodes ? ',' : '');
13355 $listofcodes .= "'" . $db->escape($val) . "'";
13356 }
13357 }
13358 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -3 ? 'NOT ' : '') . "IN (" . $db->sanitize($listofcodes, 1, 0, 1) . ")";
13359 $i2++; // a criteria for 1 more field was added to string
13360 }
13361 if ($mode == -3) {
13362 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13363 }
13364 } elseif ($mode == 4) {
13365 $tmparray = explode(',', $crit);
13366 if (count($tmparray)) {
13367 $listofcodes = '';
13368 foreach ($tmparray as $val) {
13369 $val = trim($val);
13370 if ($val) {
13371 $newres .= ($i2 > 0 ? " OR (" : "(") . $db->sanitize($field) . " LIKE '" . $db->escape($val) . ",%'";
13372 $newres .= ' OR ' . $db->sanitize($field) . " = '" . $db->escape($val) . "'";
13373 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . "'";
13374 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . ",%'";
13375 $newres .= ')';
13376 $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)
13377 }
13378 }
13379 }
13380 } else { // $mode=0
13381 $tmpcrits = explode('|', $crit);
13382 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
13383 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
13384 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13385 continue;
13386 }
13387 $tmpcrit = trim($tmpcrit);
13388
13389 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
13390 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
13391 } else {
13392 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13393 }
13394
13395 $isSellist = false;
13396 $table = $label = $key = null;
13397
13398 if (strpos($field, 'ef.') === 0) {
13399 $extrafieldName = substr($field, 3);
13400 $extrafields = new ExtraFields($db);
13401 $extrafields->fetch_name_optionals_label('product');
13402
13403 if (isset($extrafields->attributes['product']['type'][$extrafieldName]) && $extrafields->attributes['product']['type'][$extrafieldName] === 'sellist') {
13404 $isSellist = true;
13405 $paramArray = $extrafields->attributes['product']['param'][$extrafieldName]['options'] ?? [];
13406 $param = array_key_first($paramArray);
13407 list($table, $label, $key) = explode(':', $param);
13408 }
13409 }
13410
13411 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
13412 $newres .= $db->sanitize($field) . " = " . (is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
13413 } else {
13414 $tmpcrit2 = $tmpcrit;
13415 $tmpbefore = '%';
13416 $tmpafter = '%';
13417 $tmps = '';
13418
13419 if ($isSellist) {
13420 $newres .= $field . " IN (SELECT t." . $db->sanitize($key) . " FROM " . $db->prefix() . $db->sanitize($table) . " AS t WHERE t." . $db->sanitize($label) . " LIKE '%" . $db->escape($tmpcrit2) . "%')";
13421 } else {
13422 if (preg_match('/^!/', $tmpcrit)) {
13423 $tmps .= $db->sanitize($field) . " NOT LIKE "; // ! as exclude character
13424 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
13425 } else {
13426 $tmps .= $db->sanitize($field) . " LIKE ";
13427 }
13428 $tmps .= "'";
13429
13430 if (preg_match('/^[\^\$]/', $tmpcrit)) {
13431 $tmpbefore = '';
13432 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
13433 }
13434 if (preg_match('/[\^\$]$/', $tmpcrit)) {
13435 $tmpafter = '';
13436 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
13437 }
13438
13439 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13440 $tmps = "(" . $tmps;
13441 }
13442 $newres .= $tmps;
13443 $newres .= $tmpbefore;
13444 $newres .= $db->escape($tmpcrit2);
13445 $newres .= $tmpafter;
13446 $newres .= "'";
13447 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13448 $newres .= " OR " . $db->sanitize($field) . " IS NULL)";
13449 }
13450 }
13451 }
13452
13453 $i3++;
13454 }
13455
13456 $i2++; // a criteria for 1 more field was added to string
13457 }
13458 }
13459
13460 if ($sqltoadd) {
13461 $newres .= ($newres ? '' : ' OR ').str_replace('__KEYTOSEARCH__', $db->escape($crit), $sqltoadd);
13462 }
13463
13464 if ($newres) {
13465 $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') . $newres . ($i2 > 1 ? ')' : '');
13466 }
13467 $i1++;
13468 }
13469 $res = ($nofirstand ? "" : " AND ") . "(" . $res . ")";
13470
13471 return $res;
13472}
13473
13480function showDirectDownloadLink($object)
13481{
13482 global $langs;
13483
13484 $out = '';
13485 $url = $object->getLastMainDocLink($object->element);
13486
13487 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe') . ' <span class="opacitymedium">' . $langs->trans("DirectDownloadLink") . '</span><br>';
13488 if ($url) {
13489 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="' . $url . '"></div>';
13490 $out .= ajax_autoselect("directdownloadlink", '');
13491 } else {
13492 $out .= '<div class="urllink">' . $langs->trans("FileNotShared") . '</div>';
13493 }
13494
13495 return $out;
13496}
13497
13506function getImageFileNameForSize($file, $extName, $extImgTarget = '')
13507{
13508 $dirName = dirname($file);
13509 if ($dirName == '.') {
13510 $dirName = '';
13511 }
13512
13513 if (!in_array($extName, array('', '_small', '_mini'))) {
13514 return 'Bad parameter extName';
13515 }
13516
13517 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
13518 $fileName = basename($fileName);
13519
13520 if (empty($extImgTarget)) {
13521 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
13522 }
13523 if (empty($extImgTarget)) {
13524 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
13525 }
13526 if (empty($extImgTarget)) {
13527 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
13528 }
13529 if (empty($extImgTarget)) {
13530 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
13531 }
13532 if (empty($extImgTarget)) {
13533 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
13534 }
13535 if (empty($extImgTarget)) {
13536 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
13537 }
13538
13539 if (!$extImgTarget) {
13540 return $file;
13541 }
13542
13543 $subdir = '';
13544 if ($extName) {
13545 $subdir = 'thumbs/';
13546 }
13547
13548 return ($dirName ? $dirName . '/' : '') . $subdir . $fileName . $extName . $extImgTarget; // New filename for thumb
13549}
13550
13551
13561function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
13562{
13563 global $conf, $langs;
13564
13565 if (empty($conf->use_javascript_ajax)) {
13566 return '';
13567 }
13568
13569 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
13570
13571 if ($alldata == 1) {
13572 if ($isAllowedForPreview) {
13573 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));
13574 } else {
13575 return array();
13576 }
13577 }
13578
13579 // old behavior, return a string
13580 if ($isAllowedForPreview) {
13581 $tmpurl = DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : '');
13582 $title = $langs->transnoentities("Preview");
13583 //$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().
13584 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
13585
13586 // 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,
13587 // 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.
13588 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
13589 return 'javascript:' . dol_escape_uri('document_preview(\'' . dol_escape_js($tmpurl) . '\', \'' . dol_escape_js(dol_mimetype($relativepath)) . '\', \'' . dol_escape_js($title) . '\')');
13590 } else {
13591 return '';
13592 }
13593}
13594
13601function getLabelSpecialCode($idcode)
13602{
13603 global $langs;
13604
13605 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
13606 if ($idcode > 10) {
13607 return 'Module ID ' . $idcode;
13608 }
13609 if (!empty($arrayspecialines[$idcode])) {
13610 return $langs->trans($arrayspecialines[$idcode]);
13611 }
13612 return '';
13613}
13614
13623function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
13624{
13625 global $langs;
13626 $out = '<script nonce="' . getNonce() . '">
13627 jQuery(document).ready(function () {
13628 jQuery("' . ((strpos($htmlname, '.') === 0 ? '' : '#') . $htmlname) . '").click(function() { jQuery(this).select(); } );
13629 });
13630 </script>';
13631 if ($addlink) {
13632 if ($textonlink === 'image') {
13633 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . img_picto('', 'globe') . '</a>';
13634 } else {
13635 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . $langs->trans("Link") . '</a>';
13636 }
13637 }
13638 return $out;
13639}
13640
13648function dolIsAllowedForPreview($file)
13649{
13650 // Check .noexe extension in filename
13651 if (preg_match('/\.noexe$/i', $file)) {
13652 return 0;
13653 }
13654
13655 // Check mime types
13656 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp', 'webm', 'mp4');
13657 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
13658 $mime_preview[] = 'svg+xml';
13659 }
13660 if (getDolGlobalString('MAIN_ALLOW_XML_FILES_AS_PREVIEW')) {
13661 $mime_preview[] = 'xml';
13662 }
13663
13664 //$mime_preview[]='vnd.oasis.opendocument.presentation';
13665 //$mime_preview[]='archive';
13666 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
13667 if ($num_mime !== false) {
13668 return 1;
13669 }
13670
13671 // By default, not allowed for preview
13672 return 0;
13673}
13674
13675
13685function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
13686{
13687 $mime = $default;
13688 $imgmime = 'other.png';
13689 $famime = 'file-o';
13690 $srclang = '';
13691
13692 $tmpfile = preg_replace('/\.noexe$/', '', $file);
13693
13694 // Plain text files
13695 if (preg_match('/\.txt$/i', $tmpfile)) {
13696 $mime = 'text/plain';
13697 $imgmime = 'text.png';
13698 $famime = 'file-alt';
13699 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
13700 $mime = 'text/richtext';
13701 $imgmime = 'text.png';
13702 $famime = 'file-alt';
13703 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
13704 $mime = 'text/csv';
13705 $imgmime = 'text.png';
13706 $famime = 'file-csv';
13707 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
13708 $mime = 'text/tab-separated-values';
13709 $imgmime = 'text.png';
13710 $famime = 'file-alt';
13711 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
13712 $mime = 'text/plain';
13713 $imgmime = 'text.png';
13714 $famime = 'file-alt';
13715 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
13716 $mime = 'text/plain';
13717 $imgmime = 'text.png';
13718 $srclang = 'ini';
13719 $famime = 'file-alt';
13720 } elseif (preg_match('/\.md$/i', $tmpfile)) {
13721 $mime = 'text/plain';
13722 $imgmime = 'text.png';
13723 $srclang = 'md';
13724 $famime = 'file-alt';
13725 } elseif (preg_match('/\.css$/i', $tmpfile)) {
13726 $mime = 'text/css';
13727 $imgmime = 'css.png';
13728 $srclang = 'css';
13729 $famime = 'file-alt';
13730 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
13731 $mime = 'text/plain';
13732 $imgmime = 'text.png';
13733 $srclang = 'lang';
13734 $famime = 'file-alt';
13735 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
13736 $mime = 'text/plain';
13737 $imgmime = 'text.png';
13738 $famime = 'file-alt';
13739 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
13740 $mime = 'text/html';
13741 $imgmime = 'html.png';
13742 $srclang = 'html';
13743 $famime = 'file-alt';
13744 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
13745 $mime = 'text/xml';
13746 $imgmime = 'other.png';
13747 $srclang = 'xml';
13748 $famime = 'file-alt';
13749 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
13750 $mime = 'text/xml';
13751 $imgmime = 'other.png';
13752 $srclang = 'xaml';
13753 $famime = 'file-alt';
13754 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
13755 $mime = 'text/plain';
13756 $imgmime = 'text.png';
13757 $srclang = 'bas';
13758 $famime = 'file-code';
13759 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
13760 $mime = 'text/plain';
13761 $imgmime = 'text.png';
13762 $srclang = 'c';
13763 $famime = 'file-code';
13764 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
13765 $mime = 'text/plain';
13766 $imgmime = 'text.png';
13767 $srclang = 'cpp';
13768 $famime = 'file-code';
13769 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
13770 $mime = 'text/plain';
13771 $imgmime = 'text.png';
13772 $srclang = 'cs';
13773 $famime = 'file-code';
13774 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
13775 $mime = 'text/plain';
13776 $imgmime = 'text.png';
13777 $srclang = 'h';
13778 $famime = 'file-code';
13779 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
13780 $mime = 'text/plain';
13781 $imgmime = 'text.png';
13782 $srclang = 'java';
13783 $famime = 'file-code';
13784 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
13785 $mime = 'text/plain';
13786 $imgmime = 'php.png';
13787 $srclang = 'php';
13788 $famime = 'file-code';
13789 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
13790 $mime = 'text/plain';
13791 $imgmime = 'php.png';
13792 $srclang = 'php';
13793 $famime = 'file-code';
13794 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
13795 $mime = 'text/plain';
13796 $imgmime = 'pl.png';
13797 $srclang = 'perl';
13798 $famime = 'file-code';
13799 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
13800 $mime = 'text/plain';
13801 $imgmime = 'text.png';
13802 $srclang = 'sql';
13803 $famime = 'file-code';
13804 } elseif (preg_match('/\.js$/i', $tmpfile)) {
13805 $mime = 'text/x-javascript';
13806 $imgmime = 'jscript.png';
13807 $srclang = 'js';
13808 $famime = 'file-code';
13809 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
13810 $mime = 'application/vnd.oasis.opendocument.presentation';
13811 $imgmime = 'ooffice.png';
13812 $famime = 'file-powerpoint';
13813 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
13814 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
13815 $imgmime = 'ooffice.png';
13816 $famime = 'file-excel';
13817 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
13818 $mime = 'application/vnd.oasis.opendocument.text';
13819 $imgmime = 'ooffice.png';
13820 $famime = 'file-word';
13821 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
13822 $mime = 'application/msaccess';
13823 $imgmime = 'mdb.png';
13824 $famime = 'file';
13825 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
13826 $mime = 'application/msword';
13827 $imgmime = 'doc.png';
13828 $famime = 'file-word';
13829 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
13830 $mime = 'application/msword';
13831 $imgmime = 'doc.png';
13832 $famime = 'file-word';
13833 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
13834 $mime = 'application/vnd.ms-excel';
13835 $imgmime = 'xls.png';
13836 $famime = 'file-excel';
13837 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
13838 $mime = 'application/vnd.ms-excel';
13839 $imgmime = 'xls.png';
13840 $famime = 'file-excel';
13841 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
13842 $mime = 'application/vnd.ms-excel';
13843 $imgmime = 'xls.png';
13844 $famime = 'file-excel';
13845 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
13846 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
13847 $imgmime = 'xls.png';
13848 $famime = 'file-excel';
13849 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
13850 $mime = 'application/vnd.ms-powerpoint';
13851 $imgmime = 'ppt.png';
13852 $famime = 'file-powerpoint';
13853 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
13854 $mime = 'application/x-mspowerpoint';
13855 $imgmime = 'ppt.png';
13856 $famime = 'file-powerpoint';
13857 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
13858 $mime = 'application/pdf';
13859 $imgmime = 'pdf.png';
13860 $famime = 'file-pdf';
13861 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
13862 $mime = 'text/x-bat';
13863 $imgmime = 'script.png';
13864 $srclang = 'dos';
13865 $famime = 'file-code';
13866 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
13867 $mime = 'text/x-sh';
13868 $imgmime = 'script.png';
13869 $srclang = 'bash';
13870 $famime = 'file-code';
13871 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
13872 $mime = 'text/x-ksh';
13873 $imgmime = 'script.png';
13874 $srclang = 'bash';
13875 $famime = 'file-code';
13876 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
13877 $mime = 'text/x-bash';
13878 $imgmime = 'script.png';
13879 $srclang = 'bash';
13880 $famime = 'file-code';
13881 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
13882 $mime = 'image/x-icon';
13883 $imgmime = 'image.png';
13884 $famime = 'file-image';
13885 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
13886 $mime = 'image/jpeg';
13887 $imgmime = 'image.png';
13888 $famime = 'file-image';
13889 } elseif (preg_match('/\.png$/i', $tmpfile)) {
13890 $mime = 'image/png';
13891 $imgmime = 'image.png';
13892 $famime = 'file-image';
13893 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
13894 $mime = 'image/gif';
13895 $imgmime = 'image.png';
13896 $famime = 'file-image';
13897 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
13898 $mime = 'image/bmp';
13899 $imgmime = 'image.png';
13900 $famime = 'file-image';
13901 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
13902 $mime = 'image/tiff';
13903 $imgmime = 'image.png';
13904 $famime = 'file-image';
13905 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
13906 $mime = 'image/svg+xml';
13907 $imgmime = 'image.png';
13908 $famime = 'file-image';
13909 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
13910 $mime = 'image/webp';
13911 $imgmime = 'image.png';
13912 $famime = 'file-image';
13913 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
13914 $mime = 'text/calendar';
13915 $imgmime = 'other.png';
13916 $famime = 'file-alt';
13917 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
13918 $mime = 'text/calendar';
13919 $imgmime = 'other.png';
13920 $famime = 'file-alt';
13921 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
13922 $mime = 'application/x-bittorrent';
13923 $imgmime = 'other.png';
13924 $famime = 'file-o';
13925 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
13926 $mime = 'audio';
13927 $imgmime = 'audio.png';
13928 $famime = 'file-audio';
13929 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
13930 $mime = 'video/mp4';
13931 $imgmime = 'video.png';
13932 $famime = 'file-video';
13933 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
13934 $mime = 'video/ogg';
13935 $imgmime = 'video.png';
13936 $famime = 'file-video';
13937 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
13938 $mime = 'video/webm';
13939 $imgmime = 'video.png';
13940 $famime = 'file-video';
13941 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
13942 $mime = 'video/x-msvideo';
13943 $imgmime = 'video.png';
13944 $famime = 'file-video';
13945 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
13946 $mime = 'video/divx';
13947 $imgmime = 'video.png';
13948 $famime = 'file-video';
13949 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
13950 $mime = 'video/xvid';
13951 $imgmime = 'video.png';
13952 $famime = 'file-video';
13953 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
13954 $mime = 'video';
13955 $imgmime = 'video.png';
13956 $famime = 'file-video';
13957 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
13958 // application/xxx where zzz is zip, ...
13959 $mime = 'archive';
13960 $imgmime = 'archive.png';
13961 $famime = 'file-archive';
13962 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
13963 $mime = 'application/octet-stream';
13964 $imgmime = 'other.png';
13965 $famime = 'file-o';
13966 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
13967 $mime = 'library';
13968 $imgmime = 'library.png';
13969 $famime = 'file-o';
13970 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13971 $mime = 'error';
13972 $imgmime = 'error.png';
13973 $famime = 'file-alt';
13974 }
13975
13976 if ($famime == 'file-o') {
13977 // file-o seems to not work in fontawesome 5
13978 $famime = 'file';
13979 }
13980
13981 // Return mimetype string
13982 switch ((int) $mode) {
13983 case 1:
13984 $tmp = explode('/', $mime);
13985 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13986 case 2:
13987 return $imgmime;
13988 case 3:
13989 return $srclang;
13990 case 4:
13991 return $famime;
13992 }
13993 return $mime;
13994}
13995
14007function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
14008{
14009 global $conf, $db;
14010
14011 $tablename = preg_replace('/^' . preg_quote(MAIN_DB_PREFIX, '/') . '/', '', $tablename); // Clean name of table for backward compatibility.
14012
14013 $dictvalues = (isset($conf->cache['dictvalues_' . $tablename]) ? $conf->cache['dictvalues_' . $tablename] : null);
14014
14015 if (is_null($dictvalues)) {
14016 $dictvalues = array();
14017
14018 $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
14019 if ($checkentity) {
14020 $sql .= ' AND entity IN (0,' . getEntity($tablename) . ')';
14021 }
14022
14023 $resql = $db->query($sql);
14024 if ($resql) {
14025 while ($obj = $db->fetch_object($resql)) {
14026 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
14027 }
14028 } else {
14029 dol_print_error($db);
14030 }
14031
14032 $conf->cache['dictvalues_' . $tablename] = $dictvalues;
14033 }
14034
14035 if (!empty($dictvalues[$id])) {
14036 // Found
14037 $tmp = $dictvalues[$id];
14038 return (property_exists($tmp, $field) ? $tmp->$field : '');
14039 } else {
14040 // Not found
14041 return '';
14042 }
14043}
14044
14051function colorIsLight($stringcolor)
14052{
14053 $stringcolor = str_replace('#', '', $stringcolor);
14054 $res = -1;
14055 if (!empty($stringcolor)) {
14056 $res = 0;
14057 $tmp = explode(',', $stringcolor);
14058 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
14059 $r = $tmp[0];
14060 $g = $tmp[1];
14061 $b = $tmp[2];
14062 } else {
14063 $hexr = $stringcolor[0] . $stringcolor[1];
14064 $hexg = $stringcolor[2] . $stringcolor[3];
14065 $hexb = $stringcolor[4] . $stringcolor[5];
14066 $r = hexdec($hexr);
14067 $g = hexdec($hexg);
14068 $b = hexdec($hexb);
14069 }
14070 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
14071 if ($bright > 0.6) {
14072 $res = 1;
14073 }
14074 }
14075 return $res;
14076}
14077
14086function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
14087{
14088 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
14089 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
14090 if (empty($menuentry['enabled'])) {
14091 return 0; // Entry disabled by condition
14092 }
14093 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
14094 $tmploops = explode('|', $menuentry['module']);
14095 $found = 0;
14096 foreach ($tmploops as $tmploop) {
14097 if (in_array($tmploop, $listofmodulesforexternal)) {
14098 $found++;
14099 break;
14100 }
14101 }
14102 if (!$found) {
14103 return 0; // Entry is for menus all excluded to external users
14104 }
14105 }
14106 if (!$menuentry['perms'] && $type_user) {
14107 return 0; // No permissions and user is external
14108 }
14109 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
14110 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
14111 }
14112 if (!$menuentry['perms']) {
14113 return 2; // No permissions and user is external
14114 }
14115 return 1;
14116}
14117
14125function roundUpToNextMultiple($n, $x = 5)
14126{
14127 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
14128 return (int) $result;
14129}
14130
14142function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
14143{
14144 $csstouse = 'badge';
14145 $csstouse .= (!empty($mode) ? ' badge-' . $mode : '');
14146 $csstouse .= (!empty($type) ? ' badge-' . $type : '');
14147 $csstouse .= (empty($params['css']) ? '' : ' ' . $params['css']);
14148
14149 $attr = array(
14150 'class' => $csstouse
14151 );
14152
14153 if (empty($html)) {
14154 $html = $label;
14155 }
14156
14157 if (!empty($url)) {
14158 $attr['href'] = $url;
14159 }
14160
14161 if ($mode === 'dot') {
14162 $attr['class'] .= ' classfortooltip';
14163 $attr['title'] = $html;
14164 $attr['aria-label'] = $label;
14165 $html = '';
14166 }
14167
14168 // Override attr
14169 if (!empty($params['attr']) && is_array($params['attr'])) {
14170 foreach ($params['attr'] as $key => $value) {
14171 if ($key == 'class') {
14172 $attr['class'] .= ' ' . $value;
14173 } elseif ($key == 'classOverride') {
14174 $attr['class'] = $value;
14175 } else {
14176 $attr[$key] = $value;
14177 }
14178 }
14179 }
14180
14181 // TODO: add hook
14182
14183 // escape all attribute
14184 $attr = array_map('dolPrintHTMLForAttribute', $attr);
14185
14186 $TCompiledAttr = array();
14187 foreach ($attr as $key => $value) {
14188 $TCompiledAttr[] = $key . '="' . $value . '"';
14189 }
14190
14191 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
14192
14193 $tag = !empty($url) ? 'a' : 'span';
14194
14195 return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
14196}
14197
14198
14211function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
14212{
14213 global $conf;
14214
14215 $return = '';
14216 $dolGetBadgeParams = array();
14217
14218 if (!empty($params['badgeParams'])) {
14219 $dolGetBadgeParams = $params['badgeParams'];
14220 }
14221
14222 // TODO : add a hook
14223 if ($displayMode == 0) {
14224 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
14225 } elseif ($displayMode == 1) {
14226 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14227 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
14228 // Use status with images (for backward compatibility)
14229 $return = '';
14230 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : $statusLabel) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
14231 $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>' : '');
14232
14233 // For small screen, we always use the short label instead of long label.
14234 if (!empty($conf->dol_optimize_smallscreen)) {
14235 if ($displayMode == 0) {
14236 $displayMode = 1;
14237 } elseif ($displayMode == 4) {
14238 $displayMode = 2;
14239 } elseif ($displayMode == 6) {
14240 $displayMode = 5;
14241 }
14242 }
14243
14244 // For backward compatibility. Image's filename are still in French, so we use this array to convert
14245 $statusImg = array(
14246 'status0' => 'statut0',
14247 'status1' => 'statut1',
14248 'status2' => 'statut2',
14249 'status3' => 'statut3',
14250 'status4' => 'statut4',
14251 'status5' => 'statut5',
14252 'status6' => 'statut6',
14253 'status7' => 'statut7',
14254 'status8' => 'statut8',
14255 'status9' => 'statut9'
14256 );
14257
14258 if (!empty($statusImg[$statusType])) {
14259 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
14260 } else {
14261 $htmlImg = img_picto($statusLabel, $statusType);
14262 }
14263
14264 if ($displayMode === 2) {
14265 $return = $htmlImg . ' ' . $htmlLabelShort;
14266 } elseif ($displayMode === 3) {
14267 $return = $htmlImg;
14268 } elseif ($displayMode === 4) {
14269 $return = $htmlImg . ' ' . $htmlLabel;
14270 } elseif ($displayMode === 5) {
14271 $return = $htmlLabelShort . ' ' . $htmlImg;
14272 } else { // $displayMode >= 6
14273 $return = $htmlLabel . ' ' . $htmlImg;
14274 }
14275 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
14276 // Use new badge
14277 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14278
14279 $dolGetBadgeParams['attr']['class'] = 'badge-status';
14280 if (empty($dolGetBadgeParams['attr']['title'])) {
14281 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
14282 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
14283 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
14284 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
14285 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
14286 }
14287
14288 if ($displayMode == 3) {
14289 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
14290 } elseif ($displayMode === 5) {
14291 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
14292 } else {
14293 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
14294 }
14295 }
14296
14297 return $return;
14298}
14299
14300
14336function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
14337{
14338 global $hookmanager, $action, $object, $langs;
14339
14340 // If $url is an array, we must build a dropdown button or recursively iterate over each value
14341 if (is_array($url)) {
14342 // Loop on $url array to remove entries of disabled modules
14343 foreach ($url as $key => $subbutton) {
14344 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
14345 unset($url[$key]);
14346 }
14347 }
14348
14349 $out = '';
14350
14351 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
14352 foreach ($url as $button) {
14353 if (!empty($button['lang'])) {
14354 $langs->load($button['lang']);
14355 }
14356 $label = $langs->trans($button['label']);
14357 $text = $button['text'] ?? '';
14358 $actionType = $button['actionType'] ?? '';
14359 $tmpUrl = DOL_URL_ROOT . $button['url'] . (empty($params['backtopage']) ? '' : '&amp;backtopage=' . urlencode($params['backtopage']));
14360 $id = $button['id'] ?? '';
14361 $userRight = $button['perm'] ?? 1;
14362 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
14363
14364 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
14365 }
14366 return $out;
14367 }
14368
14369 if (count($url) > 1) {
14370 $out .= '<div class="dropdown inline-block dropdown-holder">';
14371 $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>';
14372 $out .= '<div class="dropdown-content">';
14373 foreach ($url as $subbutton) {
14374 if (!empty($subbutton['lang'])) {
14375 $langs->load($subbutton['lang']);
14376 }
14377
14378 if (!empty($subbutton['urlraw'])) {
14379 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14380 } else {
14381 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14382 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14383 }
14384
14385 $subbuttonparam = array();
14386 if (!empty($subbutton['attr'])) {
14387 $subbuttonparam['attr'] = $subbutton['attr'];
14388 }
14389 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
14390
14391 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
14392 }
14393 $out .= "</div>";
14394 $out .= "</div>";
14395 } else {
14396 foreach ($url as $subbutton) { // Should loop on 1 record only
14397 if (!empty($subbutton['lang'])) {
14398 $langs->load($subbutton['lang']);
14399 }
14400
14401 if (!empty($subbutton['urlraw'])) {
14402 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14403 } else {
14404 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14405 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14406 }
14407
14408 $label = $langs->trans($subbutton['label']);
14409 $text = $subbutton['text'] ?? '';
14410 if (empty($text)) {
14411 $text = $label;
14412 $label = '';
14413 }
14414
14415 $out .= dolGetButtonAction($label, $text, 'default', $tmpurl, '', $subbutton['perm'], $params);
14416 }
14417 }
14418
14419 return $out;
14420 }
14421
14422 // Here, $url is a simple link
14423 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
14424 $class = "dropdown-item";
14425 } else {
14426 $class = 'butAction';
14427 if ($actionType == 'edit') {
14428 $class = 'butAction butActionEdit';
14429 } elseif ($actionType == 'email') {
14430 $class = 'butAction butActionEmail';
14431 } elseif ($actionType == 'clone') {
14432 $class = 'butAction butActionClone';
14433 } elseif ($actionType == 'danger' || $actionType == 'delete') {
14434 $class = 'butAction butActionDelete';
14435 if (!empty($url) && strpos($url, 'token=') === false) {
14436 $url .= '&token=' . newToken();
14437 }
14438 }
14439 }
14440 $attr = array(
14441 'class' => $class,
14442 'href' => empty($url) ? '' : $url,
14443 'title' => $label
14444 );
14445
14446 if (empty($text)) {
14447 $text = $label;
14448 $attr['title'] = ''; // if html not set, using label on title is redundant
14449 } else {
14450 $attr['title'] = $label;
14451 $attr['aria-label'] = $label;
14452 }
14453
14454 if (empty($userRight) || $userRight < 0) {
14455 $attr['class'] = 'butActionRefused';
14456 $attr['href'] = '';
14457 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
14458 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '');
14459 $attr['title'] .= ((empty($userRight) && empty($label)) ? $langs->trans('NotEnoughPermissions') : '');
14460 }
14461
14462 if (!empty($id)) {
14463 $attr['id'] = $id;
14464 }
14465
14466 // Override attr
14467 if (!empty($params['attr']) && is_array($params['attr'])) {
14468 foreach ($params['attr'] as $key => $value) {
14469 if ($key == 'class') {
14470 $attr['class'] .= ' ' . $value;
14471 } elseif ($key == 'classOverride') {
14472 $attr['class'] = $value;
14473 } else {
14474 $attr[$key] = $value;
14475 }
14476 }
14477 }
14478
14479 // automatic add tooltip when title is detected
14480 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
14481 $attr['class'] .= ' classfortooltip';
14482 }
14483
14484 // Js Confirm button
14485 if ($userRight && !empty($params['confirm'])) {
14486 if (!is_array($params['confirm'])) {
14487 $params['confirm'] = array();
14488 }
14489
14490 if (empty($params['confirm']['url'])) {
14491 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
14492 }
14493
14494 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
14495 $attr['data-confirm-url'] = $params['confirm']['url'];
14496 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
14497 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
14498 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
14499 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
14500 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
14501 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
14502
14503 $attr['class'] .= ' butActionConfirm';
14504 }
14505
14506 if (isset($attr['href']) && empty($attr['href'])) {
14507 unset($attr['href']);
14508 }
14509
14510 // TODO replace $TCompiledAttr generation by commonHtmlAttributeBuilder given below
14511 $TCompiledAttr = array();
14512 foreach ($attr as $key => $value) {
14513 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
14514 // Not recommended
14515 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
14516 } elseif (in_array($key, ['href', 'data-confirm-url'])) {
14517 $value = dolPrintHTMLForAttributeUrl($value);
14518 } else {
14519 $value = dolPrintHTMLForAttribute($value);
14520 }
14521
14522 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14523 }
14524 // TODO replace $TCompiledAttr generation by uncomment line below and remove old code
14525 // $TCompiledAttr = commonHtmlAttributeBuilder($attr,$params['use_unsecured_unescapedattr'] ?? []);
14526 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
14527
14528 $tag = !empty($attr['href']) ? 'a' : 'span';
14529
14530 $parameters = array(
14531 'TCompiledAttr' => $TCompiledAttr, // array
14532 'compiledAttributes' => $compiledAttributes, // string
14533 'attr' => $attr,
14534 'tag' => $tag,
14535 'label' => $label,
14536 'html' => $text,
14537 'actionType' => $actionType,
14538 'url' => $url,
14539 'id' => $id,
14540 'userRight' => $userRight,
14541 'params' => $params
14542 );
14543
14544 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14545 if ($reshook < 0) {
14546 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
14547 }
14548
14549 if (empty($reshook)) {
14550 if (dol_textishtml($text)) { // If content already HTML encoded
14551 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
14552 } else {
14553 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
14554 }
14555 } else {
14556 return $hookmanager->resPrint;
14557 }
14558}
14559
14595function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
14596{
14597 $TCompiledAttr = array();
14598 if (empty($attr)) {
14599 return [];
14600 }
14601
14602 foreach ($attr as $key => $value) {
14603 // special boolean attributes case
14604 if (in_array($key, getListOfHtmlBooleanAttributes())) {
14605 if ($value) {
14606 $TCompiledAttr[$key] = $key;
14607 }
14608 continue;
14609 }
14610
14611 if (!empty($unescapedAttr) && in_array($key, $unescapedAttr)) {
14612 // Not recommended
14613 $value = dol_htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE);
14614 } elseif ($key == 'href') {
14615 $value = dolPrintHTMLForAttributeUrl((string) $value);
14616 } else {
14617 $value = dolPrintHTMLForAttribute((string) $value);
14618 }
14619
14620 $TCompiledAttr[$key] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14621 }
14622
14623 return $TCompiledAttr;
14624}
14625
14640function getListOfHtmlBooleanAttributes(): array
14641{
14642 return [
14643 // Input / Form
14644 'checked',
14645 'disabled',
14646 'readonly',
14647 'required',
14648 'autofocus',
14649 'multiple',
14650
14651 // Option
14652 'selected',
14653
14654 // Form / General
14655 'novalidate',
14656 'formnovalidate',
14657
14658 // Media
14659 'autoplay',
14660 'controls',
14661 'loop',
14662 'muted',
14663 'playsinline',
14664
14665 // Other elements
14666 'hidden',
14667 'open',
14668 'ismap',
14669 'reversed',
14670 'allowfullscreen',
14671 'itemscope',
14672 'nomodule',
14673 'defer',
14674 'async',
14675 'default',
14676 'inert',
14677 ];
14678}
14679
14680
14689function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
14690{
14691 if (empty($url)) {
14692 return '';
14693 }
14694
14695 $parsedUrl = parse_url($url);
14696 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
14697 return $url;
14698 }
14699
14700 if (!empty($parsedUrl['query'])) {
14701 // Use parse_str() function to parse the string passed via URL
14702 parse_str($parsedUrl['query'], $urlQuery);
14703 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
14704 $url .= '&amp;backtopage=' . urlencode($params['backtopage']);
14705 }
14706 }
14707
14708 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
14709 $url = DOL_URL_ROOT . $url;
14710 }
14711
14712 return $url;
14713}
14714
14715
14722function dolGetButtonTitleSeparator($moreClass = "")
14723{
14724 return '<span class="button-title-separator ' . $moreClass . '" ></span>';
14725}
14726
14733function getFieldErrorIcon($fieldValidationErrorMsg)
14734{
14735 $out = '';
14736 if (!empty($fieldValidationErrorMsg)) {
14737 $out .= '<span class="field-error-icon classfortooltip" title="' . dol_escape_htmltag($fieldValidationErrorMsg, 1) . '" role="alert" >'; // role alert is used for accessibility
14738 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
14739 $out .= '</span>';
14740 }
14741
14742 return $out;
14743}
14744
14757function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
14758{
14759 global $langs, $user;
14760
14761 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
14762 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
14763 return '';
14764 }
14765 // Fix old picto fa-th-list to use fa-grid-vertical instead
14766 if ($iconClass == 'fa fa-th-list imgforviewmode') {
14767 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
14768 }
14769
14770 $class = 'btnTitle';
14771 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
14772 $class .= ' btnTitlePlus';
14773 }
14774 $useclassfortooltip = 1;
14775
14776 if (!empty($params['morecss'])) {
14777 $class .= ' ' . $params['morecss'];
14778 }
14779
14780 $attr = array(
14781 'class' => $class,
14782 'href' => empty($url) ? '' : $url
14783 );
14784
14785 if (!empty($helpText)) {
14786 $attr['title'] = $helpText;
14787 } elseif ($label) { // empty($attr['title']) &&
14788 $attr['title'] = $label;
14789 $useclassfortooltip = 0;
14790 }
14791
14792 if ($status == 2) {
14793 $attr['class'] .= ' btnTitleSelected';
14794 } elseif ($status <= 0) {
14795 $attr['class'] .= ' refused';
14796
14797 $attr['href'] = '';
14798
14799 if ($status == -1) { // disable
14800 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
14801 } elseif ($status == 0) { // Not enough permissions
14802 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
14803 }
14804 }
14805
14806 if (!empty($attr['title']) && $useclassfortooltip) {
14807 $attr['class'] .= ' classfortooltip';
14808 }
14809
14810 if (!empty($id)) {
14811 $attr['id'] = $id;
14812 }
14813
14814 // Override attr
14815 if (!empty($params['attr']) && is_array($params['attr'])) {
14816 foreach ($params['attr'] as $key => $value) {
14817 if ($key == 'class') {
14818 $attr['class'] .= ' ' . $value;
14819 } elseif ($key == 'classOverride') {
14820 $attr['class'] = $value;
14821 } else {
14822 $attr[$key] = $value;
14823 }
14824 }
14825 }
14826
14827 if (isset($attr['href']) && empty($attr['href'])) {
14828 unset($attr['href']);
14829 }
14830
14831 // TODO : add a hook
14832
14833 // Generate attributes with escapement
14834 $TCompiledAttr = array();
14835 foreach ($attr as $key => $value) {
14836 $TCompiledAttr[] = $key . '="' . dol_escape_htmltag($value) . '"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
14837 }
14838
14839 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
14840
14841 $tag = (empty($attr['href']) ? 'span' : 'a');
14842
14843 $button = '<' . $tag . ' ' . $compiledAttributes . '>';
14844 $button .= '<span class="' . $iconClass . ' valignmiddle btnTitle-icon"></span>';
14845 if (!empty($params['forcenohideoftext'])) {
14846 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label' . (empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '') . '">' . $label . '</span>';
14847 }
14848 $button .= '</' . $tag . '>';
14849
14850 return $button;
14851}
14852
14862function getElementProperties($elementType)
14863{
14864 global $conf, $db, $hookmanager;
14865
14866 $regs = array();
14867
14868 //$element_type='facture';
14869
14870 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
14871
14872 // Parse element/subelement
14873 $module = $elementType;
14874 $element = $elementType;
14875 $subelement = $elementType;
14876 $table_element = $elementType;
14877
14878 // If we ask a resource form external module (instead of default path)
14879 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
14880 $element = $subelement = $regs[1];
14881 $module = $regs[2];
14882 }
14883
14884 // If we ask a resource for a string with an element and a subelement
14885 // Example 'project_task'
14886 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
14887 $module = $element = $regs[1];
14888 $subelement = $regs[2];
14889 }
14890
14891 // Object lines will use parent classpath and module ref
14892 if (substr($elementType, -3) == 'det') {
14893 $module = preg_replace('/det$/', '', $element);
14894 $subelement = preg_replace('/det$/', '', $subelement);
14895 $classpath = $module . '/class';
14896 $classfile = $module;
14897 $classname = preg_replace('/det$/', 'Line', $element);
14898 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'supplier_order', 'commandefournisseur'))) {
14899 $classname = preg_replace('/det$/', 'Ligne', $element);
14900 }
14901 }
14902 // For compatibility and to work with non standard path
14903 if ($elementType == "action" || $elementType == "actioncomm") {
14904 $classpath = 'comm/action/class';
14905 $subelement = 'Actioncomm';
14906 $module = 'agenda';
14907 $table_element = 'actioncomm';
14908 } elseif ($elementType == 'cronjob') {
14909 $classpath = 'cron/class';
14910 $module = 'cron';
14911 $table_element = 'cron';
14912 } elseif ($elementType == 'adherent_type') {
14913 $classpath = 'adherents/class';
14914 $classfile = 'adherent_type';
14915 $module = 'adherent';
14916 $subelement = 'adherent_type';
14917 $classname = 'AdherentType';
14918 $table_element = 'adherent_type';
14919 } elseif ($elementType == 'bank_account') {
14920 $classpath = 'compta/bank/class';
14921 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14922 $classfile = 'account';
14923 $classname = 'Account';
14924 } elseif ($elementType == 'bank_line') {
14925 $classpath = 'compta/bank/class';
14926 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
14927 $classfile = 'account';
14928 $classname = 'AccountLine';
14929 } elseif ($elementType == 'category') {
14930 $classpath = 'categories/class';
14931 $module = 'categorie';
14932 $subelement = 'categorie';
14933 $table_element = 'categorie';
14934 } elseif ($elementType == 'contact') {
14935 $classpath = 'contact/class';
14936 $classfile = 'contact';
14937 $module = 'societe';
14938 $subelement = 'contact';
14939 $table_element = 'socpeople';
14940 } elseif ($elementType == 'inventory') {
14941 $module = 'product';
14942 $classpath = 'product/inventory/class';
14943 } elseif ($elementType == 'inventoryline') {
14944 $module = 'product';
14945 $classpath = 'product/inventory/class';
14946 $table_element = 'inventorydet';
14947 $parent_element = 'inventory';
14948 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
14949 $module = 'stock';
14950 $classpath = 'product/stock/class';
14951 $classfile = 'entrepot';
14952 $classname = 'Entrepot';
14953 $table_element = 'entrepot';
14954 } elseif ($elementType == 'project') {
14955 $classpath = 'projet/class';
14956 $module = 'projet';
14957 $table_element = 'projet';
14958 } elseif ($elementType == 'project_task') {
14959 $classpath = 'projet/class';
14960 $module = 'projet';
14961 $subelement = 'task';
14962 $table_element = 'projet_task';
14963 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
14964 $classpath = 'compta/facture/class';
14965 $module = 'facture';
14966 $subelement = 'facture';
14967 $table_element = 'facture';
14968 } elseif ($elementType == 'facturedet') {
14969 $classpath = 'compta/facture/class';
14970 $classfile = 'facture';
14971 $classname = 'FactureLigne';
14972 $module = 'facture';
14973 $table_element = 'facturedet';
14974 $parent_element = 'facture';
14975 } elseif ($elementType == 'facturerec'|| $elementType == 'facture_rec') {
14976 $classpath = 'compta/facture/class';
14977 $classfile = 'facture-rec';
14978 $module = 'facture';
14979 $classname = 'FactureRec';
14980 } elseif ($elementType == 'commande' || $elementType == 'order') {
14981 $classpath = 'commande/class';
14982 $module = 'commande';
14983 $subelement = 'commande';
14984 $table_element = 'commande';
14985 } elseif ($elementType == 'commandedet') {
14986 $classpath = 'commande/class';
14987 $classfile = 'commande';
14988 $classname = 'OrderLine';
14989 $module = 'commande';
14990 $table_element = 'commandedet';
14991 $parent_element = 'commande';
14992 } elseif ($elementType == 'propal') {
14993 $classpath = 'comm/propal/class';
14994 $table_element = 'propal';
14995 } elseif ($elementType == 'propaldet') {
14996 $classpath = 'comm/propal/class';
14997 $classfile = 'propal';
14998 $subelement = 'propaleligne';
14999 $module = 'propal';
15000 $table_element = 'propaldet';
15001 $parent_element = 'propal';
15002 } elseif ($elementType == 'shipping') {
15003 $classpath = 'expedition/class';
15004 $classfile = 'expedition';
15005 $classname = 'Expedition';
15006 $module = 'expedition';
15007 $table_element = 'expedition';
15008 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
15009 $classpath = 'expedition/class';
15010 $classfile = 'expedition';
15011 $classname = 'ExpeditionLigne';
15012 $module = 'expedition';
15013 $table_element = 'expeditiondet';
15014 $parent_element = 'expedition';
15015 } elseif ($elementType == 'delivery_note') {
15016 $classpath = 'delivery/class';
15017 $subelement = 'delivery';
15018 $module = 'expedition';
15019 } elseif ($elementType == 'delivery') {
15020 $classpath = 'delivery/class';
15021 $subelement = 'delivery';
15022 $module = 'expedition';
15023 } elseif ($elementType == 'deliverydet') {
15024 // @todo
15025 } elseif ($elementType == 'supplier_proposal') {
15026 $classpath = 'supplier_proposal/class';
15027 $module = 'supplier_proposal';
15028 $element = 'supplierproposal';
15029 $classfile = 'supplier_proposal';
15030 $subelement = 'supplierproposal';
15031 } elseif ($elementType == 'supplier_proposaldet') {
15032 $classpath = 'supplier_proposal/class';
15033 $module = 'supplier_proposal';
15034 $classfile = 'supplier_proposal';
15035 $classname = 'SupplierProposalLine';
15036 $table_element = 'supplier_proposaldet';
15037 $parent_element = 'supplier_proposal';
15038 } elseif ($elementType == 'contract') {
15039 $classpath = 'contrat/class';
15040 $module = 'contrat';
15041 $subelement = 'contrat';
15042 $table_element = 'contract';
15043 } elseif ($elementType == 'contratdet') {
15044 $classpath = 'contrat/class';
15045 $module = 'contrat';
15046 $table_element = 'contratdet';
15047 $parent_element = 'contrat';
15048 } elseif ($elementType == 'mailing') {
15049 $classpath = 'comm/mailing/class';
15050 $module = 'mailing';
15051 $classfile = 'mailing';
15052 $classname = 'Mailing';
15053 $subelement = '';
15054 } elseif ($elementType == 'member' || $elementType == 'adherent') {
15055 $classpath = 'adherents/class';
15056 $module = 'adherent';
15057 $subelement = 'adherent';
15058 $table_element = 'adherent';
15059 } elseif ($elementType == 'subscription') {
15060 $classpath = 'adherents/class';
15061 $classfile = 'subscription';
15062 $module = 'adherent';
15063 $subelement = 'subscription';
15064 $classname = 'Subscription';
15065 $table_element = 'subscription';
15066 } elseif ($elementType == 'usergroup') {
15067 $classpath = 'user/class';
15068 $module = 'user';
15069 } elseif ($elementType == 'mo' || $elementType == 'mrp') {
15070 $classpath = 'mrp/class';
15071 $classfile = 'mo';
15072 $classname = 'Mo';
15073 $module = 'mrp';
15074 $subelement = '';
15075 $table_element = 'mrp_mo';
15076 } elseif ($elementType == 'mrp_production') {
15077 $classpath = 'mrp/class';
15078 $classfile = 'mo';
15079 $classname = 'MoLine';
15080 $module = 'mrp';
15081 $subelement = '';
15082 $table_element = 'mrp_production';
15083 $parent_element = 'mo';
15084 } elseif ($elementType == 'cabinetmed_cons') {
15085 $classpath = 'cabinetmed/class';
15086 $module = 'cabinetmed';
15087 $subelement = 'cabinetmedcons';
15088 $table_element = 'cabinetmedcons';
15089 } elseif ($elementType == 'fichinter') {
15090 $classpath = 'fichinter/class';
15091 $module = 'ficheinter';
15092 $subelement = 'fichinter';
15093 $table_element = 'fichinter';
15094 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
15095 $classpath = 'resource/class';
15096 $module = 'resource';
15097 $subelement = 'dolresource';
15098 $table_element = 'resource';
15099 } elseif ($elementType == 'opensurvey_sondage') {
15100 $classpath = 'opensurvey/class';
15101 $module = 'opensurvey';
15102 $subelement = 'opensurveysondage';
15103 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' || $elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
15104 $classpath = 'fourn/class';
15105 $module = 'fournisseur';
15106 $classfile = 'fournisseur.commande';
15107 $element = 'order_supplier';
15108 $subelement = '';
15109 $classname = 'CommandeFournisseur';
15110 $table_element = 'commande_fournisseur';
15111 } elseif ($elementType == 'commande_fournisseurdet') {
15112 $classpath = 'fourn/class';
15113 $module = 'fournisseur';
15114 $classfile = 'fournisseur.commande';
15115 $element = 'commande_fournisseurdet';
15116 $subelement = '';
15117 $classname = 'CommandeFournisseurLigne';
15118 $table_element = 'commande_fournisseurdet';
15119 $parent_element = 'commande_fournisseur';
15120 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
15121 $classpath = 'fourn/class';
15122 $module = 'fournisseur';
15123 $classfile = 'fournisseur.facture';
15124 $element = 'invoice_supplier';
15125 $subelement = '';
15126 $classname = 'FactureFournisseur';
15127 $table_element = 'facture_fourn';
15128 } elseif ($elementType == 'facture_fourn_det') {
15129 $classpath = 'fourn/class';
15130 $module = 'fournisseur';
15131 $classfile = 'fournisseur.facture';
15132 $element = 'facture_fourn_det';
15133 $subelement = '';
15134 $classname = 'SupplierInvoiceLine';
15135 $table_element = 'facture_fourn_det';
15136 $parent_element = 'invoice_supplier';
15137 } elseif ($elementType == "service") {
15138 $classpath = 'product/class';
15139 $subelement = 'product';
15140 $table_element = 'product';
15141 } elseif ($elementType == 'product_attribute') {
15142 $module = 'variants';
15143 $element = 'product_attribute';
15144 $subelement = 'product_attribute';
15145 $classpath = 'variants/class';
15146 $classfile = 'ProductAttribute';
15147 $classname = 'ProductAttribute';
15148 $table_element = 'product_attribute';
15149 } elseif ($elementType == 'product_attribute_value') {
15150 $module = 'variants';
15151 $element = 'product_attribute_value';
15152 $subelement = 'product_attribute_value';
15153 $classpath = 'variants/class';
15154 $classfile = 'ProductAttributeValue';
15155 $classname = 'ProductAttributeValue';
15156 $table_element = 'product_attribute_value';
15157 $parent_element = 'product_attribute';
15158 } elseif ($elementType == 'salary') {
15159 $classpath = 'salaries/class';
15160 $module = 'salaries';
15161 } elseif ($elementType == 'payment_salary') {
15162 $classpath = 'salaries/class';
15163 $classfile = 'paymentsalary';
15164 $classname = 'PaymentSalary';
15165 $module = 'salaries';
15166 } elseif ($elementType == 'productlot') {
15167 $module = 'productbatch';
15168 $classpath = 'product/stock/class';
15169 $classfile = 'productlot';
15170 $classname = 'Productlot';
15171 $element = 'productlot';
15172 $subelement = '';
15173 $table_element = 'product_lot';
15174 } elseif ($elementType == 'societeaccount') {
15175 $classpath = 'societe/class';
15176 $classfile = 'societeaccount';
15177 $classname = 'SocieteAccount';
15178 $module = 'societe';
15179 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
15180 $classpath = 'website/class';
15181 $classfile = 'websitepage';
15182 $classname = 'Websitepage';
15183 $module = 'website';
15184 $subelement = 'websitepage';
15185 $table_element = 'website_page';
15186 } elseif ($elementType == 'fiscalyear') {
15187 $classpath = 'core/class';
15188 $module = 'accounting';
15189 $subelement = 'fiscalyear';
15190 } elseif ($elementType == 'chargesociales') {
15191 $classpath = 'compta/sociales/class';
15192 $module = 'tax';
15193 $table_element = 'chargesociales';
15194 } elseif ($elementType == 'tva') {
15195 $classpath = 'compta/tva/class';
15196 $module = 'tax';
15197 $subdir = '/vat';
15198 $table_element = 'tva';
15199 } elseif ($elementType == 'emailsenderprofile') {
15200 $module = '';
15201 $classpath = 'core/class';
15202 $classfile = 'emailsenderprofile';
15203 $classname = 'EmailSenderProfile';
15204 $table_element = 'c_email_senderprofile';
15205 $subelement = '';
15206 } elseif ($elementType == 'conferenceorboothattendee') {
15207 $classpath = 'eventorganization/class';
15208 $classfile = 'conferenceorboothattendee';
15209 $classname = 'ConferenceOrBoothAttendee';
15210 $module = 'eventorganization';
15211 } elseif ($elementType == 'conferenceorbooth') {
15212 $classpath = 'eventorganization/class';
15213 $classfile = 'conferenceorbooth';
15214 $classname = 'ConferenceOrBooth';
15215 $module = 'eventorganization';
15216 } elseif ($elementType == 'ccountry') {
15217 $module = '';
15218 $classpath = 'core/class';
15219 $classfile = 'ccountry';
15220 $classname = 'Ccountry';
15221 $table_element = 'c_country';
15222 $subelement = '';
15223 } elseif ($elementType == 'ecmfiles') {
15224 $module = 'ecm';
15225 $classpath = 'ecm/class';
15226 $classfile = 'ecmfiles';
15227 $classname = 'Ecmfiles';
15228 $table_element = 'ecmfiles';
15229 $subelement = '';
15230 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
15231 $module = 'knowledgemanagement';
15232 $classpath = 'knowledgemanagement/class';
15233 $classfile = 'knowledgerecord';
15234 $classname = 'KnowledgeRecord';
15235 $table_element = 'knowledgemanagement_knowledgerecord';
15236 $subelement = '';
15237 } elseif ($elementType == 'customer') {
15238 $module = 'societe';
15239 $classpath = 'societe/class';
15240 $classfile = 'client';
15241 $classname = 'Client';
15242 $table_element = 'societe';
15243 $subelement = '';
15244 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
15245 $module = 'societe';
15246 $classpath = 'fourn/class';
15247 $classfile = 'fournisseur';
15248 $classname = 'Fournisseur';
15249 $table_element = 'societe';
15250 $subelement = '';
15251 } elseif ($elementType == 'recruitmentcandidature') {
15252 $module = 'recruitment';
15253 $classfile = 'recruitmentcandidature';
15254 $classpath = 'recruitment/class';
15255 $classname = 'RecruitmentCandidature';
15256 $subelement = 'recruitmentcandidature';
15257 $subdir = '/recruitmentcandidature';
15258 } elseif ($elementType == 'recruitmentjobposition') {
15259 $module = 'recruitment';
15260 $classfile = 'recruitmentjobposition';
15261 $classpath = 'recruitment/class';
15262 $classname = 'RecruitmentJobPosition';
15263 $subelement = 'recruitmentjobposition';
15264 $subdir = '/recruitmentjobposition';
15265 }
15266
15267
15268 if (empty($classfile)) {
15269 $classfile = strtolower($subelement);
15270 }
15271 if (empty($classname)) {
15272 $classname = ucfirst($subelement);
15273 }
15274 if (empty($classpath)) {
15275 $classpath = $module . '/class';
15276 }
15277
15278 //print 'getElementProperties subdir='.$subdir;
15279
15280 // Set dir_output
15281 if ($module && isset($conf->$module)) { // The generic case
15282 if (!empty($conf->$module->multidir_output[$conf->entity])) {
15283 $dir_output = $conf->$module->multidir_output[$conf->entity];
15284 } elseif (!empty($conf->$module->output[$conf->entity])) {
15285 $dir_output = $conf->$module->output[$conf->entity];
15286 } elseif (!empty($conf->$module->dir_output)) {
15287 $dir_output = $conf->$module->dir_output;
15288 }
15289 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
15290 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
15291 } elseif (!empty($conf->$module->temp[$conf->entity])) {
15292 $dir_temp = $conf->$module->temp[$conf->entity];
15293 } elseif (!empty($conf->$module->dir_temp)) {
15294 $dir_temp = $conf->$module->dir_temp;
15295 }
15296 }
15297
15298 // Overwrite value for special cases
15299 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
15300 $dir_output = $conf->fournisseur->commande->dir_output;
15301 $dir_temp = $conf->fournisseur->commande->dir_temp;
15302 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
15303 $dir_output = $conf->fournisseur->facture->dir_output;
15304 $dir_temp = $conf->fournisseur->facture->dir_temp;
15305 }
15306 $dir_output .= $subdir;
15307 $dir_temp .= $subdir;
15308
15309 $elementProperties = array(
15310 'module' => $module,
15311 'element' => $element,
15312 'table_element' => $table_element,
15313 'subelement' => $subelement,
15314 'classpath' => $classpath,
15315 'classfile' => $classfile,
15316 'classname' => $classname,
15317 'dir_output' => $dir_output,
15318 'dir_temp' => $dir_temp,
15319 'parent_element' => $parent_element,
15320 );
15321
15322
15323 // Add hook
15324 if (!is_object($hookmanager)) {
15325 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
15326 $hookmanager = new HookManager($db);
15327 }
15328 $hookmanager->initHooks(array('elementproperties'));
15329
15330
15331 // Hook params
15332 $parameters = array(
15333 'elementType' => $elementType,
15334 'elementProperties' => $elementProperties
15335 );
15336
15337 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
15338
15339 if ($reshook) {
15340 $elementProperties = $hookmanager->resArray;
15341 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
15342 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
15343 }
15344
15345 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
15346 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
15347 unset($hookmanager->contextarray[$key]);
15348 }
15349
15350 return $elementProperties;
15351}
15352
15365function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
15366{
15367 global $db, $conf;
15368
15369 $ret = 0;
15370
15371 $element_prop = getElementProperties($element_type);
15372
15373 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
15374 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
15375 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
15376 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
15377 // of service and we will return properties of a product.
15378 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
15379 } elseif ($element_prop['module'] == 'societeaccount') {
15380 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
15381 } else {
15382 $ismodenabled = isModEnabled($element_prop['module']);
15383 }
15384 //var_dump('element_type='.$element_type);
15385 //var_dump($element_prop);
15386 //var_dump($element_prop['module'].' '.$ismodenabled);
15387 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
15388 if ($useCache === 1 && $element_id > 0
15389 && !empty($conf->cache['fetchObjectByElement'][$element_type])
15390 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15391 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15392 ) {
15393 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
15394 }
15395
15396 dol_include_once('/' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15397
15398 if (class_exists($element_prop['classname'])) {
15399 $className = $element_prop['classname'];
15400 $objecttmp = new $className($db);
15401 '@phan-var-force CommonObject $objecttmp';
15404 if ($element_id > 0 || !empty($element_ref)) {
15405 $ret = $objecttmp->fetch($element_id, $element_ref);
15406 if ($ret >= 0) {
15407 if (empty($objecttmp->module)) {
15408 $objecttmp->module = $element_prop['module'];
15409 }
15410
15411 if ($useCache > 0) {
15412 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
15413 $conf->cache['fetchObjectByElement'][$element_type] = [];
15414 }
15415
15416 // Manage cache limit
15417 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
15418 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
15419 }
15420
15421 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
15422 }
15423
15424 return $objecttmp;
15425 }
15426 } else {
15427 return $objecttmp; // returned an object without fetch
15428 }
15429 } else {
15430 dol_syslog($element_prop['classname'] . ' doesn\'t exists in /' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15431 return -1;
15432 }
15433 }
15434
15435 return $ret;
15436}
15437
15443function getExecutableContent()
15444{
15445 $arrayofregexextension = array(
15446 'htm',
15447 'html',
15448 'shtml',
15449 'js',
15450 'phar',
15451 'php',
15452 'php3',
15453 'php4',
15454 'php5',
15455 'phtml',
15456 'pht',
15457 'pl',
15458 'py',
15459 'cgi',
15460 'ksh',
15461 'sh',
15462 'shtml',
15463 'bash',
15464 'bat',
15465 'cmd',
15466 'wpk',
15467 'exe',
15468 'dmg',
15469 'appimage'
15470 );
15471
15472 return $arrayofregexextension;
15473}
15474
15481function isAFileWithExecutableContent($filename)
15482{
15483 $arrayofregexextension = getExecutableContent();
15484
15485 foreach ($arrayofregexextension as $fileextension) {
15486 if (preg_match('/\.' . preg_quote($fileextension, '/') . '$/i', $filename)) {
15487 return true;
15488 }
15489 }
15490
15491 return false;
15492}
15493
15501function newToken()
15502{
15503 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
15504}
15505
15513function currentToken()
15514{
15515 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
15516}
15517
15523function getNonce()
15524{
15525 global $conf;
15526
15527 if (empty($conf->cache['nonce'])) {
15528 include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
15529 $conf->cache['nonce'] = dolGetRandomBytes(8);
15530 }
15531
15532 return $conf->cache['nonce'];
15533}
15534
15535
15549function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
15550{
15551 global $langs;
15552
15553 print '<div class="div-table-responsive-no-min">';
15554 print '<table class="noborder centpercent">';
15555 print '<tr class="liste_titre">';
15556
15557 print ($emptyColumns < 1) ? '<th>' : '<th colspan="' . ($emptyColumns + 1) . '">';
15558
15559 print '<span class="valignmiddle">' . $langs->trans($header) . '</span>';
15560
15561 if (!empty($link)) {
15562 if (!empty($arguments)) {
15563 print '<a href="' . DOL_URL_ROOT . '/' . $link . '?' . $arguments . '">';
15564 } else {
15565 print '<a href="' . DOL_URL_ROOT . '/' . $link . '">';
15566 }
15567 }
15568
15569 if ($number > -1) {
15570 print '<span class="badge marginleftonlyshort">' . $number . '</span>';
15571 } elseif (!empty($link)) {
15572 print '<span class="badge marginleftonlyshort">...</span>';
15573 }
15574
15575 if (!empty($link)) {
15576 print '</a>';
15577 }
15578
15579 print '</th>';
15580
15581 if ($number < 0 && !empty($link)) {
15582 print '<th class="right">';
15583 print '</th>';
15584 }
15585
15586 print '</tr>';
15587}
15588
15597function finishSimpleTable($addLineBreak = false)
15598{
15599 print '</table>';
15600 print '</div>';
15601
15602 if ($addLineBreak) {
15603 print '<br>';
15604 }
15605}
15606
15618function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
15619{
15620 global $langs;
15621
15622 if ($num === 0) {
15623 print '<tr class="oddeven">';
15624 print '<td colspan="' . $tableColumnCount . '"><span class="opacitymedium">' . $langs->trans($noneWord) . '</span></td>';
15625 print '</tr>';
15626 return;
15627 }
15628
15629 if ($nbofloop === 0) {
15630 // don't show a summary line
15631 return;
15632 }
15633
15634 /* Case already handled above, commented to satisfy phpstan.
15635 if ($num === 0) {
15636 $colspan = $tableColumnCount;
15637 } else
15638 */
15639 if ($num > $nbofloop) {
15640 $colspan = $tableColumnCount;
15641 } else {
15642 $colspan = $tableColumnCount - 1;
15643 }
15644
15645 if ($extraRightColumn) {
15646 $colspan--;
15647 }
15648
15649 print '<tr class="liste_total">';
15650
15651 if ($nbofloop > 0 && $num > $nbofloop) {
15652 print '<td colspan="' . $colspan . '" class="right">' . $langs->trans("XMoreLines", ($num - $nbofloop)) . '</td>';
15653 } else {
15654 print '<td colspan="' . $colspan . '" class="right"> ' . $langs->trans("Total") . '</td>';
15655 print '<td class="right centpercent">' . price($total) . '</td>';
15656 }
15657
15658 if ($extraRightColumn) {
15659 print '<td></td>';
15660 }
15661
15662 print '</tr>';
15663}
15664
15673function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
15674{
15675 if ($method == -1) {
15676 $method = 0;
15677 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
15678 $method = 1;
15679 }
15680 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
15681 $method = 2;
15682 }
15683 }
15684
15685 // Be sure we don't have output buffering enabled to have readfile working correctly
15686 while (ob_get_level()) {
15687 ob_end_flush();
15688 }
15689
15690 // Solution 0
15691 if ($method == 0) {
15692 readfile($fullpath_original_file_osencoded);
15693 } elseif ($method == 1) {
15694 // Solution 1
15695 $handle = fopen($fullpath_original_file_osencoded, "rb");
15696 while (!feof($handle)) {
15697 print fread($handle, 8192);
15698 }
15699 fclose($handle);
15700 } elseif ($method == 2) {
15701 // Solution 2
15702 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
15703 $handle2 = fopen("php://output", "wb");
15704 stream_copy_to_stream($handle1, $handle2);
15705 fclose($handle1);
15706 fclose($handle2);
15707 }
15708}
15709
15719function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
15720{
15721 global $langs;
15722
15723 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
15724
15725 $result = '<span class="clipboardCP' . ($showonlyonhover ? ' clipboardCPShowOnHover valignmiddle' : '') . '">';
15726 if ($texttoshow === 'none') {
15727 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15728 $result .= '<span class="clipboardCPValueToPrint"></span>';
15729 } elseif ($texttoshow) {
15730 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15731 $result .= '<span class="clipboardCPValueToPrint">' . dol_escape_htmltag($texttoshow, 1, 1) . '</span>';
15732 } else {
15733 $result .= '<' . $tag . ' class="clipboardCPValue">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
15734 }
15735 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="' . dolPrintHTML($langs->trans("ClickToCopyToClipboard")) . '"></span>';
15736 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
15737 $result .= '<span class="clipboardCPText"></span>';
15738 $result .= '</span>';
15739
15740 return $result;
15741}
15742
15743
15751function jsonOrUnserialize($stringtodecode, $assoc = true)
15752{
15753 $result = json_decode($stringtodecode, $assoc);
15754 if ($result === null) {
15755 $result = unserialize($stringtodecode, ['allowed_classes' => false]); // For backward compatibility. Is no more used in recent versions.
15756 }
15757
15758 return $result;
15759}
15760
15761
15778function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
15779{
15780 global $db, $user;
15781
15782 if (is_null($filter) || !is_string($filter) || $filter === '') {
15783 return '';
15784 }
15785 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
15786 $filter = '(' . $filter . ')';
15787 }
15788
15789 $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'
15790 $firstandlastparenthesis = 0;
15791
15792 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
15793 if ($noerror) {
15794 return '1 = 2';
15795 } else {
15796 return 'Filter syntax error - ' . $errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
15797 }
15798 }
15799
15800 // Test the filter syntax
15801 $t = preg_replace_callback('/' . $regexstring . '/i', 'dolForgeDummyCriteriaCallback', $filter);
15802 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
15803 // If the string result contains something else than '()', the syntax was wrong
15804
15805 if (preg_match('/[^\‍(\‍)]/', $t)) {
15806 $tmperrorstr = 'Bad syntax of the search string';
15807 $errorstr = 'Bad syntax of the search string: ' . $filter;
15808 if ($noerror) {
15809 return '1 = 2';
15810 } else {
15811 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - " . $errorstr, LOG_WARNING);
15812 return 'Filter error - ' . $tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
15813 }
15814 }
15815
15816 $ret = ($noand ? "" : " AND ") . ($nopar ? "" : '(') . preg_replace_callback('/' . $regexstring . '/i', 'dolForgeSQLCriteriaCallback', $filter) . ($nopar ? "" : ')');
15817
15818 if (is_object($db)) {
15819 $ret = str_replace('__NOW__', "'" . $db->idate(dol_now()) . "'", $ret);
15820 }
15821 if (is_object($user)) {
15822 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
15823 }
15824
15825 return $ret;
15826}
15827
15835function dolForgeExplodeAnd($sqlfilters)
15836{
15837 $arrayofandtags = array();
15838 $nbofchars = dol_strlen($sqlfilters);
15839
15840 $error = '';
15841 $parenthesislevel = 0;
15842 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
15843 if (!$result) {
15844 return array();
15845 }
15846 if ($parenthesislevel >= 1) {
15847 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
15848 }
15849
15850 $i = 0;
15851 $s = '';
15852 $countparenthesis = 0;
15853 while ($i < $nbofchars) {
15854 $char = dol_substr($sqlfilters, $i, 1);
15855
15856 if ($char == '(') {
15857 $countparenthesis++;
15858 } elseif ($char == ')') {
15859 $countparenthesis--;
15860 }
15861
15862 if ($countparenthesis == 0) {
15863 $char2 = dol_substr($sqlfilters, $i + 1, 1);
15864 $char3 = dol_substr($sqlfilters, $i + 2, 1);
15865 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
15866 // We found a AND
15867 $s = trim($s);
15868 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15869 $s = '(' . $s . ')';
15870 }
15871 $arrayofandtags[] = $s;
15872 $s = '';
15873 $i += 2;
15874 } else {
15875 $s .= $char;
15876 }
15877 } else {
15878 $s .= $char;
15879 }
15880 $i++;
15881 }
15882 if ($s) {
15883 $s = trim($s);
15884 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
15885 $s = '(' . $s . ')';
15886 }
15887 $arrayofandtags[] = $s;
15888 }
15889
15890 return $arrayofandtags;
15891}
15892
15902function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
15903{
15904 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
15905 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
15906 $tmp = $sqlfilters;
15907
15908 $nb = dol_strlen($tmp);
15909 $counter = 0;
15910 $parenthesislevel = 0;
15911
15912 $error = '';
15913
15914 $i = 0;
15915 while ($i < $nb) {
15916 $char = dol_substr($tmp, $i, 1);
15917
15918 if ($char == '(') {
15919 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
15920 // We open a parenthesis and it is the first char
15921 $parenthesislevel++;
15922 }
15923 $counter++;
15924 } elseif ($char == ')') {
15925 $nbcharremaining = ($nb - $i - 1);
15926 if ($nbcharremaining >= $counter) {
15927 $parenthesislevel = min($parenthesislevel, $counter - 1);
15928 }
15929 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
15930 $parenthesislevel = $counter;
15931 }
15932 $counter--;
15933 }
15934
15935 if ($counter < 0) {
15936 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15937 $parenthesislevel = 0;
15938 dol_syslog($error, LOG_WARNING);
15939 return false;
15940 }
15941
15942 $i++;
15943 }
15944
15945 if ($counter > 0) {
15946 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
15947 $parenthesislevel = 0;
15948 dol_syslog($error, LOG_WARNING);
15949 return false;
15950 }
15951
15952 return true;
15953}
15954
15962function dolForgeDummyCriteriaCallback($matches)
15963{
15964 //dol_syslog("Convert matches ".$matches[1]);
15965 if (empty($matches[1])) {
15966 return '';
15967 }
15968 $tmp = explode(':', $matches[1]);
15969 if (count($tmp) < 3) {
15970 return '';
15971 }
15972
15973 return '()'; // An empty criteria
15974}
15975
15984function dolForgeSQLCriteriaCallback($matches)
15985{
15986 global $db;
15987
15988 //dol_syslog("Convert matches ".$matches[1]);
15989 if (empty($matches[1])) {
15990 return '';
15991 }
15992 $tmp = explode(':', $matches[1], 3);
15993 if (count($tmp) < 3) {
15994 return '';
15995 }
15996
15997 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
15998
15999 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
16000
16001 $realOperator = [
16002 'NOTLIKE' => 'NOT LIKE',
16003 'ISNOT' => 'IS NOT',
16004 'NOTIN' => 'NOT IN',
16005 '!=' => '<>',
16006 ];
16007
16008 if (array_key_exists($operator, $realOperator)) {
16009 $operator = $realOperator[$operator];
16010 }
16011
16012 $tmpescaped = $tmp[2];
16013
16014 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
16015
16016 $regbis = array();
16017
16018 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)
16019 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
16020 $tmpescaped2 = '(';
16021 // Explode and sanitize each element in list
16022 $tmpelemarray = explode(',', $tmpescaped);
16023 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
16024 $reg = array();
16025 $tmpelem = trim($tmpelem);
16026 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
16027 $tmpelemarray[$tmpkey] = "'" . $db->escape($db->sanitize($reg[1], 2, 1, 1, 1)) . "'";
16028 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
16029 } elseif (preg_match('/^[0-9]+$/', (string) $tmpelem)) { // if only 0-9 chars, no .
16030 $tmpelemarray[$tmpkey] = (int) $tmpelem;
16031 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
16032 $tmpelemarray[$tmpkey] = (float) $tmpelem;
16033 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
16034 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
16035 } else {
16036 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
16037 }
16038 }
16039 $tmpescaped2 .= implode(',', $tmpelemarray);
16040 $tmpescaped2 .= ')';
16041
16042 $tmpescaped = $tmpescaped2;
16043 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
16044 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
16045 $tmpescaped = $regbis[1];
16046 }
16047 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
16048 $tmpescaped = "'" . $db->escape($tmpescaped) . "'"; // We do not escape the _ and % so the LIKE will work as expected
16049 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
16050 // TODO Retrieve type of field for $operand field name.
16051 // So we can complete format. For example we could complete a year with month and day.
16052 $tmpescaped = "'" . $db->escape($regbis[1]) . "'";
16053 } else {
16054 if (strtoupper($tmpescaped) == 'NULL') {
16055 $tmpescaped = 'NULL';
16056 } elseif (preg_match('/^[0-9]+$/', (string) $tmpescaped)) { // if only 0-9 chars, no .
16057 $tmpescaped = (int) $tmpescaped;
16058 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
16059 $tmpescaped = (float) $tmpescaped;
16060 } else {
16061 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
16062 }
16063 }
16064
16065 return '(' . $db->escape($operand) . ' ' . strtoupper($operator) . ' ' . $tmpescaped . ')';
16066}
16067
16068
16078function getTimelineIcon($actionstatic, &$histo, $key)
16079{
16080 global $langs;
16081
16082 $out = '<!-- timeline icon -->' . "\n";
16083 $iconClass = 'fa fa-comments';
16084 $img_picto = '';
16085 $colorClass = '';
16086 $pictoTitle = '';
16087
16088 if ($histo[$key]['percent'] == -1) {
16089 $colorClass = 'timeline-icon-not-applicble';
16090 $pictoTitle = $langs->trans('StatusNotApplicable');
16091 } elseif ($histo[$key]['percent'] == 0) {
16092 $colorClass = 'timeline-icon-todo';
16093 $pictoTitle = $langs->trans('StatusActionToDo') . ' (0%)';
16094 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
16095 $colorClass = 'timeline-icon-in-progress';
16096 $pictoTitle = $langs->trans('StatusActionInProcess') . ' (' . $histo[$key]['percent'] . '%)';
16097 } elseif ($histo[$key]['percent'] >= 100) {
16098 $colorClass = 'timeline-icon-done';
16099 $pictoTitle = $langs->trans('StatusActionDone') . ' (100%)';
16100 }
16101
16102 if ($actionstatic->code == 'AC_TICKET_CREATE') {
16103 $iconClass = 'fa fa-ticket';
16104 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
16105 $iconClass = 'fa fa-pencilxxx';
16106 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16107 $iconClass = 'fa fa-comments';
16108 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16109 $iconClass = 'fa fa-mask';
16110 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16111 if ($actionstatic->type_picto) {
16112 $img_picto = img_picto('', $actionstatic->type_picto);
16113 } else {
16114 if ($actionstatic->type_code == 'AC_RDV') {
16115 $iconClass = 'fa fa-handshake';
16116 } elseif ($actionstatic->type_code == 'AC_TEL') {
16117 $iconClass = 'fa fa-phone';
16118 } elseif ($actionstatic->type_code == 'AC_FAX') {
16119 $iconClass = 'fa fa-fax';
16120 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
16121 $iconClass = 'fa fa-envelope';
16122 } elseif ($actionstatic->type_code == 'AC_INT') {
16123 $iconClass = 'fa fa-shipping-fast';
16124 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
16125 $iconClass = 'fa fa-robot';
16126 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
16127 $iconClass = 'fa fa-robot';
16128 }
16129 }
16130 }
16131
16132 $out .= '<i class="' . $iconClass . ' ' . $colorClass . '" title="' . $pictoTitle . '">' . $img_picto . '</i>' . "\n";
16133 return $out;
16134}
16135
16143{
16144 global $db;
16145
16146 $documents = array();
16147
16148 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
16149 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'ecm_files ecm';
16150 $sql .= " WHERE ecm.filepath = 'agenda/" . ((int) $object->id) . "'";
16151 //$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
16152 $sql .= ' OR ecm.agenda_id = ' . (int) $object->id;
16153 $sql .= ' ORDER BY ecm.position ASC';
16154
16155 $resql = $db->query($sql);
16156 if ($resql) {
16157 if ($db->num_rows($resql)) {
16158 while ($obj = $db->fetch_object($resql)) {
16159 $documents[$obj->id] = $obj;
16160 }
16161 }
16162 }
16163
16164 return $documents;
16165}
16166
16167
16185function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
16186{
16187 global $user, $conf;
16188 global $form;
16189
16190 global $param, $massactionbutton;
16191
16192 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16193
16194 // Check parameters
16195 if (!is_object($filterobj) && !is_object($objcon)) {
16196 dol_print_error(null, 'BadParameter');
16197 }
16198
16199 $histo = array();
16200 '@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';
16201
16202 $numaction = 0;
16203 $now = dol_now();
16204
16205 $sortfield_list = explode(',', $sortfield);
16206 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
16207 $sortfield_new_list = array();
16208 foreach ($sortfield_list as $sortfield_value) {
16209 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
16210 }
16211 $sortfield_new = implode(',', $sortfield_new_list);
16212
16213 $sql = null;
16214 $sql2 = null;
16215
16216 if (isModEnabled('agenda')) {
16217 // Search histo on actioncomm
16218 if (is_object($objcon) && $objcon->id > 0) {
16219 $sql = "SELECT DISTINCT a.id, a.label as label,";
16220 } else {
16221 $sql = "SELECT a.id, a.label as label,";
16222 }
16223 $sql .= " a.datep as dp,";
16224 $sql .= " a.note as message,";
16225 $sql .= " a.datep2 as dp2,";
16226 $sql .= " a.percent as percent, 'action' as type,";
16227 $sql .= " a.fk_element, a.elementtype,";
16228 $sql .= " a.fk_contact,";
16229 $sql .= " a.email_from as msg_from,";
16230 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
16231 $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";
16232 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16233 $sql .= ", sp.lastname, sp.firstname";
16234 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16235 $sql .= ", m.lastname, m.firstname";
16236 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur', 'Propal', 'Expedition'))) {
16237 $sql .= ", o.ref";
16238 } else {
16239 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16240 $sql .= ", o.ref";
16241 }
16242 }
16243 $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as a";
16244 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as u on u.rowid = a.fk_user_action";
16245 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_actioncomm as c ON a.fk_action = c.id";
16246
16247 $force_filter_contact = $filterobj instanceof User;
16248
16249 if (is_object($objcon) && $objcon->id > 0) {
16250 $force_filter_contact = true;
16251 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "actioncomm_resources as r ON a.id = r.fk_actioncomm";
16252 $sql .= " AND r.element_type = '" . $db->escape($objcon->table_element) . "' AND r.fk_element = " . ((int) $objcon->id);
16253 }
16254
16255 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
16256 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople as sp ON a.fk_contact = sp.rowid";
16257 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
16258 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "element_resources as er";
16259 $sql .= " ON er.resource_type = 'dolresource'";
16260 $sql .= " AND er.element_id = a.id";
16261 $sql .= " AND er.resource_id = " . ((int) $filterobj->id);
16262 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16263 $sql .= ", " . MAIN_DB_PREFIX . "adherent as m";
16264 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16265 $sql .= ", " . MAIN_DB_PREFIX . "commande_fournisseur as o";
16266 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16267 $sql .= ", " . MAIN_DB_PREFIX . "product as o";
16268 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16269 $sql .= ", " . MAIN_DB_PREFIX . "ticket as o";
16270 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16271 $sql .= ", " . MAIN_DB_PREFIX . "bom_bom as o";
16272 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16273 $sql .= ", " . MAIN_DB_PREFIX . "contrat as o";
16274 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16275 $sql .= ", " . MAIN_DB_PREFIX . "facture as o";
16276 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16277 $sql .= ", " . MAIN_DB_PREFIX . "facture_fourn as o";
16278 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16279 $sql .= ", " . MAIN_DB_PREFIX . "commande as o";
16280 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16281 $sql .= ", " . MAIN_DB_PREFIX . "expedition as o";
16282 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16283 $sql .= ", " . MAIN_DB_PREFIX . "propal as o";
16284 } else {
16285 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16286 $sql .= ", " . MAIN_DB_PREFIX . $filterobj->table_element . " as o";
16287 }
16288 }
16289 $sql .= " WHERE a.entity IN (" . getEntity('agenda') . ")";
16290 if (!$force_filter_contact) {
16291 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
16292 $sql .= " AND a.fk_soc = " . ((int) $filterobj->id);
16293 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
16294 $sql .= " AND a.fk_project = o.rowid AND a.fk_project = " . ((int) $filterobj->id);
16295 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16296 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
16297 if ($filterobj->id) {
16298 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16299 }
16300 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16301 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
16302 if ($filterobj->id) {
16303 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16304 }
16305 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16306 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'shipping'";
16307 if ($filterobj->id) {
16308 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16309 }
16310 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16311 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'propal'";
16312 if ($filterobj->id) {
16313 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16314 }
16315 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16316 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
16317 if ($filterobj->id) {
16318 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16319 }
16320 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16321 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
16322 if ($filterobj->id) {
16323 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16324 }
16325 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16326 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
16327 if ($filterobj->id) {
16328 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16329 }
16330 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16331 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
16332 if ($filterobj->id) {
16333 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16334 }
16335 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16336 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
16337 if ($filterobj->id) {
16338 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16339 }
16340 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
16341 $sql .= " AND a.fk_contact = sp.rowid";
16342 if ($filterobj->id) {
16343 $sql .= " AND a.fk_contact = " . ((int) $filterobj->id);
16344 }
16345 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16346 $sql .= " AND a.fk_element = o.rowid";
16347 if ($filterobj->id) {
16348 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice'";
16349 }
16350 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16351 $sql .= " AND a.fk_element = o.rowid";
16352 if ($filterobj->id) {
16353 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice_supplier'";
16354 }
16355 } else {
16356 if (is_object($filterobj) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16357 $sql .= " AND a.fk_element = o.rowid";
16358 $sql .= " AND a.elementtype = '" . $db->escape($filterobj->element) . "'";
16359 if ($filterobj->id) {
16360 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16361 }
16362 }
16363 }
16364 } else {
16365 $sql .= " AND u.rowid = " . ((int) $filterobj->id);
16366 }
16367
16368 // Condition on actioncode
16369 if (!empty($actioncode) && $actioncode != '-1') {
16370 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16371 if ($actioncode == 'AC_NON_AUTO') {
16372 $sql .= " AND c.type != 'systemauto'";
16373 } elseif ($actioncode == 'AC_ALL_AUTO') {
16374 $sql .= " AND c.type = 'systemauto'";
16375 } else {
16376 if ($actioncode == 'AC_OTH') {
16377 $sql .= " AND c.type != 'systemauto'";
16378 } elseif ($actioncode == 'AC_OTH_AUTO') {
16379 $sql .= " AND c.type = 'systemauto'";
16380 }
16381 }
16382 } else {
16383 if ($actioncode == 'AC_NON_AUTO') {
16384 $sql .= " AND c.type != 'systemauto'";
16385 } elseif ($actioncode == 'AC_ALL_AUTO') {
16386 $sql .= " AND c.type = 'systemauto'";
16387 } else {
16388 $sql .= " AND c.code = '" . $db->escape($actioncode) . "'";
16389 }
16390 }
16391 }
16392 if ($donetodo == 'todo') {
16393 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '" . $db->idate($now) . "'))";
16394 } elseif ($donetodo == 'done') {
16395 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '" . $db->idate($now) . "'))";
16396 }
16397 if (is_array($filters) && $filters['search_agenda_label']) {
16398 $sql .= natural_search('a.label', $filters['search_agenda_label']);
16399 }
16400 }
16401
16402 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
16403 if (
16404 isModEnabled('mailing') && !empty($objcon->email)
16405 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')
16406 ) {
16407 $langs->load("mails");
16408
16409 $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";
16410 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
16411 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
16412 $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
16413 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16414 $sql2 .= ", '' as lastname, '' as firstname";
16415 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16416 $sql2 .= ", '' as lastname, '' as firstname";
16417 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16418 $sql2 .= ", '' as ref";
16419 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16420 $sql2 .= ", '' as ref";
16421 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16422 $sql2 .= ", '' as ref";
16423 }
16424 $sql2 .= " FROM " . MAIN_DB_PREFIX . "mailing as m, " . MAIN_DB_PREFIX . "mailing_cibles as mc, " . MAIN_DB_PREFIX . "user as u";
16425 $sql2 .= " WHERE mc.email = '" . $db->escape($objcon->email) . "'"; // Search is done on email.
16426 $sql2 .= " AND mc.statut = 1";
16427 $sql2 .= " AND u.rowid = m.fk_user_valid";
16428 $sql2 .= " AND mc.fk_mailing=m.rowid";
16429 }
16430
16431 $num = 0;
16432 $MAXWITHOUTPAGINATION = getDolGlobalInt('AGENDA_MAX_EVENTS_ON_PAGE_WITHOUT_PAGINATION', 100);
16433
16434 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
16435 if (!empty($sql) && !empty($sql2)) {
16436 $sql = $sql . " UNION " . $sql2;
16437 } elseif (empty($sql) && !empty($sql2)) {
16438 $sql = $sql2;
16439 }
16440
16441 //TODO Add navigation with this limits...
16442 $offset = 0;
16443 $limit = $MAXWITHOUTPAGINATION;
16444
16445 // Complete request and execute it with limit
16446 $sql .= $db->order($sortfield_new, $sortorder);
16447 if ($limit) {
16448 $sql .= $db->plimit($limit + 1, $offset);
16449 }
16450
16451 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
16452
16453 $resql = $db->query($sql);
16454 if ($resql) {
16455 $i = 0;
16456 $num = $db->num_rows($resql);
16457
16458 $imaxinloop = ($limit ? min($num, $limit) : $num);
16459 while ($i < $imaxinloop) {
16460 $obj = $db->fetch_object($resql);
16461
16462 if ($obj->type == 'action') {
16463 $contactaction = new ActionComm($db);
16464 $contactaction->id = $obj->id;
16465 $result = $contactaction->fetchResources();
16466 if ($result < 0) {
16467 dol_print_error($db);
16468 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
16469 }
16470
16471 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
16472 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
16473 $tododone = '';
16474 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
16475 $tododone = 'todo';
16476 }
16477
16478 $histo[$numaction] = array(
16479 'type' => $obj->type,
16480 'tododone' => $tododone,
16481 'id' => $obj->id,
16482 'datestart' => $db->jdate($obj->dp),
16483 'dateend' => $db->jdate($obj->dp2),
16484 'note' => $obj->label,
16485 'message' => $obj->message,
16486 'percent' => $obj->percent,
16487
16488 'userid' => $obj->user_id,
16489 'login' => $obj->user_login,
16490 'userfirstname' => $obj->user_firstname,
16491 'userlastname' => $obj->user_lastname,
16492 'userphoto' => $obj->user_photo,
16493 'msg_from' => $obj->msg_from,
16494
16495 'contact_id' => $obj->fk_contact,
16496 'socpeopleassigned' => $contactaction->socpeopleassigned,
16497 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
16498 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
16499 'fk_element' => $obj->fk_element,
16500 'elementtype' => $obj->elementtype,
16501 // Type of event
16502 'acode' => $obj->acode,
16503 'alabel' => $obj->alabel,
16504 'libelle' => $obj->alabel, // deprecated
16505 'apicto' => $obj->apicto
16506 );
16507 } else {
16508 $histo[$numaction] = array(
16509 'type' => $obj->type,
16510 'tododone' => 'done',
16511 'id' => $obj->id,
16512 'datestart' => $db->jdate($obj->dp),
16513 'dateend' => $db->jdate($obj->dp2),
16514 'note' => $obj->label,
16515 'message' => $obj->message,
16516 'percent' => $obj->percent,
16517 'acode' => $obj->acode,
16518
16519 'userid' => $obj->user_id,
16520 'login' => $obj->user_login,
16521 'userfirstname' => $obj->user_firstname,
16522 'userlastname' => $obj->user_lastname,
16523 'userphoto' => $obj->user_photo
16524 );
16525 }
16526
16527 $numaction++;
16528 $i++;
16529 }
16530 } else {
16531 dol_print_error($db);
16532 }
16533 }
16534
16535 // Set $out to show events
16536 $out = '';
16537
16538 if (!isModEnabled('agenda')) {
16539 $langs->loadLangs(array("admin", "errors"));
16540 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
16541 }
16542
16543 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
16544 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
16545
16546 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16547 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
16548 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
16549 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
16550
16551 $formactions = new FormActions($db);
16552
16553 $actionstatic = new ActionComm($db);
16554 $userstatic = new User($db);
16555 $contactstatic = new Contact($db);
16556 $userGetNomUrlCache = array();
16557 $contactGetNomUrlCache = array();
16558
16559 $out .= '<div class="filters-container" >';
16560 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
16561 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
16562
16563 if (
16564 $objcon && get_class($objcon) == 'Contact' &&
16565 (is_null($filterobj) || get_class($filterobj) == 'Societe')
16566 ) {
16567 $out .= '<input type="hidden" name="id" value="' . $objcon->id . '" />';
16568 } else {
16569 $out .= '<input type="hidden" name="id" value="' . $filterobj->id . '" />';
16570 }
16571 if (($filterobj && get_class($filterobj) == 'Societe')) {
16572 $out .= '<input type="hidden" name="socid" value="' . $filterobj->id . '" />';
16573 } else {
16574 $out .= '<input type="hidden" name="userid" value="' . $filterobj->id . '" />';
16575 }
16576
16577 $out .= "\n";
16578
16579 $out .= '<div class="div-table-responsive-no-min">';
16580 $out .= '<table class="noborder borderbottom centpercent">';
16581
16582 $out .= '<tr class="liste_titre">';
16583
16584 // Action column
16585 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16586 $out .= '<th class="liste_titre width50 middle">';
16587 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16588 $out .= $searchpicto;
16589 $out .= '</th>';
16590 }
16591
16592 // Date
16593 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ') . "\n";
16594
16595 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">' . $langs->trans("Search") . ' : </strong></th>';
16596 if ($donetodo) {
16597 $out .= '<th class="liste_titre"></th>';
16598 }
16599 // Type of event
16600 $out .= '<th class="liste_titre">';
16601 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="' . $langs->trans("ActionType") . '"></span>';
16602 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
16603 $out .= '</th>';
16604 // Label
16605 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
16606 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="' . $filters['search_agenda_label'] . '" placeholder="' . $langs->trans("Label") . '">';
16607 $out .= '</th>';
16608
16609 // Action column
16610 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
16611 $out .= '<th class="liste_titre width50 middle">';
16612 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16613 $out .= $searchpicto;
16614 $out .= '</th>';
16615 }
16616
16617 $out .= '</tr>';
16618
16619 $out .= '</table>';
16620
16621 $out .= '</form>';
16622 $out .= '</div>';
16623
16624 $out .= "\n";
16625
16626 $out .= '<ul class="timeline">';
16627
16628 if ($donetodo) {
16629 $tmp = '';
16630 if ($filterobj instanceof Societe) {
16631 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16632 }
16633 if ($filterobj instanceof User) {
16634 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16635 }
16636 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
16637 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
16638 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
16639 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
16640 if ($filterobj instanceof Societe) {
16641 $tmp .= '</a>';
16642 }
16643 if ($filterobj instanceof User) {
16644 $tmp .= '</a>';
16645 }
16646 $out .= getTitleFieldOfList($tmp);
16647 }
16648
16649 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/cactioncomm.class.php';
16650 $caction = new CActionComm($db);
16651 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
16652
16653 $actualCycleDate = false;
16654
16655 // Loop on each event to show it
16656 foreach ($histo as $key => $value) {
16657 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
16658
16659 $actionstatic->type_picto = $histo[$key]['apicto'];
16660 $actionstatic->type_code = $histo[$key]['acode'];
16661
16662 $labeltype = $actionstatic->type_code;
16663 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
16664 $labeltype = 'AC_OTH';
16665 }
16666 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16667 $labeltype = $langs->trans("Message");
16668 } else {
16669 if (!empty($arraylist[$labeltype])) {
16670 $labeltype = $arraylist[$labeltype];
16671 }
16672 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
16673 $labeltype .= ' - ' . $arraylist[$actionstatic->code]; // Use code in priority on type_code
16674 }
16675 }
16676
16677 $url = DOL_URL_ROOT . '/comm/action/card.php?id=' . $histo[$key]['id'];
16678
16679 $tmpa = dol_getdate($histo[$key]['datestart'], false);
16680
16681 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'] . '-' . $tmpa['yday']) {
16682 $actualCycleDate = $tmpa['year'] . '-' . $tmpa['yday'];
16683 $out .= '<!-- timeline time label -->';
16684 $out .= '<li class="time-label">';
16685 $out .= '<span class="timeline-badge-date">';
16686 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
16687 $out .= '</span>';
16688 $out .= '</li>';
16689 $out .= '<!-- /.timeline-label -->';
16690 }
16691
16692
16693 $out .= '<!-- timeline item -->' . "\n";
16694 $out .= '<li class="timeline-code-' . (!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none") . '">';
16695
16696 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
16697 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
16698 //$out .= $timelineicon;
16699 //var_dump($timelineicon);
16700 $out .= $typeicon;
16701
16702 $out .= '<div class="timeline-item">' . "\n";
16703
16704 $out .= '<span class="time timeline-header-action2">';
16705
16706 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
16707 $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") . ' ';
16708 $out .= $histo[$key]['id'];
16709 $out .= '</a> ';
16710 } else {
16711 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle') . ' ';
16712 }
16713
16714 if (
16715 $user->hasRight('agenda', 'allactions', 'create') ||
16716 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))
16717 ) {
16718 $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) . '">';
16719 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
16720 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
16721 $out .= '</a>';
16722 }
16723
16724 $out .= '</span>';
16725
16726 // Date
16727 $out .= '<span class="time"><i class="fa fa-clock valignmiddle"></i> ';
16728 $out .= '<span class="valignmiddle marginrightonly">';
16729 $out .= dol_print_date($histo[$key]['datestart'], 'day', 'tzuserrel');
16730 //$out .= '</span>';
16731 //$out .= '<span class="valignmiddle">'.
16732 $out .= ' '.dol_print_date($histo[$key]['datestart'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16733 //$out .= '</span>';
16734 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
16735 $tmpa = dol_getdate($histo[$key]['datestart'], true);
16736 $tmpb = dol_getdate($histo[$key]['dateend'], true);
16737 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
16738 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 1);
16739 } else {
16740 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'day', 'tzuserrel');
16741 //$out .= '<span class="valignmiddle marginrightonly">';
16742 $out .= ' '.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
16743 //$out .= '</span>';
16744 }
16745 }
16746 $late = 0;
16747 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16748 $late = 1;
16749 }
16750 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16751 $late = 1;
16752 }
16753 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
16754 $late = 1;
16755 }
16756 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
16757 $late = 1;
16758 }
16759 if ($late) {
16760 $out .= img_warning($langs->trans("Late")) . ' ';
16761 }
16762 $out .= "</span></span>\n";
16763
16764 $out .= '<span class="time">';
16765 $out .= $actionstatic->getLibStatut(2);
16766 $out .= '</span>';
16767
16768 // Ref
16769 $out .= '<h3 class="timeline-header">';
16770
16771 // Author of event
16772 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
16773 if ($histo[$key]['userid'] > 0) {
16774 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
16775 $userstatic->fetch($histo[$key]['userid']);
16776 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
16777 }
16778 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
16779 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
16780 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
16781 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
16782 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
16783 } else {
16784 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
16785 }
16786 }
16787 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
16788 } else {
16789 $out .= '<img class="photomemberphoto userphoto" alt="" src="/public/theme/common/user_anonymous.png">'.$langs->trans("Anonymous");
16790 }
16791 $out .= '</div>';
16792
16793 // Title
16794 $out .= ' <div class="messaging-title inline-block">';
16795 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
16796 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
16797 $out .= $labeltype . ' - ';
16798 }
16799
16800 $libelle = '';
16801
16802 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16803 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
16804 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16805 $out .= $langs->trans('TicketNewMessage');
16806 } elseif (isset($histo[$key]['type'])) {
16807 if ($histo[$key]['type'] == 'action') {
16808 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16809 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
16810 $libelle = $histo[$key]['note'];
16811 $actionstatic->id = $histo[$key]['id'];
16812 if ($libelle != $labeltype) {
16813 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16814 }
16815 } elseif ($histo[$key]['type'] == 'mailing') {
16816 $out .= '<a href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
16817 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
16818 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : 'Send mass mailing');
16819 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16820 } else {
16821 $libelle .= $histo[$key]['note'];
16822 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
16823 }
16824 }
16825 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
16826
16827 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
16828 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
16829 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
16830 } else {
16831 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
16832 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
16833 }
16834 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
16835 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
16836 }
16837 if ($link) {
16838 $out .= ' - ' . $link;
16839 }
16840 }
16841
16842 $out .= '</div>';
16843
16844 $out .= '</h3>';
16845
16846 // Message
16847 if (
16848 !empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
16849 && $actionstatic->code != 'AC_TICKET_CREATE'
16850 && $actionstatic->code != 'AC_TICKET_MODIFY'
16851 ) {
16852 $out .= '<div class="timeline-body wordbreak small">';
16853 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
16854 $newmess = dol_htmlentitiesbr($histo[$key]['message']);
16855 $truncatedText = dolGetFirstLineOfText($newmess, $truncateLines);
16856 // dolGetFirstLineOfText() cuts on <br> without caring about tag balance, so a message wrapped in
16857 // a block tag leaves the excerpt with an unclosed tag. The browser then nests the read more link
16858 // and the full text inside the excerpt, and hiding the excerpt hides the whole message (#39035).
16859 $truncatedText = dolCloseUnclosedHtmlTags($truncatedText);
16860 if ($truncateLines > 0 && strlen($newmess) > strlen($truncatedText)) {
16861 $out .= '<div class="readmore-block --closed" >';
16862 $out .= ' <div class="readmore-block__excerpt">';
16863 $out .= dolPrintHTML($truncatedText);
16864 $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>';
16865 $out .= ' </div>';
16866 $out .= ' <div class="readmore-block__full-text" >';
16867 $out .= dolPrintHTML($newmess);
16868 $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>';
16869 $out .= ' </div>';
16870 $out .= '</div>';
16871 } else {
16872 $out .= dolPrintHTML($newmess);
16873 }
16874
16875 $out .= '</div>';
16876 }
16877
16878 // Timeline footer
16879 $footer = '';
16880
16881 // Contact for this action
16882 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
16883 $contactList = '';
16884 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
16885 if (empty($conf->cache['contact'][$cid])) {
16886 $contact = new Contact($db);
16887 $contact->fetch($cid);
16888 $conf->cache['contact'][$cid] = $contact;
16889 } else {
16890 $contact = $conf->cache['contact'][$cid];
16891 }
16892
16893 if ($contact) {
16894 $contactList .= !empty($contactList) ? ', ' : '';
16895 $contactList .= $contact->getNomUrl(1);
16896 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16897 if (!empty($contact->phone_pro)) {
16898 $contactList .= '(' . dol_print_phone($contact->phone_pro) . ')';
16899 }
16900 }
16901 }
16902 }
16903
16904 $footer .= $langs->trans('ActionOnContact') . ' : ' . $contactList;
16905 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
16906 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
16907 $contact = new Contact($db);
16908 $result = $contact->fetch($histo[$key]['contact_id']);
16909 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
16910 } else {
16911 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
16912 $result = ($contact instanceof Contact) ? $contact->id : 0;
16913 }
16914
16915 if ($result > 0) {
16916 $footer .= $contact->getNomUrl(1);
16917 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
16918 if (!empty($contact->phone_pro)) {
16919 $footer .= '(' . dol_print_phone($contact->phone_pro) . ')';
16920 }
16921 }
16922 }
16923 }
16924
16925 $documents = getActionCommEcmList($actionstatic);
16926 if (!empty($documents)) {
16927 $footer .= '<div class="timeline-documents-container">';
16928 foreach ($documents as $doc) {
16929 $footer .= '<span id="document_' . $doc->id . '" class="timeline-documents" ';
16930 $footer .= ' data-id="' . $doc->id . '" ';
16931 $footer .= ' data-path="' . $doc->filepath . '"';
16932 $footer .= ' data-filename="' . dol_escape_htmltag($doc->filename) . '" ';
16933 $footer .= '>';
16934
16935 $filePath = DOL_DATA_ROOT . '/' . $doc->filepath . '/' . $doc->filename;
16936 $mime = dol_mimetype($filePath);
16937 if (empty($doc->agenda_id)) {
16938 $dir_ref = $actionstatic->id;
16939 $modulepart = 'actions';
16940 } else {
16941 $split_dir = explode('/', $doc->filepath);
16942 $modulepart = array_shift($split_dir);
16943 $dir_ref = implode('/', $split_dir);
16944 }
16945
16946 $file = $dir_ref . '/' . $doc->filename;
16947 $thumb = $dir_ref . '/thumbs/' . substr($doc->filename, 0, strrpos($doc->filename, '.')) . '_mini' . substr($doc->filename, strrpos($doc->filename, '.'));
16948 $doclink = dol_buildpath('document.php', 1) . '?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($file) . '&entity=' . $conf->entity;
16949 $viewlink = dol_buildpath('viewimage.php', 1) . '?modulepart=' . $modulepart . '&file=' . urlencode($thumb) . '&entity=' . $conf->entity;
16950
16951
16952
16953 $mimeAttr = ' mime="' . $mime . '" ';
16954 $class = '';
16955 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
16956 $class .= ' documentpreview';
16957 }
16958
16959 $footer .= '<a href="' . $doclink . '" class="btn-link ' . $class . '" target="_blank" rel="noopener noreferrer" ' . $mimeAttr . ' >';
16960 $footer .= img_mime($filePath) . ' ' . $doc->filename;
16961 $footer .= '</a>';
16962
16963 $footer .= '</span>';
16964 }
16965 $footer .= '</div>';
16966 }
16967
16968 if (!empty($footer)) {
16969 $out .= '<div class="timeline-footer">' . $footer . '</div>';
16970 }
16971
16972 $out .= '</div>' . "\n"; // end timeline-item
16973
16974 $out .= '</li>';
16975 $out .= '<!-- END timeline item -->';
16976 }
16977
16978 $out .= "</ul>\n";
16979
16980 // Code to manage the click on button data-read-more-action to show full description of an event
16981 $out .= '<script>
16982 jQuery(document).ready(function () {
16983 $(document).on("click", "[data-read-more-action]", function(e){
16984 console.log("We click on data-read-more-action");
16985 let readMoreBloc = $(this).closest(".readmore-block");
16986 if(readMoreBloc.length > 0){
16987 e.preventDefault();
16988 if($(this).attr("data-read-more-action") == "close"){
16989 readMoreBloc.addClass("--closed").removeClass("--open");
16990 $("html, body").animate({
16991 scrollTop: readMoreBloc.offset().top - 200
16992 }, 100);
16993 }else{
16994 readMoreBloc.addClass("--open").removeClass("--closed");
16995 }
16996 }
16997 });
16998 });
16999 </script>';
17000
17001
17002 if (empty($histo)) {
17003 $out .= '<span class="opacitymedium">' . $langs->trans("NoRecordFound") . '</span>';
17004 }
17005
17006 if ($num > $MAXWITHOUTPAGINATION) {
17007 $langs->load("errors");
17008 $colspan = 9;
17009 $out .= '<center><span class="opacitymedium">...' . $langs->trans("WarningTooManyDataPleaseUseMoreFilters", $MAXWITHOUTPAGINATION) . '...</span></center>';
17010 }
17011 }
17012
17013 if ($noprint) {
17014 return $out;
17015 } else {
17016 print $out;
17017 return null;
17018 }
17019}
17020
17032function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
17033{
17034 if ($timestamp === null) {
17035 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
17036 }
17037 $TParam = array(
17038 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
17039 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
17040 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
17041 );
17042 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
17043 $TParam = array_merge($TParam, array(
17044 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
17045 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
17046 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
17047 ));
17048 }
17049
17050 return '&' . http_build_query($TParam);
17051}
17052
17071function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
17072{
17073 global $conf, $db, $langs, $hookmanager;
17074 global $action, $object;
17075
17076 if (!is_object($langs)) {
17077 include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
17078 $langs = new Translate('', $conf);
17079 $langs->setDefaultLang();
17080 }
17081
17082 $langs->load("errors");
17083
17084 if ($printheader) {
17085 if (function_exists("llxHeader")) {
17086 llxHeader('');
17087 } elseif (function_exists("llxHeaderVierge")) {
17088 llxHeaderVierge('');
17089 }
17090 }
17091
17092 print '<div class="error">';
17093 if (empty($message)) {
17094 print $langs->trans("ErrorRecordNotFound");
17095 } else {
17096 print $langs->trans($message);
17097 }
17098 print '</div>';
17099 print '<br>';
17100
17101 if (empty($showonlymessage)) {
17102 if (empty($hookmanager)) {
17103 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
17104 $hookmanager = new HookManager($db);
17105 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
17106 $hookmanager->initHooks(array('main'));
17107 }
17108
17109 $parameters = array('message' => $message, 'params' => $params);
17110 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
17111 print $hookmanager->resPrint;
17112 }
17113
17114 if ($printfooter && function_exists("llxFooter")) {
17115 llxFooter();
17116 if (is_object($db)) {
17117 $db->close();
17118 }
17119 }
17120 exit(0);
17121}
17122
17151function array_merge_recursive_distinct(array $array1, array $array2): array
17152{
17153 $merged = $array1;
17154
17155 foreach ($array2 as $key => $value) {
17156 if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
17157 $merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
17158 } else {
17159 $merged[$key] = $value;
17160 }
17161 }
17162
17163 return $merged;
17164}
17165
17172function getObjectSocId($obj)
17173{
17174 if (!empty($obj->socid)) {
17175 return (int) $obj->socid;
17176 } elseif (!empty($obj->soc_id)) {
17177 return (int) $obj->soc_id;
17178 } elseif (!empty($obj->societe_id)) {
17179 return (int) $obj->societe_id;
17180 }
17181 return null;
17182}
$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:537
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:522
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:506
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:556
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert an image file or a PDF into another image format.
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
dol_is_dir($folder)
Test if filename is a directory.
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
isValidMailDomain($mail)
Return true if email has a domain name that can be resolved to MX type.
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access)
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_now($mode='gmt')
Return date for now.
dol_fiche_end($notab=0)
Show tab footer of a card.
verifCond($strToEvaluate, $onlysimplestring='1')
Verify if condition in string is ok or not.
recordNotFound($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Displays an error page when a record is not found.
getDolGlobalFloat($key, $default=0)
Return a Dolibarr global constant float value.
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formatted size.
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=2, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
Function that return localtax of a product line (according to seller, buyer and product vat rate) If ...
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
show_actions_messaging($conf, $langs, $db, $filterobj, $objcon=null, $noprint=0, $actioncode='', $donetodo='done', $filters=array(), $sortfield='a.datep, a.id', $sortorder='DESC')
Show html area with actions in messaging format.
dol_getmypid()
Return getmypid() or random PID when function is disabled Some web hosts disable this php function fo...
getLanguageCodeFromCountryCode($countrycode)
Return default language from country code.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
setEntity($currentobject)
Set entity id to use when to create an object.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
img_credit_card($brand, $morecss='fa-2x inline-block valignmiddle')
Return image of a credit card according to its brand name.
GETPOSTDATE($prefix, $hourTime='', $gm='auto', $saverestore='')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_ip($ip, $mode=0, $showname=0)
Return an IP formatted to be shown on screen.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
img_right($titlealt='default', $selected=0, $moreatt='')
Show right arrow logo.
dol_print_phone($phone, $countrycode='', $contactid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto='UTF-8')
This function is called to decode a HTML string (it decodes entities and br tags)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs=null, $mode=0, $extralangcode='')
Return a formatted address (part address/zip/town/state) according to country rules.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
getObjectSocId($obj)
Get the socid of an object, supporting legacy attribute names.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dolPrintHTML($s, $allowiframe=0)
Return a string (that can be on several lines) ready to be output on a HTML page.
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
dolPrintHTMLForTextArea($s, $allowiframe=0)
Return a string ready to be output on input textarea.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_eval_new($s)
Replace eval function to add more security.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Get formatted error messages to output (Used to show messages on html output).
dol_user_country()
Return country code for current user.
dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes=null)
Clean a string from some undesirable HTML tags.
getMultidirTemp($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its temporary ...
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
dol_get_fiche_end($notab=0)
Return tab footer of a card.
getDolEntity()
Return the current entity.
picto_required()
Return picto saying a field is required.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_action($titlealt, $numaction, $picto='', $moreatt='')
Show logo action.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
dol_sanitizePathName($str, $newstr='_', $unaccent=0, $allowdash=0)
Clean a string to use it as a path name.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dolPrintText($s)
Return a string label (possible on several lines and that should not contains any HTML) ready to be o...
utf8_valid($str)
Check if a string is in UTF8.
getPictoForType($key, $morecss='')
Return the picto for a data type.
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
getDolOptimizeSmallScreen()
Return if render must be optimized for small screen.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
Create a dialog with two buttons for export and overwrite of a website.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
jsonOrUnserialize($stringtodecode, $assoc=true)
Decode an encoded string.
dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
get_localtax_by_third($local)
Get values of localtaxes (1 or 2) for company country for the common vat with the highest value.
dol_escape_php($stringtoescape, $stringforquotes=2)
Returns text escaped for inclusion into a php string, build with double quotes " or '.
dolSetCookie(string $cookiename, string $cookievalue, int $expire=-1)
Set a cookie.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
dol_get_object_properties($obj, $properties=[])
Get properties for an object - including magic properties when requested.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_set_focus($selector)
Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
img_search($titlealt='default', $other='')
Show search logo.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
isValidPhone($phone)
Return true if phone number syntax is ok TODO Decide what to do with this.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
img_previous($titlealt='default', $moreatt='')
Show previous logo.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
getBrowserInfo($user_agent)
Return information about user browser.
dolGetFirstLetters($s, $nbofchar=1)
Return first letters of a strings.
dolPrintLabel($s, $escapeonlyhtmltags=0)
Return a string label (so on 1 line only and that should not contains any HTML) ready to be output on...
dol_clone_in_array($srcobject, $startlevel=0)
Create a clone of instance of object into a full array, using recursive call.
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
getMultidirVersion($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its versioned ...
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
dol_sanitizeUrl($stringtoclean, $type=1)
Clean a string to use it as an URL (into a href or src attribute)
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
img_printer($titlealt="default", $other='')
Show printer logo.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
ascii_check($str)
Check if a string is in ASCII.
get_date_range($date_start, $date_end, $format='', $outputlangs=null, $withparenthesis=1)
Format output for start and end date.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
getImgPictoConv($mode='fa')
Get array to convert the Dolibarr picto keys into Font awesome keys.
print_date_range($date_start, $date_end, $format='', $outputlangs=null)
Format output for start and end date.
getArrayOfSocialNetworks()
Get array of social network dictionary.
getDolDefaultContextPage($s)
Return the default context page string.
num2Alpha($n)
Return a numeric value into an Excel like column number.
dol_size($size, $type='')
Optimize a size for some browsers (phone, smarphone...)
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolPrintPassword($s)
Return a string ready to be output on an HTML attribute (alt, title, ...)
dol_escape_all($stringtoescape)
Returns text escaped for all protocols (so only alpha chars and numbers)
dolForgeSQLCriteriaCallback($matches)
Function to forge a SQL criteria from a USF (Universal Filter Syntax) string.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
dol_shutdown()
Function called at end of web php process.
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl='')
Format address string.
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
dol_escape_uri($stringtoescape)
Returns text escaped by RFC 3986 for inclusion into a clickable link.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
getDolDBType()
Return the current entity.
print_titre($title)
Show a title.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
img_error($titlealt='default')
Show error logo.
getTimelineIcon($actionstatic, &$histo, $key)
Get timeline icon.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formatted messages to output (Used to show messages on html output).
dolCloseUnclosedHtmlTags($text)
Close the HTML tags left open in a truncated HTML string.
get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
Return localtax vat rate of a product in a particular country or default country vat if product is un...
getUserRemoteIP($trusted=0)
Return the real IP of remote user.
buildParamDate($prefix, $timestamp=null, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_next($titlealt='default', $moreatt='')
Show next logo.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_string_is_good_iso($s, $clean=0)
Check if a string is a correct iso string If not, it will not be considered as HTML encoded even if i...
getNonce()
Return a random string to be used as a nonce value for js.
isStringVarMatching($var, $regextext, $matchrule=1)
Check if a variable with name $var start with $regextext.
dolSlugify($stringtoslugify)
Returns text slugified (lowercase and no special char, separator is "-").
dol_concat($text1, $text2)
Concat 2 strings.
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode='add', $filterorigmodule='')
Complete or removed entries into a head array (used to build tabs).
get_htmloutput_mesg($mesgstring='', $mesgarray=[], $style='ok', $keepembedded=0)
Get formatted messages to output (Used to show messages on html output).
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $selectlimitsuffix='', $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
dol_nboflines($s, $maxchar=0)
Return nb of lines of a clear text.
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js, dangerous content and external links.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
dol_escape_xml($stringtoescape)
Returns text escaped for inclusion into a XML string.
getActionCommEcmList($object)
getActionCommEcmList
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
img_edit_add($titlealt='default', $other='')
Show logo "+".
print_fiche_titre($title, $mesg='', $picto='generic', $pictoisfullpath=0, $id='')
Show a title with picto.
dolForgeDummyCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax string.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0, $allowphp=0)
Clean a string to keep only desirable HTML tags.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_validElement($element)
Return if var element is ok.
dol_sanitizeKeyCode($str)
Clean a string to use it as a key or code.
isModEnabled($module)
Is Dolibarr module enabled.
array_merge_recursive_distinct(array $array1, array $array2)
Recursively merges two arrays while preserving keys and replacing existing values.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
img_searchclear($titlealt='default', $other='')
Show search logo.
getWarningDelay($module, $parmlevel1, $parmlevel2='')
Return a warning delay You can use it like this: if (getWarningDelay('module', 'paramlevel1')) It rep...
utf8_check($str)
Check if a string is in UTF8.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Print formatted error messages to output (Used to show messages on html output).
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
dol_eval_standard($s, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
get_product_vat_for_country($idprod, $thirdpartytouseforcountry, $idprodfournprice=0)
Return vat rate of a product in a particular country, or default country vat if product is unknown.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dolPrintHTMLForAttributeUrl($s)
Return a string ready to be output on a href attribute (this one need a special because we need conte...
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
img_edit_remove($titlealt='default', $other='')
Show logo "-".
img_info($titlealt='default')
Show info logo.
getDoliDBInstance($type, $host, $user, $pass, $name, $port)
Return a DoliDB instance (database handler).
dol_sanitizeEmail($stringtoclean)
Clean a string to use it as an Email.
dol_nboflines_bis($text, $maxlinesize=0, $charset='UTF-8')
Return nb of lines of a formatted text with and (WARNING: string must not have mixed and br sep...
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
const MODULE_MAPPING
This mapping defines the conversion to the current internal names from the alternative allowed names ...
dolBECalculateStructuredCommunication($invoice_number, $invoice_type)
Calculate Structured Communication / BE Bank payment reference number.
dol_convertToWord($num, $langs, $currency='', $centimes=false)
Function to return a number into a text.
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
treeview li table
No Email.
div refaddress div address
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
dol_setcache($memoryid, $data, $expire=0, $filecache=0, $replace=0)
Save data into a memory area shared by all users, all sessions on server.
dol_getcache($memoryid, $filecache=0)
Read a memory area shared by all users, all sessions on server.
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:132
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:135
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition waf.inc.php:66