dolibarr 25.0.0-alpha
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-2026 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-2026 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 * Copyright (C) 2026 Pierre Ardoin <developpeur@lesmetiersdubatiment.fr>
31 *
32 * This program is free software; you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation; either version 3 of the License, or
35 * (at your option) any later version.
36 *
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 *
42 * You should have received a copy of the GNU General Public License
43 * along with this program. If not, see <https://www.gnu.org/licenses/>.
44 * or see https://www.gnu.org/
45 */
46
53//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
54
55// Function for better PHP x compatibility
56if (!function_exists('utf8_encode')) {
64 function utf8_encode($elements)
65 {
66 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
67 }
68}
69
70if (!function_exists('utf8_decode')) {
78 function utf8_decode($elements)
79 {
80 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
81 }
82}
83if (!function_exists('str_starts_with')) {
92 function str_starts_with($haystack, $needle)
93 {
94 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
95 }
96}
97if (!function_exists('str_ends_with')) {
106 function str_ends_with($haystack, $needle)
107 {
108 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
109 }
110}
111if (!function_exists('str_contains')) {
120 function str_contains($haystack, $needle)
121 {
122 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
123 }
124}
125
126
134function formatLogObject($data)
135{
136 if (getDolGlobalInt("MAIN_LOG_ON_ONE_LINE")) {
137 return json_encode($data);
138 }
139
140 return var_export($data, true);
141}
142
143
155function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
156{
157 global $conf;
158
159 $subdirectory = '';
160 if (!is_object($object) && empty($module)) {
161 return null;
162 }
163 if (empty($module) && !empty($object->element)) {
164 $module = $object->element;
165 }
166
167 // Special case for backward compatibility
168 switch ($module) {
169 case 'fichinter':
170 $module = 'ficheinter';
171 break;
172 case 'invoice_supplier':
173 $module = 'supplier_invoice';
174 break;
175 case 'order_supplier':
176 $module = 'supplier_order';
177 break;
178 case 'recruitmentjobposition':
179 $module = 'recruitment';
180 $subdirectory = '/recruitmentjobposition';
181 break;
182 case 'recruitmentcandidature':
183 $module = 'recruitment';
184 $subdirectory = '/recruitmentcandidature';
185 break;
186 case 'knowledgerecord':
187 $module = 'knowledgemanagement';
188 $subdirectory = '/knowledgerecord';
189 break;
190 case 'commande_fournisseur':
191 $module = 'fournisseur';
192 $subdirectory = '/commande';
193 break;
194 case 'expedition':
195 case 'shipment':
196 case 'shipping':
197 $module = 'expedition';
198 $subdirectory = '/sending';
199 break;
200 case 'company':
201 $module = 'societe';
202 break;
203 case 'service':
204 case 'produit':
205 $module = 'product';
206 break;
207 case 'project_task':
208 $module = 'projet';
209
210 // Fetch the project to build the correct path
211 $object->fetchProject();
212
213 $subdirectory = '/'.$object->project->ref;
214 break;
215 case 'action':
216 case 'actioncomm':
217 case 'event':
218 $module = 'agenda';
219 break;
220 default:
221 break;
222 }
223
224 // Get the relative path of directory
225 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
226 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
227 $s = '';
228 if ($mode != 'outputrel') {
229 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)] . $subdirectory;
230 }
231 if ($forobject && $object->id > 0) {
232 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
233 }
234 return dol_sanitizePathName($s);
235 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
236 $s = '';
237 if ($mode != 'outputrel') {
238 $s = $conf->$module->dir_output . $subdirectory;
239 }
240 if ($forobject && $object->id > 0) {
241 $s .= ($mode != 'outputrel' ? '/' : '') . get_exdir(0, 0, 0, 0, $object);
242 }
243 return dol_sanitizePathName($s);
244 } else {
245 return 'error-diroutput-not-defined-for-this-object=' . $module;
246 }
247 } elseif ($mode == 'temp') {
248 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
249 return dol_sanitizePathName($conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)]);
250 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
251 return dol_sanitizePathName($conf->$module->dir_temp);
252 } else {
253 return 'error-dirtemp-not-defined-for-this-object=' . $module;
254 }
255 } else {
256 return 'error-bad-value-for-mode';
257 }
258}
259
269function getMultidirTemp($object, $module = '', $forobject = 0)
270{
271 return getMultidirOutput($object, $module, $forobject, 'temp');
272}
273
283function getMultidirVersion($object, $module = '', $forobject = 0)
284{
285 return getMultidirOutput($object, $module, $forobject, 'version');
286}
287
288
297function getDolGlobalString($key, $default = '')
298{
299 global $conf;
300 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
301}
302
309{
310 global $dolibarr_login_badcharunauthorized;
311
312 if (isset($dolibarr_login_badcharunauthorized)) {
313 if ($dolibarr_login_badcharunauthorized === 'MAIN_LOGIN_BADCHARUNAUTHORIZED') {
314 return getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
315 }
316
317 return (string) $dolibarr_login_badcharunauthorized;
318 }
319
320 return ',@<>"\'';
321}
322
332function getDolGlobalInt($key, $default = 0)
333{
334 global $conf;
335 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
336}
337
347function getDolGlobalFloat($key, $default = 0)
348{
349 global $conf;
350 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
351}
352
361function getDolGlobalBool($key, $default = false)
362{
363 global $conf;
364 return (bool) ($conf->global->$key ?? $default);
365}
366
373{
374 global $conf;
375 return (string) $conf->currency;
376}
377
384{
385 global $conf;
386 return (string) $conf->dol_optimize_smallscreen;
387}
388
394function getDolEntity()
395{
396 global $conf;
397 return (int) $conf->entity;
398}
399
405function getDolDBType()
406{
407 global $conf;
408 return $conf->db->type;
409}
410
418{
419 return str_replace('_', '', basename(dirname($s)).basename($s, '.php'));
420}
421
431function getDolUserString($key, $default = '', $tmpuser = null)
432{
433 if (empty($tmpuser)) {
434 global $user;
435 $tmpuser = $user;
436 }
437
438 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
439}
440
449function getDolUserInt($key, $default = 0, $tmpuser = null)
450{
451 if (empty($tmpuser)) {
452 global $user;
453 $tmpuser = $user;
454 }
455
456 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
457}
458
459
468define(
469 'MODULE_MAPPING',
470 array(
471 // Map deprecated names to new names
472 'adherent' => 'member', // Has new directory
473 'member_type' => 'adherent_type', // No directory, but file called adherent_type
474 'banque' => 'bank', // Has new directory
475 'contrat' => 'contract', // Has new directory
476 'entrepot' => 'stock', // Has new directory
477 'projet' => 'project', // Has new directory
478 'categorie' => 'category', // Has old directory
479 'commande' => 'order', // Has old directory
480 'expedition' => 'shipping', // Has old directory
481 'facture' => 'invoice', // Has old directory
482 'fichinter' => 'intervention', // Has old directory
483 'ficheinter' => 'intervention', // Backup for 'fichinter'
484 'propale' => 'propal', // Has old directory
485 'societe' => 'thirdparty', // Has old directory
486 'socpeople' => 'contact', // Has old directory
487 'fournisseur' => 'supplier', // Has old directory
488
489 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
490 'product_price' => 'productprice', // NO directory
491 'product_fournisseur_price' => 'productsupplierprice', // NO directory
492 )
493);
494
501function isModEnabled($module)
502{
503 global $conf;
504
505 // Fix old names (map to new names)
506 $arrayconv = MODULE_MAPPING;
507 $arrayconvbis = array_flip(MODULE_MAPPING);
508
509 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
510 // Special cases: both use the same module.
511 $arrayconv['supplier_order'] = 'fournisseur';
512 $arrayconv['supplier_invoice'] = 'fournisseur';
513 }
514
515 $module_alt = $module;
516 if (!empty($arrayconv[$module])) {
517 $module_alt = $arrayconv[$module];
518 }
519 $module_bis = $module;
520 if (!empty($arrayconvbis[$module])) {
521 $module_bis = $arrayconvbis[$module];
522 }
523
524 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
525}
526
537function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
538{
539 global $conf;
540
541 // For compatibility with bad naming on module
542 $moduletomoduletouse = array(
543 'invoice' => 'facture',
544 );
545 $moduleParmsMapping = array(
546 'product' => 'produit',
547 );
548
549 if (!empty($moduletomoduletouse[$module])) {
550 $module = $moduletomoduletouse[$module];
551 }
552
553 $warningDelayPath = $parmlevel1;
554 if (!empty($moduleParmsMapping[$warningDelayPath])) {
555 $warningDelayPath = $moduleParmsMapping[$warningDelayPath];
556 }
557
558 if ($parmlevel2) {
559 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->$parmlevel2) && !empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
560 return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
561 }
562 } else {
563 if (!empty($conf->$module) && !empty($conf->$module->$warningDelayPath) && !empty($conf->$module->$warningDelayPath->warning_delay)) {
564 return (int) $conf->$module->$warningDelayPath->warning_delay;
565 }
566 }
567
568 return 0;
569}
570
577function isDolTms($timestamp)
578{
579 if ($timestamp === '') {
580 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page ' . $_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
581 return false;
582 }
583 if (is_null($timestamp) || !is_numeric($timestamp)) {
584 return false;
585 }
586
587 return true;
588}
589
601function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
602{
603 require_once DOL_DOCUMENT_ROOT . "/core/db/" . $type . '.class.php';
604
605 $class = 'DoliDB' . ucfirst($type);
606 $db = new $class($type, $host, $user, $pass, $name, $port);
607 return $db;
608}
609
627function getEntity($element, $shared = 1, $currentobject = null)
628{
629 global $conf, $mc, $hookmanager, $object, $action, $db;
630
631 if (!is_object($hookmanager)) {
632 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
633 $hookmanager = new HookManager($db);
634 }
635
636 // fix different element names (France to English)
637 switch ($element) {
638 case 'projet':
639 $element = 'project';
640 break;
641 case 'contrat':
642 $element = 'contract';
643 break; // "/contrat/class/contrat.class.php"
644 case 'order_supplier':
645 $element = 'supplier_order';
646 break; // "/fourn/class/fournisseur.commande.class.php"
647 case 'invoice_supplier':
648 $element = 'supplier_invoice';
649 break; // "/fourn/class/fournisseur.facture.class.php"
650 }
651
652 if (is_object($mc)) {
653 $out = $mc->getEntity($element, $shared, $currentobject);
654 } else {
655 $out = '';
656 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
657 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
658 $addzero[] = 'c_holiday_types';
659 }
660 if (in_array($element, $addzero)) {
661 $out .= '0,';
662 }
663 $out .= ((int) $conf->entity);
664 }
665
666 // Manipulate entities to query on the fly
667 $parameters = array(
668 'element' => $element,
669 'shared' => $shared,
670 'object' => $object,
671 'currentobject' => $currentobject,
672 'out' => $out
673 );
674 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
675 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
676
677 if (is_numeric($reshook)) {
678 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
679 $out .= ',' . $hookmanager->resPrint; // add
680 } elseif ($reshook == 1) {
681 $out = $hookmanager->resPrint; // replace
682 }
683 }
684
685 return $out;
686}
687
694function setEntity($currentobject)
695{
696 global $conf, $mc;
697
698 if (is_object($mc) && method_exists($mc, 'setEntity')) {
699 return $mc->setEntity($currentobject);
700 } else {
701 return ((is_object($currentobject) && $currentobject->id > 0 && ((int) $currentobject->entity) > 0) ? (int) $currentobject->entity : $conf->entity);
702 }
703}
704
711function isASecretKey($keyname)
712{
713 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
714}
715
716
723function num2Alpha($n)
724{
725 $r = '';
726 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
727 $r = chr($n % 26 + 0x41) . $r;
728 }
729 return $r;
730}
731
732
749function getBrowserInfo($user_agent)
750{
751 include_once DOL_DOCUMENT_ROOT . '/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
752
753 $name = 'unknown';
754 $version = '';
755 $os = 'unknown';
756 $phone = '';
757
758 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
759
760 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal Bad definition of Mobile_Detect function
761 $detectmobile = new Mobile_Detect(null, $user_agent);
762 $tablet = $detectmobile->isTablet();
763
764 if ($detectmobile->isMobile()) {
765 $phone = 'unknown';
766
767 // If phone/smartphone, we set phone os name.
768 if ($detectmobile->is('AndroidOS')) {
769 $os = $phone = 'android';
770 } elseif ($detectmobile->is('BlackBerryOS')) {
771 $os = $phone = 'blackberry';
772 } elseif ($detectmobile->is('iOS')) {
773 $os = 'ios';
774 $phone = 'iphone';
775 } elseif ($detectmobile->is('PalmOS')) {
776 $os = $phone = 'palm';
777 } elseif ($detectmobile->is('SymbianOS')) {
778 $os = 'symbian';
779 } elseif ($detectmobile->is('webOS')) {
780 $os = 'webos';
781 } elseif ($detectmobile->is('MaemoOS')) {
782 $os = 'maemo';
783 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
784 $os = 'windows';
785 }
786 }
787
788 // OS
789 if (preg_match('/linux/i', $user_agent)) {
790 $os = 'linux';
791 } elseif (preg_match('/macintosh/i', $user_agent)) {
792 $os = 'macintosh';
793 } elseif (preg_match('/windows/i', $user_agent)) {
794 $os = 'windows';
795 }
796
797 // Name
798 $reg = array();
799 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
800 $name = 'firefox';
801 $version = empty($reg[2]) ? '' : $reg[2];
802 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
803 $name = 'edge';
804 $version = empty($reg[2]) ? '' : $reg[2];
805 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
806 $name = 'chrome';
807 $version = empty($reg[2]) ? '' : $reg[2];
808 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
809 // we can have 'chrome (Mozilla...) chrome x.y' in one string
810 $name = 'chrome';
811 } elseif (preg_match('/iceweasel/i', $user_agent)) {
812 $name = 'iceweasel';
813 } elseif (preg_match('/epiphany/i', $user_agent)) {
814 $name = 'epiphany';
815 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
816 $name = 'safari';
817 $version = empty($reg[2]) ? '' : $reg[2];
818 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
819 // Safari is often present in string for mobile but its not.
820 $name = 'opera';
821 $version = empty($reg[2]) ? '' : $reg[2];
822 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
823 $name = 'ie';
824 $version = end($reg);
825 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
826 // MS products at end
827 $name = 'ie';
828 $version = end($reg);
829 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
830 // MS products at end
831 $name = 'textbrowser';
832 $version = empty($reg[3]) ? '' : $reg[3];
833 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
834 // MS products at end
835 $name = 'textbrowser';
836 $version = empty($reg[1]) ? '' : $reg[1];
837 }
838
839 if ($tablet) {
840 $layout = 'tablet';
841 } elseif ($phone) {
842 $layout = 'phone';
843 } else {
844 $layout = 'classic';
845 }
846
847 return array(
848 'browsername' => $name,
849 'browserversion' => $version,
850 'browseros' => $os,
851 'browserua' => $user_agent,
852 'layout' => $layout, // tablet, phone, classic
853 'phone' => $phone, // deprecated
854 'tablet' => $tablet // deprecated
855 );
856}
857
863function dol_shutdown()
864{
865 global $db;
866 $disconnectdone = false;
867 $depth = 0;
868 if (is_object($db) && !empty($db->connected)) {
869 $depth = $db->transaction_opened;
870 $disconnectdone = $db->close();
871 }
872 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));
873}
874
884function GETPOSTISSET($paramname)
885{
886 $isset = false;
887
888 $relativepathstring = $_SERVER["PHP_SELF"];
889 // Clean $relativepathstring
890 if (constant('DOL_URL_ROOT')) {
891 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
892 }
893 $relativepathstring = ltrim($relativepathstring, '/');
894 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
895
896 // Code for search criteria persistence.
897 // Retrieve values if restore_lastsearch_values
898 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
899 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
900 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
901 if (is_array($tmp)) {
902 foreach ($tmp as $key => $val) {
903 if ($key == $paramname) { // We are on the requested parameter
904 $isset = true;
905 break;
906 }
907 }
908 }
909 }
910 // If there is saved contextpage, limit, page or mode
911 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
912 $isset = true;
913 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
914 $isset = true;
915 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
916 $isset = true;
917 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
918 $isset = true;
919 }
920 } else {
921 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
922 }
923
924 return $isset;
925}
926
935function GETPOSTISARRAY($paramname, $method = 0)
936{
937 // for $method test need return the same $val as GETPOST
938 if (empty($method)) {
939 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
940 } elseif ($method == 1) {
941 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
942 } elseif ($method == 2) {
943 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
944 } elseif ($method == 3) {
945 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
946 } else {
947 $val = 'BadFirstParameterForGETPOST';
948 }
949
950 return is_array($val);
951}
952
953
964function GETPOSTINT($paramname, $method = 0, $nodefault = 0)
965{
966 return (int) GETPOST($paramname, 'int', $method, null, null, 0, $nodefault);
967}
968
982function GETPOSTFLOAT($paramname, $rounding = '', $option = 2)
983{
984 // 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.)
985 return (float) price2num(GETPOST($paramname), $rounding, $option);
986}
987
1003function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto', $saverestore = '')
1004{
1005 $m = array();
1006 if ($hourTime === 'getpost' || $hourTime === 'getpostend') {
1007 $hour = (GETPOSTISSET($prefix . 'hour') && GETPOSTINT($prefix . 'hour') >= 0) ? GETPOSTINT($prefix . 'hour') : ($hourTime === 'getpostend' ? 23 : 0);
1008 $minute = (GETPOSTISSET($prefix . 'min') && GETPOSTINT($prefix . 'min') >= 0) ? GETPOSTINT($prefix . 'min') : ($hourTime === 'getpostend' ? 59 : 0);
1009 $second = (GETPOSTISSET($prefix . 'sec') && GETPOSTINT($prefix . 'sec') >= 0) ? GETPOSTINT($prefix . 'sec') : ($hourTime === 'getpostend' ? 59 : 0);
1010 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1011 $hour = intval($m[1]);
1012 $minute = intval($m[2]);
1013 $second = intval($m[3]);
1014 } elseif ($hourTime === 'end') {
1015 $hour = 23;
1016 $minute = 59;
1017 $second = 59;
1018 } else {
1019 $hour = $minute = $second = 0;
1020 }
1021
1022 if (
1023 $saverestore
1024 && !GETPOSTISSET($prefix . 'day')
1025 && !GETPOSTISSET($prefix . 'month')
1026 && !GETPOSTISSET($prefix . 'year')
1027 && isset($_SESSION['DOLDATE_' . $saverestore . '_day'])
1028 && isset($_SESSION['DOLDATE_' . $saverestore . '_month'])
1029 && isset($_SESSION['DOLDATE_' . $saverestore . '_year'])
1030 ) {
1031 $day = $_SESSION['DOLDATE_' . $saverestore . '_day'];
1032 $month = $_SESSION['DOLDATE_' . $saverestore . '_month'];
1033 $year = $_SESSION['DOLDATE_' . $saverestore . '_year'];
1034 } else {
1035 $month = GETPOSTINT($prefix . 'month');
1036 $day = GETPOSTINT($prefix . 'day');
1037 $year = GETPOSTINT($prefix . 'year');
1038 }
1039
1040 // normalize out of range values
1041 $hour = (int) min($hour, 23);
1042 $minute = (int) min($minute, 59);
1043 $second = (int) min($second, 59);
1044
1045 if ($saverestore) {
1046 $_SESSION['DOLDATE_' . $saverestore . '_day'] = $day;
1047 $_SESSION['DOLDATE_' . $saverestore . '_month'] = $month;
1048 $_SESSION['DOLDATE_' . $saverestore . '_year'] = $year;
1049 }
1050
1051 //print "$hour, $minute, $second, $month, $day, $year, $gm<br>";
1052 return dol_mktime($hour, $minute, $second, $month, $day, $year, $gm);
1053}
1054
1096function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0, $nodefault = 0)
1097{
1098 global $langs, $mysoc, $user, $conf;
1099
1100 if (empty($paramname)) { // Explicit test for null for phan.
1101 return 'BadFirstParameterForGETPOST';
1102 }
1103 if (empty($check)) {
1104 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);
1105 // Enable this line to know who call the GETPOST with '' $check parameter.
1106 //var_dump(getCallerInfoString());
1107 }
1108 if (in_array($paramname, array('sortfield', 'sortorder'))) { // Force the $check to a more appropriated value
1109 $check = 'aZ09comma';
1110 }
1111
1112 if (empty($method)) {
1113 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
1114 } elseif ($method == 1) {
1115 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
1116 } elseif ($method == 2) {
1117 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
1118 } elseif ($method == 3) {
1119 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
1120 } else {
1121 return 'BadThirdParameterForGETPOST';
1122 }
1123
1124 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
1125
1126 if (empty($method) || $method == 3 || $method == 4) {
1127 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
1128 // Clean $relativepathstring
1129 if (constant('DOL_URL_ROOT')) {
1130 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
1131 }
1132 $relativepathstring = ltrim($relativepathstring, '/');
1133 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1134
1135 // Code for search criteria persistence.
1136 // Retrieve saved values if restore_lastsearch_values is set
1137 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
1138 if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
1139 $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
1140 if (is_array($tmp)) {
1141 foreach ($tmp as $key => $val) {
1142 if ($key == $paramname) { // We are on the requested parameter
1143 $out = $val;
1144 break;
1145 }
1146 }
1147 }
1148 }
1149 // If there is saved contextpage, page or limit
1150 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
1151 $out = $_SESSION['lastsearch_contextpage_' . $relativepathstring];
1152 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
1153 $out = $_SESSION['lastsearch_limit_' . $relativepathstring];
1154 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
1155 $out = $_SESSION['lastsearch_page_' . $relativepathstring];
1156 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_' . $relativepathstring])) {
1157 $out = $_SESSION['lastsearch_mode_' . $relativepathstring];
1158 }
1159 } elseif (!isset($_GET['sortfield'])) {
1160 // Else, retrieve default values if we are not doing a sort
1161 // 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
1162 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1163 // Search default value from $object->field
1164 global $object;
1165 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
1166 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
1167 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
1168 $out = $object->fields[$paramname]['default'];
1169 }
1170 }
1171 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1172 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
1173 // Now search in setup to overwrite default values
1174 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
1175 if (isset($user->default_values[$relativepathstring]['createform'])) {
1176 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
1177 $qualified = 0;
1178 if ($defkey != '_noquery_') {
1179 $tmpqueryarraytohave = explode('&', $defkey);
1180 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1181 $foundintru = 0;
1182 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1183 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1184 $foundintru = 1;
1185 }
1186 }
1187 if (!$foundintru) {
1188 $qualified = 1;
1189 }
1190 } else {
1191 $qualified = 1;
1192 }
1193
1194 if ($qualified) {
1195 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
1196 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
1197 break;
1198 }
1199 }
1200 }
1201 }
1202 }
1203 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname]) && empty($nodefault)) {
1204 // Management of default search_filters and sort order
1205 if (!empty($user->default_values)) {
1206 // $user->default_values defined from menu 'Setup - Default values'
1207 //var_dump($user->default_values[$relativepathstring]);
1208 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
1209 // Sorted on which fields ? ASC or DESC ?
1210 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
1211 // Even if paramname is sortfield, data are stored into ['sortorder...']
1212 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
1213 $qualified = 0;
1214 if ($defkey != '_noquery_') {
1215 $tmpqueryarraytohave = explode('&', $defkey);
1216 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1217 $foundintru = 0;
1218 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1219 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1220 $foundintru = 1;
1221 }
1222 }
1223 if (!$foundintru) {
1224 $qualified = 1;
1225 }
1226 } else {
1227 $qualified = 1;
1228 }
1229
1230 if ($qualified) {
1231 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1232 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
1233 if ($out) {
1234 $out .= ', ';
1235 }
1236 if ($paramname == 'sortfield') {
1237 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
1238 }
1239 if ($paramname == 'sortorder') {
1240 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
1241 }
1242 }
1243 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
1244 }
1245 }
1246 }
1247 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
1248 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
1249 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
1250 continue;
1251 }
1252 $qualified = 0;
1253 if ($defkey != '_noquery_') {
1254 $tmpqueryarraytohave = explode('&', $defkey);
1255 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1256 $foundintru = 0;
1257 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1258 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1259 $foundintru = 1;
1260 }
1261 }
1262 if (!$foundintru) {
1263 $qualified = 1;
1264 }
1265 } else {
1266 $qualified = 1;
1267 }
1268
1269 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
1270 // We must keep $_POST and $_GET here
1271 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
1272 // We made a search from quick search menu, do we still use default filter ?
1273 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
1274 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1275 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1276 }
1277 } else {
1278 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1279 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1280 }
1281 break;
1282 }
1283 }
1284 }
1285 }
1286 }
1287 }
1288 }
1289 }
1290
1291 // Replace 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)
1292 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1293 // 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.
1294 '@phan-var-force string $paramname';
1295 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1296 if (preg_match('/__([A-Z0-9]+(?:_[A-Z0-9]+){0,3})__/i', $out)) { // If there is at least one substitution key, we try to replace all known substitution keys
1297 $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $user, array('mycompany', 'date', 'system', 'user'));
1298 complete_substitutions_array($substitutionarray, $langs, $user);
1299
1300 $out = make_substitutions($out, $substitutionarray, $langs);
1301 }
1302 }
1303
1304 // Check type of variable and make sanitization according to this
1305 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:int'
1306 $tmpcheck = 'alphanohtml';
1307 if ($out === null || $out === '') {
1308 $out = array();
1309 } elseif (!is_array($out)) {
1310 $out = explode(',', $out);
1311 } else {
1312 $tmparray = explode(':', $check);
1313 if (!empty($tmparray[1])) {
1314 $tmpcheck = $tmparray[1];
1315 }
1316 }
1317 foreach ($out as $outkey => $outval) {
1318 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1319 }
1320 } else {
1321 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1322 // 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
1323 if (strpos($paramname, 'search_') === 0) {
1324 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1325 }
1326
1327 // @phan-suppress-next-line UnknownSanitizeType
1328 $out = sanitizeVal($out, $check, $filter, $options);
1329 }
1330
1331 // Sanitizing for special parameters.
1332 // 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.
1333 // @TODO Merge backtopage with backtourl
1334 // @TODO Rename backtolist into backtopagelist
1335 // @TODO Merge urlfrom into backtourl
1336 if (preg_match('/^backto/i', $paramname) || preg_match('/^urlfrom/i', $paramname)) {
1337 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1338 $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.
1339 do {
1340 $oldstringtoclean = $out;
1341 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1342 $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'
1343 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1344 } while ($oldstringtoclean != $out);
1345 }
1346
1347 // Code for search criteria persistence.
1348 // Save data into session if key start with 'search_'
1349 if (empty($method) || $method == 3 || $method == 4) {
1350 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1351 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1352
1353 // We save search key only if $out not empty that means:
1354 // - posted value not empty, or
1355 // - 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).
1356
1357 if ($out != '' && isset($user)) { // $out = '0' or 'abc', it is a search criteria to keep
1358 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1359 }
1360 }
1361 }
1362
1363 return $out;
1364}
1365
1375function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1376{
1377 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1378 // Check is done after replacement
1379 if ($out === null) {
1380 $out = '';
1381 }
1382 switch ($check) {
1383 case 'none':
1384 case 'password':
1385 break;
1386 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1387 if (!is_numeric($out)) {
1388 $out = '';
1389 }
1390 break;
1391 case 'intcomma':
1392 if (is_array($out)) {
1393 $out = implode(',', $out);
1394 }
1395 if (preg_match('/[^0-9,-]+/i', $out)) {
1396 $out = '';
1397 }
1398 break;
1399 case 'san_alpha':
1400 dol_syslog("Use of parameter value 'san_alpha' in GETPOST is deprecated. Use 'alphanohtml', 'aZ09comma', ...", LOG_WARNING);
1401 $out = filter_var($out, FILTER_SANITIZE_STRING);
1402 break;
1403 case 'email':
1404 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1405 break;
1406 case 'url':
1407 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1408 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1409 // TODO Allow ( ) but only into password of https://login:password@domain...
1410 break;
1411 case 'aZ':
1412 if (!is_array($out)) {
1413 $out = trim($out);
1414 if (preg_match('/[^a-z]+/i', $out)) {
1415 $out = '';
1416 }
1417 }
1418 break;
1419 case 'aZ09':
1420 if (!is_array($out)) {
1421 $out = trim($out);
1422 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1423 $out = '';
1424 }
1425 }
1426 break;
1427 case 'aZ09arobase': // great to sanitize $objecttype parameter
1428 if (!is_array($out)) {
1429 $out = trim($out);
1430 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1431 $out = '';
1432 }
1433 }
1434 break;
1435 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1436 if (!is_array($out)) {
1437 $out = trim($out);
1438 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1439 $out = '';
1440 }
1441 }
1442 break;
1443 case 'alpha': // No html and no ../ and "
1444 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1445 if (!is_array($out)) {
1446 $out = trim($out);
1447 do {
1448 $oldstringtoclean = $out;
1449 // Remove html tags
1450 $out = dol_string_nohtmltag($out, 0);
1451 // 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).
1452 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1453 // Remove also other dangerous string sequences
1454 // '../' or '..\' is dangerous because it allows dir transversals
1455 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1456 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1457 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1458 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1459 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1460 } while ($oldstringtoclean != $out);
1461 // keep lines feed
1462 }
1463 break;
1464 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'
1465 if (!is_array($out)) {
1466 $out = trim($out);
1467 do {
1468 $oldstringtoclean = $out;
1469 // Decode html entities
1470 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1471 // 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).
1472 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1473 // Remove also other dangerous string sequences
1474 // '../' or '..\' is dangerous because it allows dir transversals
1475 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1476 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1477 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1478 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1479 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1480 } while ($oldstringtoclean != $out);
1481 }
1482 break;
1483 case 'nohtml': // No html. Valid for JSON strings.
1484 $out = dol_string_nohtmltag($out, 0);
1485 break;
1486 case 'restricthtmlnolink':
1487 case 'restricthtml': // Recommended for most html textarea
1488 case 'restricthtmlallowclass':
1489 case 'restricthtmlallowiframe':
1490 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1491 case 'restricthtmlallowunvalid':
1492 $out = dol_htmlwithnojs($out, 1, $check);
1493 break;
1494 case 'custom':
1495 if (!empty($out)) {
1496 if (empty($filter)) {
1497 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1498 }
1499 if (is_null($options)) {
1500 $options = 0;
1501 }
1502 $out = filter_var($out, $filter, $options);
1503 }
1504 break;
1505 default:
1506 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1507 $out = GETPOST($out, 'alphanohtml');
1508 break;
1509 }
1510
1511 return $out;
1512}
1513
1522function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1523{
1524 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/securitycore.lib.php';
1525
1526 global $dolibarr_main_force_https;
1527
1528 if ($expire == -1) {
1529 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1530 }
1531
1532 if (PHP_VERSION_ID < 70300) {
1533 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1534 } else {
1535 // Only available for php >= 7.3
1536 $cookieparams = array(
1537 'expires' => empty($cookievalue) ? 0 : $expire,
1538 'path' => '/',
1539 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1540 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1541 'httponly' => true,
1542 'samesite' => 'Lax' // None || Lax || Strict
1543 );
1544 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1545 }
1546 if (empty($cookievalue)) {
1547 unset($_COOKIE[$cookiename]);
1548 }
1549}
1550
1551if (!function_exists('dol_getprefix')) {
1562 function dol_getprefix($mode = '')
1563 {
1564 // If prefix is for email (we need to have $conf already loaded for this case)
1565 if ($mode == 'email') {
1566 global $conf;
1567
1568 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1569 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1570 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1571 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1572 return $_SERVER["SERVER_NAME"];
1573 }
1574 }
1575
1576 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1577 if (!empty($conf->file->instance_unique_id)) {
1578 return sha1('dolibarr' . $conf->file->instance_unique_id);
1579 }
1580
1581 // For backward compatibility when instance_unique_id is not set
1582 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1583 }
1584
1585 // If prefix is for session (no need to have $conf loaded)
1586 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1587 $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
1588
1589 // The recommended value (may be not defined for old versions)
1590 if (!empty($tmp_instance_unique_id)) {
1591 return sha1('dolibarr' . $tmp_instance_unique_id);
1592 }
1593
1594 // For backward compatibility when instance_unique_id is not set
1595 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1596 return sha1($_SERVER["SERVER_NAME"] . $_SERVER["DOCUMENT_ROOT"] . DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1597 } else {
1598 return sha1(DOL_DOCUMENT_ROOT . DOL_URL_ROOT);
1599 }
1600 }
1601}
1602
1613function dol_include_once($relpath, $classname = '')
1614{
1615 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']
1616
1617 if (strpos($relpath, '..') !== false) {
1618 // Found a not valid path
1619 dol_syslog('functions::dol_include_once Tried to load a file with a path including a forbidden sequence ".." : ' . $relpath, LOG_WARNING);
1620 return false;
1621 }
1622 if (!preg_match('/\.php$/', $relpath)) {
1623 // Found a not valid path
1624 dol_syslog('functions::dol_include_once Tried to load a file that is not a PHP file : ' . $relpath, LOG_WARNING);
1625 return false;
1626 }
1627
1628 $fullpath = dol_buildpath($relpath);
1629
1630 if (!file_exists($fullpath)) {
1631 dol_syslog('functions::dol_include_once Tried to load unexisting file: ' . $relpath, LOG_WARNING);
1632 return false;
1633 }
1634 if (!empty($classname) && !class_exists($classname)) {
1635 return include $fullpath;
1636 } else {
1637 return include_once $fullpath;
1638 }
1639}
1640
1641
1655function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1656{
1657 global $conf;
1658
1659 $path = preg_replace('/^\//', '', $path);
1660
1661 if (empty($type)) { // For a filesystem path
1662 $res = DOL_DOCUMENT_ROOT . '/' . $path; // Standard default path
1663 if (is_array($conf->file->dol_document_root)) {
1664 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1665 if ($key == 'main') {
1666 continue;
1667 }
1668 // if (@file_exists($dirroot.'/'.$path)) {
1669 if (@file_exists($dirroot . '/' . $path)) { // avoid [php:warn]
1670 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/...'
1671 continue;
1672 }
1673 $res = $dirroot . '/' . $path;
1674 return $res;
1675 }
1676 }
1677 }
1678 if ($returnemptyifnotfound) {
1679 // Not found into alternate dir
1680 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1681 return '';
1682 }
1683 }
1684 } else {
1685 // For an url path
1686 // We try to get local path of file on filesystem from url
1687 // Note that trying to know if a file on disk exist by forging path on disk from url
1688 // works only for some web server and some setup. This is bugged when
1689 // using proxy, rewriting, virtual path, etc...
1690 $res = '';
1691 if ($type == 1) {
1692 $res = DOL_URL_ROOT . '/' . $path; // Standard value
1693 }
1694 if ($type == 2) {
1695 $res = DOL_MAIN_URL_ROOT . '/' . $path; // Standard value
1696 }
1697 if ($type == 3) {
1698 $res = DOL_URL_ROOT . '/' . $path;
1699 }
1700
1701 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1702 if ($key == 'main') {
1703 if ($type == 3) {
1704 /*global $dolibarr_main_url_root;*/
1705
1706 // Define $urlwithroot
1707 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1708 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1709 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1710
1711 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . '/' . $path; // Test on start with http is for old conf syntax
1712 }
1713 continue;
1714 }
1715 $regs = array();
1716 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1717 if (!empty($regs[1])) {
1718 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1719 //if (file_exists($dirroot.'/'.$regs[1])) {
1720 if (@file_exists($dirroot . '/' . $regs[1])) { // avoid [php:warn]
1721 if ($type == 1) {
1722 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1723 } elseif ($type == 2) {
1724 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT) . $conf->file->dol_url_root[$key] . '/' . $path;
1725 } elseif ($type == 3) {
1726 /*global $dolibarr_main_url_root;*/
1727
1728 // Define $urlwithroot
1729 $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($conf->file->dol_main_url_root));
1730 $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
1731 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1732
1733 $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
1734 }
1735 break;
1736 }
1737 }
1738 }
1739 }
1740
1741 return $res;
1742}
1743
1753function dolBuildUrl($url, $params = [], $addtoken = false, $anchor = '')
1754{
1755 global $db, $hookmanager;
1756
1757 if (!is_object($hookmanager)) {
1758 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
1759 $hookmanager = new HookManager($db);
1760 }
1761 if ((!isset($params['mainmenu']) || empty($params['mainmenu'])) && GETPOSTISSET('mainmenu')) {
1762 $params = array_merge($params, ['mainmenu' => (GETPOST('mainmenu', 'restricthtml'))]);
1763 }
1764 if ((!isset($params['leftmenu'])/* || empty($params['leftmenu']) */) && GETPOSTISSET('leftmenu')) { // do not fill leftmenu if we have leftmenu=
1765 $params = array_merge($params, ['leftmenu' => (GETPOST('leftmenu', 'restricthtml'))]);
1766 }
1767 $parameters = [
1768 'path' => &$url,
1769 'params' => &$params,
1770 'addtoken' => &$addtoken,
1771 ];
1772 $hookmanager->executeHooks('buildurl', $parameters);
1773 if ($addtoken) {
1774 $params = array_merge($params, ['token' => newToken()]);
1775 }
1776 if ($params) {
1777 $url .= '?' . http_build_query($params);
1778 }
1779 if ($anchor) {
1780 $url .= '#' . preg_replace('/[^a-z]/i', '', $anchor);
1781 }
1782
1783 return $url;
1784}
1785
1796function dol_get_object_properties($obj, $properties = [])
1797{
1798 // Get real properties using get_object_vars() if $properties is empty
1799 if (empty($properties)) {
1800 return get_object_vars($obj);
1801 }
1802
1803 $existingProperties = [];
1804 $realProperties = get_object_vars($obj);
1805
1806 // Get the real or magic property values
1807 foreach ($properties as $property) {
1808 if (array_key_exists($property, $realProperties)) {
1809 // Real property, add the value
1810 $existingProperties[$property] = $obj->{$property};
1811 } elseif (property_exists($obj, $property)) {
1812 // Magic property
1813 $existingProperties[$property] = $obj->{$property};
1814 }
1815 }
1816
1817 return $existingProperties;
1818}
1819
1820
1838function dol_clone($srcobject, $native = 2)
1839{
1840 if ($native == 0) {
1841 // deprecated method, use the method with native = 2 instead
1842 dol_syslog("Warning, call to dol_clone() with the deprecated parameter native=0, use 2 instead", LOG_WARNING);
1843
1844 $tmpsavdb = null;
1845 if (isset($srcobject->db) && isset($srcobject->db->db) && is_object($srcobject->db->db) && get_class($srcobject->db->db) == 'PgSql\Connection') {
1846 $tmpsavdb = $srcobject->db;
1847 unset($srcobject->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1848 }
1849
1850 $myclone = unserialize(serialize($srcobject)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1851
1852 if (!empty($tmpsavdb)) {
1853 $srcobject->db = $tmpsavdb;
1854 }
1855 } elseif ($native == 2) {
1856 // recommended method to have a full secured isolated cloned object
1857 $myclone = new stdClass();
1858 $tmparray = get_object_vars($srcobject); // return only public properties
1859
1860 if (is_array($tmparray)) {
1861 foreach ($tmparray as $propertykey => $propertyval) {
1862 if (is_scalar($propertyval) || is_array($propertyval)) {
1863 $myclone->$propertykey = $propertyval;
1864 }
1865 }
1866 }
1867 } else {
1868 $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)
1869 }
1870
1871 return $myclone;
1872}
1873
1874
1883function dol_clone_in_array($srcobject, $startlevel = 0)
1884{
1885 if (is_object($srcobject)) {
1886 $srcobject = get_object_vars($srcobject); // exclude private/protected properties
1887 }
1888
1889 if (is_array($srcobject)) {
1890 $result = [];
1891 foreach ($srcobject as $key => $value) {
1892 if (in_array($key, array('db', 'fields', 'error', 'errorhidden', 'errors', 'oldcopy', 'linkedObjects', 'linked_objects'))) {
1893 continue;
1894 }
1895 $result[$key] = dol_clone_in_array($value, $startlevel + 1);
1896 }
1897 return $result;
1898 }
1899
1900 return $srcobject;
1901}
1902
1903
1913function dol_size($size, $type = '')
1914{
1915 global $conf;
1916 if (empty($conf->dol_optimize_smallscreen)) {
1917 return $size;
1918 }
1919 if ($type == 'width' && $size > 250) {
1920 return 250;
1921 } else {
1922 return 10;
1923 }
1924}
1925
1926
1940function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0, $allowdash = 0)
1941{
1942 $str = (string) $str;
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',
2096 '%C3%81' => 'A',
2097 '%C3%82' => 'A',
2098 '%C3%83' => 'A',
2099 '%C3%84' => 'A',
2100 '%C3%85' => 'A',
2101 '%C3%87' => 'C',
2102 '%C3%88' => 'E',
2103 '%C3%89' => 'E',
2104 '%C3%8A' => 'E',
2105 '%C3%8B' => 'E',
2106 '%C3%8C' => 'I',
2107 '%C3%8D' => 'I',
2108 '%C3%8E' => 'I',
2109 '%C3%8F' => 'I',
2110 '%C3%91' => 'N',
2111 '%C3%92' => 'O',
2112 '%C3%93' => 'O',
2113 '%C3%94' => 'O',
2114 '%C3%95' => 'O',
2115 '%C3%96' => 'O',
2116 '%C5%A0' => 'S',
2117 '%C3%99' => 'U',
2118 '%C3%9A' => 'U',
2119 '%C3%9B' => 'U',
2120 '%C3%9C' => 'U',
2121 '%C3%9D' => 'Y',
2122 '%C5%B8' => 'y',
2123 '%C3%A0' => 'a',
2124 '%C3%A1' => 'a',
2125 '%C3%A2' => 'a',
2126 '%C3%A3' => 'a',
2127 '%C3%A4' => 'a',
2128 '%C3%A5' => 'a',
2129 '%C3%A7' => 'c',
2130 '%C3%A8' => 'e',
2131 '%C3%A9' => 'e',
2132 '%C3%AA' => 'e',
2133 '%C3%AB' => 'e',
2134 '%C3%AC' => 'i',
2135 '%C3%AD' => 'i',
2136 '%C3%AE' => 'i',
2137 '%C3%AF' => 'i',
2138 '%C3%B1' => 'n',
2139 '%C3%B2' => 'o',
2140 '%C3%B3' => 'o',
2141 '%C3%B4' => 'o',
2142 '%C3%B5' => 'o',
2143 '%C3%B6' => 'o',
2144 '%C5%A1' => 's',
2145 '%C3%B9' => 'u',
2146 '%C3%BA' => 'u',
2147 '%C3%BB' => 'u',
2148 '%C3%BC' => 'u',
2149 '%C3%BD' => 'y',
2150 '%C3%BF' => 'y',
2151 '%CC%80' => '',
2152 '%CC%81' => '',
2153 '%CC%82' => '',
2154 '%CC%83' => '',
2155 '%CC%84' => '',
2156 '%CC%85' => '',
2157 '%CC%86' => '',
2158 '%CC%87' => '',
2159 '%CC%88' => '',
2160 '%CC%89' => '',
2161 '%CC%8A' => '',
2162 '%CC%8B' => '',
2163 '%CC%8C' => '',
2164 '%CC%8D' => '',
2165 '%CC%8E' => '',
2166 '%CC%8F' => '',
2167 '%CC%90' => '',
2168 '%CC%91' => '',
2169 '%CC%A7' => '',
2170 );
2171 $string = strtr($string, $replacements);
2172 return rawurldecode($string);
2173 } else {
2174 // See http://www.ascii-code.com/
2175 $string = strtr(
2176 $str,
2177 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
2178 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
2179 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
2180 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
2181 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
2182 \xF9\xFA\xFB\xFC\xFD\xFF",
2183 "AAAAAAC
2184 EEEEIIIIDN
2185 OOOOOUUUY
2186 aaaaaaceeee
2187 iiiidnooooo
2188 uuuuyy"
2189 );
2190 $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"));
2191 return $string;
2192 }
2193}
2194
2208function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
2209{
2210 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
2211 if (empty($keepspaces)) {
2212 $forbidden_chars_to_replace[] = " ";
2213 }
2214 $forbidden_chars_to_remove = array();
2215 //$forbidden_chars_to_remove=array("(",")");
2216
2217 if (is_array($badcharstoreplace)) {
2218 $forbidden_chars_to_replace = $badcharstoreplace;
2219 }
2220 if (is_array($badcharstoremove)) {
2221 $forbidden_chars_to_remove = $badcharstoremove;
2222 }
2223
2224 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2225 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
2226}
2227
2228
2242function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2243{
2244 if ($removetabcrlf) {
2245 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2246 } else {
2247 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
2248 }
2249}
2250
2257function dolSlugify($stringtoslugify)
2258{
2259 $slug = dol_string_unaccent($stringtoslugify);
2260
2261 // Convert special characters to their ASCII equivalents
2262 if (function_exists('iconv')) {
2263 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2264 }
2265
2266 // Convert to lowercase
2267 $slug = strtolower($slug);
2268
2269 // Replace non-alphanumeric characters with hyphens
2270 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2271
2272 // Remove leading and trailing hyphens
2273 $slug = trim($slug, '-');
2274
2275 return $slug;
2276}
2277
2286function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2287{
2288 if (is_null($stringtoescape)) {
2289 return '';
2290 }
2291
2292 // escape quotes and backslashes, newlines, etc.
2293 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2294 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2295 if (empty($noescapebackslashn)) {
2296 $substitjs["\n"] = '\\n';
2297 $substitjs['\\'] = '\\\\';
2298 }
2299 if (empty($mode)) {
2300 $substitjs["'"] = "\\'";
2301 $substitjs['"'] = "\\'";
2302 } elseif ($mode == 1) {
2303 $substitjs["'"] = "\\'";
2304 } elseif ($mode == 2) {
2305 $substitjs['"'] = '\\"';
2306 } elseif ($mode == 3) {
2307 $substitjs["'"] = "\\'";
2308 $substitjs['"'] = "\\\"";
2309 }
2310 return strtr((string) $stringtoescape, $substitjs);
2311}
2312
2322function dol_escape_uri($stringtoescape)
2323{
2324 return rawurlencode($stringtoescape);
2325}
2326
2333function dol_escape_json($stringtoescape)
2334{
2335 return str_replace('"', '\"', $stringtoescape);
2336}
2337
2345function dol_escape_php($stringtoescape, $stringforquotes = 2)
2346{
2347 if (is_null($stringtoescape)) {
2348 return '';
2349 }
2350
2351 if ($stringforquotes == 2) {
2352 return str_replace('"', "'", $stringtoescape);
2353 } elseif ($stringforquotes == 1) {
2354 // We remove the \ char.
2355 // If we allow the \ char, we can have $stringtoescape =
2356 // abc\';phpcodedanger; so the escapement will become
2357 // abc\\';phpcodedanger; and injecting this into
2358 // $a='...' will give $ac='abc\\';phpcodedanger;
2359 $stringtoescape = str_replace('\\', '', $stringtoescape);
2360 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2361 }
2362
2363 return 'Bad parameter for stringforquotes in dol_escape_php';
2364}
2365
2372function dol_escape_all($stringtoescape)
2373{
2374 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2375}
2376
2383function dol_escape_xml($stringtoescape)
2384{
2385 return $stringtoescape;
2386}
2387
2397function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2398{
2399 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2400}
2401
2410function dolPrintText($s)
2411{
2412 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2413}
2414
2426function dolPrintHTML($s, $allowiframe = 0, $moreallowedtags = array())
2427{
2428 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2429 //$isAlreadyHTML = dol_textishtml($s);
2430
2431 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2432 // encode only special char like accented chars but not &, <, >, ", ' if already HTML.
2433 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2434
2435 $allowedtags = 'common';
2436 if (!empty($moreallowedtags)) {
2437 $allowedtags .= ','.implode(',', $moreallowedtags);
2438 }
2439 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe, $allowedtags)), 1, 1, $allowedtags, 0, 1);
2440}
2441
2452function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2453{
2454 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2455 if (!empty($allowothertags) && is_array($allowothertags)) {
2456 $allowedtags = array_merge($allowedtags, $allowothertags);
2457 }
2458 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2459 // The dol_escape_htmltag will escape html tags.
2460 if ($escapeonlyhtmltags) {
2461 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2462 } else {
2463 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2464 }
2465}
2466
2475function dolPrintHTMLForAttributeUrl($s)
2476{
2477 // 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)
2478 // The dol_escape_htmltag will escape html chars.
2479 $escapeonlyhtmltags = 1;
2480 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2481}
2482
2492function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2493{
2494 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2495}
2496
2503function dolPrintPassword($s)
2504{
2505 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2506}
2507
2508
2525function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2526{
2527 $reg = array();
2528 if (preg_match('/^common([a-z,]*)/', $noescapetags, $reg)) {
2529 $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';
2530 // Add also html5 tags
2531 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2532 if (!empty($reg[1])) {
2533 $noescapetags .= $reg[1];
2534 }
2535 }
2536 if ($cleanalsojavascript) {
2537 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2538 }
2539
2540 // escape quotes and backslashes, newlines, etc.
2541 if ($escapeonlyhtmltags) {
2542 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2543 } else {
2544 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2545 // Because content can be or not already HTML.
2546 // For example, this decode &egrave; into its UTF-8 char so string is UTF8 (but numbers entities like &#39; is not decoded).
2547 // In a future, we should not need this
2548
2549 $tmp = (string) $stringtoescape;
2550
2551 // We protect the 6 special entities that we don't want to decode.
2552 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2553 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2554 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2555 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2556 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2557 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2558
2559 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2560
2561 // We restore the 6 special entities that we don't want to have been decoded by previous command
2562 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2563 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2564 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2565 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2566 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2567 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2568
2569 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2570 }
2571 if (!$keepb) {
2572 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2573 }
2574 if (!$keepn) {
2575 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2576 } elseif ($keepn == -1) {
2577 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2578 }
2579
2580 if ($escapeonlyhtmltags) {
2581 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2582 return $tmp;
2583 } else {
2584 // Now we protect all the tags we want to keep
2585 $tmparrayoftags = array();
2586 if ($noescapetags) {
2587 $tmparrayoftags = explode(',', $noescapetags);
2588 }
2589
2590 if (count($tmparrayoftags)) {
2591 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2592
2593 $reg = array();
2594 // Remove reserved keywords. They are forbidden in a source string
2595 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2596
2597 foreach ($tmparrayoftags as $tagtoreplace) {
2598 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2599 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . '>/', '__BEGINTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2600 $tmp = str_ireplace('</' . $tagtoreplace . '>', '__ENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2601 $tmp = preg_replace('/<' . preg_quote($tagtoreplace, '/') . ' \/>/', '__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', $tmp);
2602
2603 // For case of tag with attributes.
2604 // All the occurrences are protected in a single pass: the replacement string contains no '<', so it
2605 // can never build a new tag to protect (a loop replacing one distinct attribute string per round was
2606 // rescanning the whole content for each of them, so the cost was quadratic on large contents).
2607 $tmp = preg_replace_callback(
2608 '/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/',
2613 static function ($reg) use ($tagtoreplace) {
2614 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2615 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2616 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2617 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2618 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2619 return '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__';
2620 },
2621 $tmp
2622 ) ?? $tmp;
2623 }
2624
2625 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2626 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2627 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2628 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2629 }
2630
2631 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2632 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2633
2634 //print $result;
2635
2636 if (count($tmparrayoftags)) {
2637 // Restore protected tags
2638 foreach ($tmparrayoftags as $tagtoreplace) {
2639 $result = str_ireplace('__BEGINTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . '>', $result);
2640 $result = preg_replace('/__BEGINTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1>', $result);
2641 $result = str_ireplace('__ENDTAGTOREPLACE' . $tagtoreplace . '__', '</' . $tagtoreplace . '>', $result);
2642 $result = str_ireplace('__BEGINENDTAGTOREPLACE' . $tagtoreplace . '__', '<' . $tagtoreplace . ' />', $result);
2643 $result = preg_replace('/__BEGINENDTAGTOREPLACE' . $tagtoreplace . '\[([^\]]*)\]__/', '<' . $tagtoreplace . ' \1 />', $result);
2644 }
2645
2646 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2647
2648 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2649 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2650 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2651 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2652 }
2653
2654 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2655
2656 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2657
2658 return $result;
2659 }
2660}
2661
2669function dol_strtolower($string, $encoding = "UTF-8")
2670{
2671 if (function_exists('mb_strtolower')) {
2672 return mb_strtolower($string, $encoding);
2673 } else {
2674 return strtolower($string);
2675 }
2676}
2677
2686function dol_strtoupper($string, $encoding = "UTF-8")
2687{
2688 if (function_exists('mb_strtoupper')) {
2689 return mb_strtoupper($string, $encoding);
2690 } else {
2691 return strtoupper($string);
2692 }
2693}
2694
2703function dol_ucfirst($string, $encoding = "UTF-8")
2704{
2705 if (function_exists('mb_substr')) {
2706 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding) . mb_substr($string, 1, null, $encoding);
2707 } else {
2708 return ucfirst($string);
2709 }
2710}
2711
2720function dol_ucwords($string, $encoding = "UTF-8")
2721{
2722 if (function_exists('mb_convert_case')) {
2723 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2724 } else {
2725 return ucwords($string);
2726 }
2727}
2728
2729
2735function getCallerInfoString()
2736{
2737 $backtrace = debug_backtrace();
2738 $msg = "";
2739 if (count($backtrace) >= 1) {
2740 $pos = 1;
2741 if (count($backtrace) == 1) {
2742 $pos = 0;
2743 }
2744 $trace = $backtrace[$pos];
2745 if (isset($trace['file'], $trace['line'])) {
2746 $msg = " From {$trace['file']}:{$trace['line']}.";
2747 }
2748 }
2749 return $msg;
2750}
2751
2774function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2775{
2776 global $conf, $user, $debugbar;
2777
2778 // If syslog module enabled
2779 if (!isModEnabled('syslog')) {
2780 return;
2781 }
2782
2783 // Check if we are into execution of code of a website
2784 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2785 global $website, $websitekey;
2786 if (is_object($website) && !empty($website->ref)) {
2787 $suffixinfilename .= '_website_' . $website->ref;
2788 } elseif (!empty($websitekey)) {
2789 $suffixinfilename .= '_website_' . $websitekey;
2790 }
2791 }
2792
2793 // Check if we have a forced suffix
2794 if (defined('USESUFFIXINLOG')) {
2795 $suffixinfilename .= constant('USESUFFIXINLOG');
2796 }
2797
2798 if ($ident < 0) {
2799 foreach ($conf->loghandlers as $loghandlerinstance) {
2800 $loghandlerinstance->setIdent($ident);
2801 }
2802 }
2803
2804 if (!empty($message)) {
2805 // Test log level
2806 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2807 $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');
2808
2809 if (!array_key_exists($level, $logLevels)) {
2810 dol_syslog('Error Bad Log Level ' . $level, LOG_ERR);
2811 $level = LOG_ERR;
2812 }
2813 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2814 return;
2815 }
2816
2817 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2818 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2819 }
2820
2821 // If adding log inside HTML page is required
2822 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2823 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))
2824 ) {
2825 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2826 $osuser = " " . sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2827
2828 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S") . " " . sprintf("%-7s", $logLevels[$level]) . " " . $ospid . " " . $osuser . " " . $message;
2829 }
2830
2831 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2832 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2833 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2834 print "\n\n<!-- Log start\n";
2835 print dol_escape_htmltag($message) . "\n";
2836 print "Log end -->\n";
2837 }
2838
2839 $data = array(
2840 'message' => $message,
2841 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2842 'level' => $level,
2843 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2844 'ip' => '',
2845 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2846 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2847 );
2848
2849 // For log, we want the reliable IP first.
2850 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2851 if (!empty($remoteip)) {
2852 $data['ip'] = $remoteip;
2853 // This is when server run behind a reverse proxy
2854 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2855 // $data['ip'] is last
2856 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2857 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2858 $data['ip'] = '';
2859 $foundremoteip = 0;
2860 $j = 0;
2861 foreach ($tmpips as $tmpip) {
2862 $tmpip = trim($tmpip);
2863 if (strtolower($tmpip) == strtolower($remoteip)) {
2864 $foundremoteip = 1;
2865 }
2866 if (empty($data['ip'])) {
2867 $data['ip'] = $tmpip;
2868 } else {
2869 $j++;
2870 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2871 }
2872 }
2873 if (!$foundremoteip) {
2874 $j++;
2875 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2876 }
2877 $data['ip'] .= (($j > 0) ? ']' : '');
2878 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2879 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2880 $data['ip'] = '';
2881 $foundremoteip = 0;
2882 $j = 0;
2883 foreach ($tmpips as $tmpip) {
2884 $tmpip = trim($tmpip);
2885 if (strtolower($tmpip) == strtolower($remoteip)) {
2886 $foundremoteip = 1;
2887 }
2888 if (empty($data['ip'])) {
2889 $data['ip'] = $tmpip;
2890 } else {
2891 $j++;
2892 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $tmpip;
2893 }
2894 }
2895 if (!$foundremoteip) {
2896 $j++;
2897 $data['ip'] .= (($j == 1) ? ' [via ' : ',') . $remoteip;
2898 }
2899 $data['ip'] .= (($j > 0) ? ']' : '');
2900 }
2901 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2902 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2903 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2904 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2905 // 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).
2906 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2907 } else {
2908 $data['ip'] = '???';
2909 }
2910
2911 if (!empty($_SERVER['USERNAME'])) {
2912 // 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).
2913 $data['osuser'] = (string) $_SERVER['USERNAME'];
2914 } elseif (!empty($_SERVER['LOGNAME'])) {
2915 // 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).
2916 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2917 }
2918
2919 // Loop on each log handler and send output
2920 foreach ($conf->loghandlers as $loghandlerinstance) {
2921 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2922 continue;
2923 }
2924 $loghandlerinstance->export($data, $suffixinfilename);
2925 }
2926 unset($data);
2927 }
2928
2929 if ($ident > 0) {
2930 foreach ($conf->loghandlers as $loghandlerinstance) {
2931 $loghandlerinstance->setIdent($ident);
2932 }
2933 }
2934}
2935
2947function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2948{
2949 global $langs, $db;
2950
2951 $form = new Form($db);
2952
2953 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2954 if (empty($templatenameforexport)) {
2955 $templatenameforexport = 'website_' . $website->ref;
2956 }
2957
2958 $out = '';
2959 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="' . dol_escape_htmltag($buttonstring) . '"/>';
2960
2961 // for generate popup
2962 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2963 $out .= 'jQuery(document).ready(function () {';
2964 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2965 $out .= ' var dialogHtml = \'';
2966
2967 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2968 $dialogcontent .= ' <div style="margin-top: 20px;">';
2969 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>' . $langs->trans("ExportSiteLabel") . '...</label><br>';
2970 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2971 $dialogcontent .= ' </div>';
2972 $dialogcontent .= ' <br>';
2973 $dialogcontent .= ' <div style="margin-top: 20px;">';
2974 $dialogcontent .= ' <strong>' . $langs->trans("ExportSiteGitLabel") . ' ' . $form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '') . '</strong><br>';
2975 $dialogcontent .= ' <form action="' . dol_escape_htmltag($overwriteGitUrl) . '" method="POST">';
2976 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2977 $dialogcontent .= ' <input type="hidden" name="token" value="' . newToken() . '">';
2978 $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>';
2979 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2980 $dialogcontent .= ' </form>';
2981 $dialogcontent .= ' </div>';
2982 $dialogcontent .= ' </div>';
2983
2984 $out .= dol_escape_js($dialogcontent);
2985
2986 $out .= '\';';
2987
2988
2989 // Add the content of the dialog to the body of the page
2990 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2991 $out .= ' if ($dialog.length > 0) {
2992 $dialog.remove();
2993 }
2994 jQuery("body").append(dialogHtml);';
2995
2996 // Configuration of popup
2997 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2998 $out .= ' autoOpen: false,';
2999 $out .= ' modal: true,';
3000 $out .= ' height: 290,';
3001 $out .= ' width: "40%",';
3002 $out .= ' title: "' . dol_escape_js($label) . '",';
3003 $out .= ' });';
3004
3005 // Simulate a click on the original "submit" input to export the site.
3006 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
3007 $out .= ' console.log("Clic on exportsite.");';
3008 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
3009 $out .= ' console.log("element founded:", target.length > 0);';
3010 $out .= ' if (target.length > 0) { target.click(); }';
3011 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
3012 $out .= ' });';
3013
3014 // open popup
3015 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
3016 $out .= ' return false;';
3017 $out .= ' });';
3018 $out .= '});';
3019 $out .= '</script>';
3020
3021 return $out;
3022}
3023
3024
3041function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
3042{
3043 global $conf;
3044
3045 if (strpos($url, '?') > 0) {
3046 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
3047 } else {
3048 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=' . urlencode($name);
3049 }
3050
3051 if (preg_match('/^https/i', $url)) {
3052 $urltoopen = $url;
3053 } else {
3054 $urltoopen = DOL_URL_ROOT . $url;
3055 }
3056
3057 $out = '';
3058
3059 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
3060 $out .= '<!-- a link for button to open url into a dialog popup -->';
3061 $out .= '<a ' . ($accesskey ? ' accesskey="' . $accesskey . '"' : '') . ' class="cursorpointer reposition button_' . $name . ($morecss ? ' ' . $morecss : '') . '"' . $disabled . ' title="' . dol_escape_htmltag($label) . '"';
3062 if (empty($conf->use_javascript_ajax)) {
3063 $out .= ' href="' . $urltoopen . '" target="_blank"';
3064 } elseif ($jsonopen) {
3065 $out .= ' href="#" onclick="' . $jsonopen . '"';
3066 } else {
3067 $out .= ' href="#"';
3068 }
3069 $out .= '>' . $buttonstring . '</a>';
3070
3071 if (!empty($conf->use_javascript_ajax)) {
3072 // Add code to open url using the popup.
3073 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
3074 $out .= '<div id="idfordialog' . $name . '" class="hidden">' . (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '') . '</div>';
3075
3076 $out .= '<!-- Add js code to open dialog popup on dialog -->';
3077 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">
3078 jQuery(document).ready(function () {
3079 jQuery(".button_' . $name . '").click(function () {
3080 console.log(\'Open popup with jQuery(...).dialog() on URL ' . dol_escape_js($urltoopen) . '\');
3081 var $tmpdialog = $(\'#idfordialog' . $name . '\');
3082 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog' . $name . '" style="border: 0px;" src="' . $urltoopen . '" width="100%" height="98%"></iframe>\');
3083 $tmpdialog.dialog({
3084 autoOpen: false,
3085 modal: true,
3086 height: (window.innerHeight - 150),
3087 width: \'80%\',
3088 title: \'' . dol_escape_js($label) . '\',
3089 open: function (event, ui) {
3090 console.log("open popup name=' . $name . '");
3091 },
3092 close: function (event, ui) {
3093 console.log("Popup is closed, run jsonclose = ' . $jsonclose . '");
3094 ' . (empty($jsonclose) || preg_match('/^TODO/', $jsonclose) ? '' : $jsonclose . ';') . '
3095 }
3096 });
3097
3098 $tmpdialog.dialog(\'open\');
3099 return false;
3100 });
3101 });
3102 </script>';
3103 }
3104 return $out;
3105}
3106
3123function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
3124{
3125 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
3126}
3127
3145function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
3146{
3147 global $conf, $langs, $hookmanager;
3148
3149 // Show title
3150 $showtitle = 1;
3151 if (!empty($conf->dol_optimize_smallscreen)) {
3152 $showtitle = 0;
3153 }
3154
3155 $out = "\n" . '<!-- dol_fiche_head - dol_get_fiche_head -->';
3156
3157 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3158 $out .= '<div class="tabs' . ($picto ? '' : ' nopaddingleft') . '" data-role="controlgroup" data-type="horizontal">' . "\n";
3159 }
3160
3161 // Show right part
3162 if ($morehtmlright) {
3163 $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.
3164 }
3165
3166 // Show tabs
3167
3168 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
3169 $maxkey = -1;
3170 if (is_array($links) && !empty($links)) {
3171 $keys = array_keys($links);
3172 if (count($keys)) {
3173 $maxkey = max($keys);
3174 }
3175 }
3176
3177 // Show tabs
3178 // if =0 we don't use the feature
3179 if (empty($limittoshow)) {
3180 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
3181 }
3182 if (!empty($conf->dol_optimize_smallscreen)) { // If on smartphone, we limit to 1 tab to show
3183 $limittoshow = 1;
3184 }
3185
3186 $displaytab = 0;
3187 $nbintab = 0;
3188 $popuptab = 0;
3189 $outmore = '';
3190 for ($i = 0; $i <= $maxkey; $i++) {
3191 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3192 // If active tab is already present
3193 if ($i >= $limittoshow) {
3194 $limittoshow--;
3195 }
3196 }
3197 }
3198
3199 for ($i = 0; $i <= $maxkey; $i++) {
3200 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
3201 $isactive = true;
3202 } else {
3203 $isactive = false;
3204 }
3205
3206 if ($i < $limittoshow || $isactive) {
3207 // Output entry with a visible tab
3208 $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])) . ' -->';
3209
3210 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3211 if (!empty($links[$i][0])) {
3212 $out .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3213 } else {
3214 $out .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3215 }
3216 } elseif (!empty($links[$i][1])) {
3217 //print "x $i $active ".$links[$i][2]." z";
3218 $out .= '<div class="tab tab' . ($isactive ? 'active' : 'unactive') . '" style="margin: 0 !important">';
3219
3220 if (!empty($links[$i][0])) {
3221 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
3222 $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) . '">';
3223 }
3224
3225 if ($displaytab == 0 && $picto) {
3226 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
3227 }
3228
3229 $out .= $links[$i][1];
3230 if (!empty($links[$i][0])) {
3231 $out .= '</a>' . "\n";
3232 }
3233 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
3234 $out .= '</div>';
3235 }
3236
3237 $out .= '</div>';
3238 } else {
3239 // Add entry into the combo popup with the other tabs
3240 if (!$popuptab) {
3241 $popuptab = 1;
3242 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
3243 }
3244 $outmore_content = '';
3245
3246 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3247 if (!empty($links[$i][0])) {
3248 $outmore_content .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
3249 } else {
3250 $outmore_content .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
3251 }
3252 } elseif (!empty($links[$i][1])) {
3253 $outmore_content .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="wordwrap inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
3254 $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.
3255 $outmore_content .= '</a>' . "\n";
3256 }
3257 if ($outmore_content !== '') {
3258 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
3259 }
3260
3261 $nbintab++;
3262 }
3263
3264 $displaytab = $i + 1;
3265 }
3266 if ($popuptab) {
3267 $outmore .= '</div>';
3268 }
3269
3270 if ($popuptab) { // If there is some tabs not shown
3271 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3272 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3273 $widthofpopup = 240;
3274
3275 $tabsname = $moretabssuffix;
3276 if (empty($tabsname)) {
3277 $tabsname = str_replace("@", "", $picto);
3278 }
3279 $out .= '<div id="moretabs' . $tabsname . '" class="inline-block tabsElem valignmiddle">';
3280 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3281 $out .= '<div class="tab valignmiddle"><a href="#" class="tab moretab inline-block tabunactive valignmiddle"><span class="fa fa-angle-down"></span> <span class="opacitymedium">+' . $nbintab . '</span></a></div>'; // Do not use "reposition" class in the "More".
3282 }
3283 $out .= '<div id="moretabsList' . $tabsname . '" style="width: ' . $widthofpopup . 'px; position: absolute; ' . $left . ': -999em; text-align: ' . $left . '; margin:0px; padding:2px; z-index:10;">';
3284 $out .= $outmore;
3285 $out .= '</div>';
3286 $out .= '<div></div>';
3287 $out .= "</div>\n";
3288
3289 $out .= '<script nonce="' . getNonce() . '">';
3290 $out .= "$('#moretabs" . $tabsname . "').mouseenter( function() {
3291 var x = this.offsetLeft, y = this.offsetTop;
3292 console.log('mouseenter " . $left . " x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3293 if ((window.innerWidth - x) < " . ($widthofpopup + 10) . ") {
3294 $('#moretabsList" . $tabsname . "').css('" . $right . "','8px');
3295 }
3296 $('#moretabsList" . $tabsname . "').css('" . $left . "','auto');
3297 });
3298 ";
3299 $out .= "$('#moretabs" . $tabsname . "').mouseleave( function() { console.log('mouseleave " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','-999em');});";
3300 $out .= "</script>";
3301 }
3302
3303 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3304 $out .= "</div>\n";
3305 }
3306
3307 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3308 $out .= "\n" . '<div id="dragDropAreaTabBar" class="tabBar' . ($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '') . ($notab == -4 ? '' : ' tabBarWithBottom'))));
3309 $out .= ($morecssdiv ? ' ' . $morecssdiv : '');
3310 $out .= '">' . "\n";
3311 }
3312 if (!empty($dragdropfile)) {
3313 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3314 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3315 }
3316 $parameters = array('tabname' => $active, 'out' => $out);
3317 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3318 if ($reshook > 0) {
3319 $out = $hookmanager->resPrint;
3320 }
3321
3322 return $out;
3323}
3324
3332function dol_fiche_end($notab = 0)
3333{
3334 print dol_get_fiche_end($notab);
3335}
3336
3343function dol_get_fiche_end($notab = 0)
3344{
3345 if (!$notab || $notab == -1) {
3346 return "\n</div>\n";
3347 } else {
3348 return '';
3349 }
3350}
3351
3371function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3372{
3373 global $conf, $form, $user, $langs, $hookmanager, $action;
3374
3375 $error = 0;
3376
3377 $maxvisiblephotos = 1;
3378 $showimage = 1;
3379 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3380 // @phan-suppress-next-line PhanUndeclaredMethod
3381 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3382 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3383 $showbarcode = 0;
3384 }
3385 $modulepart = 'unknown';
3386
3387 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3388 $modulepart = $object->element;
3389 } elseif ($object->element == 'member') {
3390 $modulepart = 'memberphoto';
3391 } elseif ($object->element == 'user') {
3392 $modulepart = 'userphoto';
3393 }
3394
3395 if (class_exists("Imagick")) {
3396 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3397 $modulepart = $object->element;
3398 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3399 $modulepart = 'ficheinter';
3400 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3401 $modulepart = 'contract';
3402 } elseif ($object->element == 'order_supplier') {
3403 $modulepart = 'supplier_order';
3404 } elseif ($object->element == 'invoice_supplier') {
3405 $modulepart = 'supplier_invoice';
3406 }
3407 }
3408
3409 if ($object->element == 'product') {
3411 '@phan-var-force Product $object';
3412 $width = 80;
3413 $cssclass = 'photowithmargin photoref';
3414 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3415 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3416 if ($conf->browser->layout == 'phone') {
3417 $maxvisiblephotos = 1;
3418 }
3419 $useLinkPathPhoto = getDolGlobalInt('PRODUCT_USE_LINK_PATH_FOR_PHOTO');
3420 if ($showimage || $useLinkPathPhoto) {
3421 $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>';
3422 } else {
3423 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3424 $nophoto = '';
3425 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3426 } else { // Show no photo link
3427 $nophoto = '/public/theme/common/nophoto.png';
3428 $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>';
3429 }
3430 }
3431 } elseif ($object->element == 'category') {
3433 '@phan-var-force Categorie $object';
3434 $width = 80;
3435 $cssclass = 'photowithmargin photoref';
3436 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3437 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3438 if ($conf->browser->layout == 'phone') {
3439 $maxvisiblephotos = 1;
3440 }
3441 if ($showimage) {
3442 $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>';
3443 } else {
3444 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3445 $nophoto = '';
3446 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3447 } else { // Show no photo link
3448 $nophoto = '/public/theme/common/nophoto.png';
3449 $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>';
3450 }
3451 }
3452 } elseif ($object->element == 'bom') {
3454 '@phan-var-force Bom $object';
3455 $width = 80;
3456 $cssclass = 'photowithmargin photoref';
3457 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3458 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3459 if ($conf->browser->layout == 'phone') {
3460 $maxvisiblephotos = 1;
3461 }
3462 if ($showimage) {
3463 $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>';
3464 } else {
3465 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3466 $nophoto = '';
3467 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3468 } else { // Show no photo link
3469 $nophoto = '/public/theme/common/nophoto.png';
3470 $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>';
3471 }
3472 }
3473 } elseif ($object->element == 'ticket') {
3474 $width = 80;
3475 $cssclass = 'photoref';
3477 '@phan-var-force Ticket $object';
3478 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity] . '/' . $object->ref);
3479 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3480 if ($conf->browser->layout == 'phone') {
3481 $maxvisiblephotos = 1;
3482 }
3483
3484 if ($showimage) {
3485 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3486 if ($object->nbphoto > 0) {
3487 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $showphoto . '</div>';
3488 } else {
3489 $showimage = 0;
3490 }
3491 }
3492 if (!$showimage) {
3493 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3494 $nophoto = '';
3495 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3496 } else { // Show no photo link
3497 $nophoto = img_picto('No photo', 'object_ticket');
3498 $morehtmlleft .= '<!-- No photo to show -->';
3499 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3500 $morehtmlleft .= $nophoto;
3501 $morehtmlleft .= '</div></div>';
3502 }
3503 }
3504 } else {
3505 // $modulepart may have been set previously if Imagick class exists (see before).
3506 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3507 $phototoshow = '';
3508 // Check if a preview file is available
3509 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3510 $objectref = dol_sanitizeFileName($object->ref);
3511 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity]) . "/";
3512 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3513 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3514 $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
3515 } else {
3516 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3517 }
3518 if (empty($subdir)) {
3519 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3520 }
3521
3522 $filepath = $dir_output . $subdir . "/";
3523
3524 $filepdf = $filepath . $objectref . ".pdf";
3525 $relativepath = $subdir . '/' . $objectref . '.pdf';
3526
3527 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3528 $fileimage = $filepdf . '_preview.png';
3529 $relativepathimage = $relativepath . '_preview.png';
3530
3531 $pdfexists = file_exists($filepdf);
3532
3533 // If PDF file exists
3534 if ($pdfexists) {
3535 // Conversion du PDF en image png si fichier png non existent
3536 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3537 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3538 include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
3539 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3540 if ($ret < 0) {
3541 $error++;
3542 }
3543 }
3544 }
3545 }
3546
3547 if ($pdfexists && !$error) {
3548 $heightforphotref = 80;
3549 if (!empty($conf->dol_optimize_smallscreen)) {
3550 $heightforphotref = 60;
3551 }
3552 // If the preview file is found
3553 if (file_exists($fileimage)) {
3554 $phototoshow = '<div class="photoref">';
3555 $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithborder" src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($relativepathimage) . '">';
3556 $phototoshow .= '</div>';
3557 }
3558 }
3559 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3560 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3561 }
3562
3563 if ($phototoshow) {
3564 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3565 $morehtmlleft .= $phototoshow;
3566 $morehtmlleft .= '</div>';
3567 }
3568 }
3569
3570 if (empty($phototoshow)) { // Show No photo link (picto of object)
3571 if ($object->element == 'action') {
3572 $width = 80;
3573 $cssclass = 'photorefcenter';
3574 $nophoto = img_picto('No photo', 'title_agenda');
3575 } else {
3576 $width = 14;
3577 $cssclass = 'photorefcenter';
3578 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3579 $prefix = 'object_';
3580 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3581 $picto = 'project'; // instead of projectpub
3582 }
3583 if (strpos($picto, 'fontawesome_') !== false) {
3584 $prefix = '';
3585 }
3586 $nophoto = img_picto('No photo', $prefix . $picto);
3587 }
3588 $morehtmlleft .= '<!-- No photo to show -->';
3589 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3590 $morehtmlleft .= $nophoto;
3591 $morehtmlleft .= '</div></div>';
3592 }
3593 }
3594
3595 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)) {
3596 $morehtmlref .= '<div style="clear: both;"></div>';
3597 $morehtmlref .= '<div class="smallimp refidno opacitymedium banner-object-technical-id">';
3598 $morehtmlref .= $langs->trans("TechnicalID") . ': ' . ((int) $object->id);
3599 $morehtmlref .= '</div>';
3600 }
3601
3602
3603 // Show barcode
3604 if ($showbarcode) {
3605 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $form->showbarcode($object, 100, 'photoref valignmiddle') . '</div>';
3606 }
3607
3608 if ($object->element == 'societe') {
3610 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3611 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3612 } else {
3613 $morehtmlstatus .= $object->getLibStatut(6);
3614 }
3615 } elseif ($object->element == 'product') {
3617 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3618 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3619 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3620 } else {
3621 $morehtmlstatus .= '<span class="statusrefsell">' . $object->getLibStatut(6, 0) . '</span>';
3622 }
3623 $morehtmlstatus .= ' &nbsp; ';
3624 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3625 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3626 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'status_buy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3627 } else {
3628 $morehtmlstatus .= '<span class="statusrefbuy">' . $object->getLibStatut(6, 1) . '</span>';
3629 }
3630 } elseif (in_array($object->element, array('salary'))) {
3632 '@phan-var-force Salary $object';
3633 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3634 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3635 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3636 }
3637 $morehtmlstatus .= $tmptxt;
3638 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) {
3640 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3641 if (!isset($object->alreadypaid)) {
3642 $object->totalpaid = $object->getSommePaiement(0);
3643 $object->totalcreditnotes = $object->getSumCreditNotesUsed(0);
3644 $object->totaldeposits = $object->getSumDepositsUsed(0);
3645 $object->alreadypaid = $object->totalpaid + $object->totalcreditnotes + $object->totaldeposits;
3646 }
3647 $tmptxt = $object->getLibStatut(6, (float) $object->alreadypaid);
3648 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3649 $tmptxt = $object->getLibStatut(5, (float) $object->alreadypaid);
3650 }
3651 $morehtmlstatus .= $tmptxt;
3652 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid like for invoices
3654 '@phan-var-force ChargeSociales|Loan|Tva $object';
3655 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3656 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3657 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3658 }
3659 $morehtmlstatus .= $tmptxt;
3660 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3662 if ($object->status == 0) {
3663 $morehtmlstatus .= $object->getLibStatut(5);
3664 } else {
3665 $morehtmlstatus .= $object->getLibStatut(4);
3666 }
3667 } elseif ($object->element == 'facturerec') {
3669 '@phan-var-force FactureRec $object';
3670 if ($object->frequency == 0) {
3671 $morehtmlstatus .= $object->getLibStatut(2);
3672 } else {
3673 $morehtmlstatus .= $object->getLibStatut(5);
3674 }
3675 } elseif ($object->element == 'project_task') {
3677 $tmptxt = $object->getLibStatut(4);
3678 $morehtmlstatus .= $tmptxt;
3679 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3680 $tmptxt = $object->getLibStatut(6);
3681 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3682 $tmptxt = $object->getLibStatut(5);
3683 }
3684 $morehtmlstatus .= $tmptxt;
3685 }
3686
3687 // Say if object was dispatched/transferred "into accountancy"
3688 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3689 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3690 if (method_exists($object, 'getVentilExportCompta')) {
3691 $accounted = $object->getVentilExportCompta(1);
3692 $langs->load("accountancy");
3693 $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>';
3694 }
3695 }
3696
3697 // Add alias for thirdparty
3698 if (!empty($object->name_alias)) {
3700 '@phan-var-force Societe $object';
3701 $morehtmlref .= '<div class="refidno opacitymedium banner-object-name-alias">' . dol_escape_htmltag($object->name_alias) . '</div>';
3702 }
3703
3704 // Add label
3705 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3707 if (!empty($object->label)) {
3708 $morehtmlref .= '<div class="refidno banner-object-label">' . $object->label . '</div>';
3709 }
3710 }
3711 // Show address and email
3712 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3713 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3714 if ($moreaddress) {
3715 $morehtmlref .= '<div class="refidno refaddress">';
3716 $morehtmlref .= $moreaddress;
3717 $morehtmlref .= '</div>';
3718 }
3719 }
3720
3721 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3722 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3723 if ($reshook < 0) {
3724 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3725 } elseif (empty($reshook)) {
3726 $morehtmlref .= $hookmanager->resPrint;
3727 } elseif ($reshook > 0) {
3728 $morehtmlref = $hookmanager->resPrint;
3729 }
3730
3731 // $morehtml is the right part (link "Back to list")
3732 // $morehtmlref is the part after the ref
3733 // $morehtmlleft is the picto or photo of banner
3734 // $morehtmlstatus is part under the status
3735 // $morehtmlright is part of htmlright
3736
3737 print '<div class="' . ($onlybanner ? 'arearefnobottom ' : 'arearef ') . 'heightref valignmiddle centpercent object-banner-tab-container" data-module-part="'.dolPrintHTMLForAttribute($modulepart).'">';
3738 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3739 print '</div>';
3740 print '<div class="underrefbanner clearboth"></div>';
3741}
3742
3752function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3753{
3754 global $langs;
3755 $ret = '';
3756 if ($fieldrequired) {
3757 $ret .= '<span class="fieldrequired">';
3758 }
3759 $ret .= '<label for="' . $fieldkey . '">';
3760 $ret .= $langs->trans($langkey);
3761 $ret .= '</label>';
3762 if ($fieldrequired) {
3763 $ret .= '</span>';
3764 }
3765 return $ret;
3766}
3767
3781function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3782{
3783 global $langs, $hookmanager;
3784
3785 $ret = '';
3786 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3787
3788 // See format of addresses on https://en.wikipedia.org/wiki/Address
3789 // Address
3790 if (empty($mode)) {
3791 $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)));
3792 }
3793 // Zip/Town/State
3794 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3795 // US: title firstname name \n address lines \n town, state, zip \n country
3796 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3797 $ret .= (($ret && $town) ? $sep : '') . $town;
3798
3799 if (!empty($object->state)) {
3800 $ret .= ($ret ? ($town ? ", " : $sep) : '') . $object->state;
3801 }
3802 if (!empty($object->zip)) {
3803 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '') . $object->zip;
3804 }
3805 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3806 // UK: title firstname name \n address lines \n town state \n zip \n country
3807 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3808 $ret .= ($ret ? $sep : '') . $town;
3809 if (!empty($object->state)) {
3810 $ret .= ($ret ? ", " : '') . $object->state;
3811 }
3812 if (!empty($object->zip)) {
3813 $ret .= ($ret ? $sep : '') . $object->zip;
3814 }
3815 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3816 // ES: title firstname name \n address lines \n zip town \n state \n country
3817 $ret .= ($ret ? $sep : '') . $object->zip;
3818 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3819 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3820 if (!empty($object->state)) {
3821 $ret .= $sep . $object->state;
3822 }
3823 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3824 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3825 // See https://www.sljfaq.org/afaq/addresses.html
3826 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3827 $ret .= ($ret ? $sep : '') . ($object->state ? $object->state . ', ' : '') . $town . ($object->zip ? ' ' : '') . $object->zip;
3828 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3829 // IT: title firstname name\n address lines \n zip town state_code \n country
3830 $ret .= ($ret ? $sep : '') . $object->zip;
3831 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3832 $ret .= ($town ? (($object->zip ? ' ' : '') . $town) : '');
3833 $ret .= (empty($object->state_code) ? '' : (' ' . $object->state_code));
3834 } else {
3835 // Other: title firstname name \n address lines \n zip town[, state] \n country
3836 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3837 $ret .= !empty($object->zip) ? (($ret ? $sep : '') . $object->zip) : '';
3838 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')) . $town) : '');
3839 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3840 $ret .= ($ret ? ", " : '') . $object->state;
3841 }
3842 }
3843
3844 if (!is_object($outputlangs)) {
3845 $outputlangs = $langs;
3846 }
3847 if ($withcountry) {
3848 $langs->load("dict");
3849 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '') . $outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country" . $object->country_code)));
3850 }
3851 if ($hookmanager) {
3852 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs, 'mode' => $mode, 'extralangcode' => $extralangcode);
3853 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3854 if ($reshook > 0) {
3855 $ret = '';
3856 }
3857 $ret .= $hookmanager->resPrint;
3858 }
3859
3860 return $ret;
3861}
3862
3863
3864
3874function dol_strftime($fmt, $ts = false, $is_gmt = false)
3875{
3876 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3877 return dol_print_date($ts, $fmt, $is_gmt);
3878 } else {
3879 return 'Error date outside supported range';
3880 }
3881}
3882
3905function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false, $decorate = 0)
3906{
3907 global $conf, $langs;
3908
3909 // If date undefined or "", we return ""
3910 if (dol_strlen((string) $time) == 0) {
3911 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3912 }
3913
3914 if ($tzoutput === 'auto') {
3915 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3916 }
3917
3918 // Clean parameters
3919 $to_gmt = false; // false if we want date in server timezone, true if we want to add offset
3920 $offsettz = $offsetdst = 0;
3921 if ($tzoutput) {
3922 $to_gmt = true; // For backward compatibility
3923 if (is_string($tzoutput)) {
3924 if ($tzoutput == 'tzserver') {
3925 $to_gmt = false;
3926 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3927 // @phan-suppress-next-line PhanPluginRedundantAssignment
3928 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3929 // @phan-suppress-next-line PhanPluginRedundantAssignment
3930 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3931 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3932 $to_gmt = true;
3933 // if no session (by example in cron) may use MAIN_DOLIBARR_USER_TIMEZONE instead UTC
3934 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? getDolGlobalString('MAIN_DOLIBARR_USER_TIMEZONE', 'UTC') : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3935
3936 if (class_exists('DateTimeZone')) {
3937 try {
3938 $user_date_tz = new DateTimeZone($offsettzstring);
3939 } catch (Exception $e) {
3940 // Bad value for $offsettzstring
3941 dol_syslog("DateInvalidTimeZoneException for timezone string '".$offsettzstring."'. Falling back to UTC.", LOG_ERR);
3942 $user_date_tz = new DateTimeZone('UTC'); // Force valid timezone as UTC
3943 }
3944 $user_dt = new DateTime();
3945 $user_dt->setTimezone($user_date_tz);
3946 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3947 $offsettz = $user_dt->getOffset(); // should include dst ?
3948 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3949 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3950 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3951 }
3952 }
3953 }
3954 }
3955 if (!is_object($outputlangs)) {
3956 $outputlangs = $langs;
3957 }
3958 if (!$format) {
3959 $format = 'daytextshort';
3960 }
3961
3962 // Do we have to reduce the length of date (year on 2 chars) to save space.
3963 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3964 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3965 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3966 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3967 if ($formatwithoutreduce != $format) {
3968 $format = $formatwithoutreduce;
3969 $reduceformat = 1;
3970 } // so format 'dayreduceformat' is processed like day
3971
3972 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3973 // TODO Add format daysmallyear and dayhoursmallyear
3974 if ($format == 'day') {
3975 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3976 } elseif ($format == 'hour') {
3977 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3978 } elseif ($format == 'hoursec') {
3979 $s1 = $outputlangs->trans("FormatDateShort");
3980 $s2 = $outputlangs->trans("FormatDateHourSecShort");
3981 $s3 = trim(preg_replace('/'.preg_quote($s1, '/').'/', '', $s2)); // Try to guess the format for FormatHourSecShort using FormatDateShort and FormatDateHourSecShort
3982 $format = $s3;
3983 //$format = ($outputlangs->trans("FormatHourSecShort") != "FormatHourSecShort" ? $outputlangs->trans("FormatHourSecShort") : ($s3 ? $s3 : $conf->format_hour_sec_short));
3984 } elseif ($format == 'hourduration') {
3985 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3986 } elseif ($format == 'daytext') {
3987 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3988 } elseif ($format == 'daytextshort') {
3989 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3990 } elseif ($format == 'dayhour') {
3991 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3992 } elseif ($format == 'dayhoursec') {
3993 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3994 } elseif ($format == 'dayhourtext') {
3995 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3996 } elseif ($format == 'dayhourtextshort') {
3997 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3998 } elseif ($format == 'dayhourlog') {
3999 // Format not sensitive to language
4000 $format = '%Y%m%d%H%M%S';
4001 } elseif ($format == 'dayhourlogsmall') {
4002 // Format not sensitive to language
4003 $format = '%y%m%d%H%M';
4004 } elseif ($format == 'dayhourldap') {
4005 $format = '%Y%m%d%H%M%SZ';
4006 } elseif ($format == 'dayhourxcard') {
4007 $format = '%Y%m%dT%H%M%SZ';
4008 } elseif ($format == 'dayxcard') {
4009 $format = '%Y%m%d';
4010 } elseif ($format == 'dayrfc') {
4011 $format = '%Y-%m-%d'; // DATE_RFC3339
4012 } elseif ($format == 'dayhourrfc') {
4013 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
4014 } elseif ($format == 'standard') {
4015 $format = '%Y-%m-%d %H:%M:%S';
4016 }
4017
4018 if ($reduceformat) {
4019 $format = str_replace('%Y', '%y', $format);
4020 $format = str_replace('yyyy', 'yy', $format);
4021 }
4022
4023 // Clean format
4024 if (preg_match('/%b/i', $format)) { // There is some text to translate
4025 // We inhibit translation to text made by strftime functions. We will use trans instead later.
4026 $format = str_replace('%b', '__b__', $format);
4027 $format = str_replace('%B', '__B__', $format);
4028 }
4029 if (preg_match('/%a/i', $format)) { // There is some text to translate
4030 // We inhibit translation to text made by strftime functions. We will use trans instead later.
4031 $format = str_replace('%a', '__a__', $format);
4032 $format = str_replace('%A', '__A__', $format);
4033 }
4034
4035 // Analyze date
4036 $reg = array();
4037 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
4038 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString());
4039 return '';
4040 } 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
4041 // This part of code should not be used anymore.
4042 dol_syslog("Functions.lib::dol_print_date function called with a bad value" . getCallerInfoString(), LOG_WARNING);
4043 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
4044 $syear = (!empty($reg[1]) ? $reg[1] : '');
4045 $smonth = (!empty($reg[2]) ? $reg[2] : '');
4046 $sday = (!empty($reg[3]) ? $reg[3] : '');
4047 $shour = (!empty($reg[4]) ? $reg[4] : '');
4048 $smin = (!empty($reg[5]) ? $reg[5] : '');
4049 $ssec = (!empty($reg[6]) ? $reg[6] : '');
4050
4051 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
4052
4053 if ($to_gmt) {
4054 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4055 } else {
4056 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4057 }
4058 $dtts = new DateTime();
4059 $dtts->setTimestamp($time);
4060 $dtts->setTimezone($tzo);
4061 $newformat = str_replace(
4062 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4063 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4064 $format
4065 );
4066 $ret = $dtts->format($newformat);
4067 $ret = str_replace(
4068 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4069 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4070 $ret
4071 );
4072 } else {
4073 // Date is a timestamps
4074 if ($time < 100000000000) { // Protection against bad date values
4075 $dtts = new DateTime();
4076 //var_dump($tzoutput.' '.$offsettzstring.' '.$offsettz.$offsetdst.' x '.$to_gmt);
4077 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
4078 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4079
4080 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4081 $dtts->setTimezone($tzo); // important: must be before the setTimestamp
4082 $dtts->setTimestamp($timetouse);
4083 } else {
4084 $timetouse = (int) $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4085
4086 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4087 $dtts->setTimestamp($timetouse); // TODO May be we can invert setTimestamp and setTimezone
4088 $dtts->setTimezone($tzo);
4089 }
4090
4091 $newformat = str_replace(
4092 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4093 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4094 $format
4095 );
4096
4097 $ret = $dtts->format($newformat);
4098 //var_dump($timetouse, $offsettz, $offsetdst, $tzo, $newformat, $ret);
4099 $ret = str_replace(
4100 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
4101 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
4102 $ret
4103 );
4104 } else {
4105 $ret = 'Bad value ' . $time . ' for date';
4106 }
4107 }
4108
4109 if (preg_match('/__b__/i', $format)) {
4110 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
4111
4112 if ($to_gmt) {
4113 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
4114 } else {
4115 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
4116 }
4117 $dtts = new DateTime();
4118 $dtts->setTimestamp($timetouse);
4119 $dtts->setTimezone($tzo);
4120 $month = (int) $dtts->format("m");
4121 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
4122 if ($encodetooutput) {
4123 $monthtext = $outputlangs->transnoentities('Month' . $month);
4124 $monthtextshort = $outputlangs->transnoentities('MonthShort' . $month);
4125 } else {
4126 $monthtext = $outputlangs->transnoentitiesnoconv('Month' . $month);
4127 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort' . $month);
4128 }
4129 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
4130 $ret = str_replace('__b__', $monthtextshort, $ret);
4131 $ret = str_replace('__B__', $monthtext, $ret);
4132 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
4133 //return $ret;
4134 }
4135 if (preg_match('/__a__/i', $format)) {
4136 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
4137 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
4138
4139 if ($to_gmt) {
4140 $tzo = new DateTimeZone('UTC');
4141 } else {
4142 $tzo = new DateTimeZone(date_default_timezone_get());
4143 }
4144 $dtts = new DateTime();
4145 $dtts->setTimestamp($timetouse);
4146 $dtts->setTimezone($tzo);
4147 $w = $dtts->format("w");
4148 $dayweek = $outputlangs->transnoentitiesnoconv('Day' . $w);
4149
4150 $ret = str_replace('__A__', $dayweek, $ret);
4151 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
4152 }
4153
4154 if ($decorate) {
4155 $ret = preg_replace('/(\d\d:\d\d [AP]M)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4156 $ret = preg_replace('/(\d\d:\d\d)$/', '<span class="'.($decorate === 1 ? 'opacitymedium' : $decorate).'">\1</span>', $ret);
4157 }
4158
4159 return $ret;
4160}
4161
4162
4183function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
4184{
4185 if ($timestamp === '') {
4186 return array();
4187 }
4188
4189 $datetimeobj = new DateTime();
4190 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
4191 if ($forcetimezone) {
4192 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
4193 }
4194 $arrayinfo = array(
4195 'year' => ((int) date_format($datetimeobj, 'Y')),
4196 'mon' => ((int) date_format($datetimeobj, 'm')),
4197 'mday' => ((int) date_format($datetimeobj, 'd')),
4198 'wday' => ((int) date_format($datetimeobj, 'w')),
4199 'yday' => ((int) date_format($datetimeobj, 'z')),
4200 'hours' => ((int) date_format($datetimeobj, 'H')),
4201 'minutes' => ((int) date_format($datetimeobj, 'i')),
4202 'seconds' => ((int) date_format($datetimeobj, 's')),
4203 '0' => $timestamp
4204 );
4205
4206 return $arrayinfo;
4207}
4208
4230function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
4231{
4232 global $conf;
4233 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
4234
4235 if ($gm === 'auto') {
4236 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
4237 }
4238 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
4239
4240 // Clean parameters
4241 if ($hour == -1 || empty($hour)) {
4242 $hour = 0;
4243 }
4244 if ($minute == -1 || empty($minute)) {
4245 $minute = 0;
4246 }
4247 if ($second == -1 || empty($second)) {
4248 $second = 0;
4249 }
4250
4251 // Check parameters
4252 if ($check) {
4253 if (!$month || !$day) {
4254 return '';
4255 }
4256 if ($day > 31) {
4257 return '';
4258 }
4259 if ($month > 12) {
4260 return '';
4261 }
4262 if ($hour < 0 || $hour > 24) {
4263 return '';
4264 }
4265 if ($minute < 0 || $minute > 60) {
4266 return '';
4267 }
4268 if ($second < 0 || $second > 60) {
4269 return '';
4270 }
4271 }
4272
4273 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
4274 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
4275 $localtz = new DateTimeZone($default_timezone);
4276 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
4277 // We use dol_tz_string first because it is more reliable.
4278 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
4279 try {
4280 $localtz = new DateTimeZone($default_timezone);
4281 } catch (Exception $e) {
4282 dol_syslog("Warning dol_tz_string contains an invalid value " . json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
4283 $default_timezone = @date_default_timezone_get();
4284 }
4285 } elseif (strrpos($gm, "tz,") !== false) {
4286 $timezone = (string) str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
4287 try {
4288 $localtz = new DateTimeZone($timezone);
4289 } catch (Exception $e) {
4290 dol_syslog("Warning passed timezone contains an invalid value " . $timezone, LOG_WARNING);
4291 }
4292 }
4293
4294 if (empty($localtz)) {
4295 $localtz = new DateTimeZone('UTC');
4296 }
4297 $dt = new DateTime('now', $localtz);
4298 $dt->setDate((int) $year, (int) $month, (int) $day);
4299 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4300 $date = $dt->getTimestamp(); // should include daylight saving time
4301
4302 return $date;
4303}
4304
4305
4316function dol_now($mode = 'gmt')
4317{
4318 $ret = 0;
4319
4320 if ($mode === 'auto') {
4321 $mode = 'gmt';
4322 }
4323
4324 if ($mode == 'gmt') {
4325 $ret = time(); // Time for now at greenwich.
4326 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4327 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
4328 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4329 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4330 // } elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4331 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4332 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4333 // $ret=dol_now('gmt')+($tzsecond*3600);
4334 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4335 // Time for now with user timezone added
4336 // print 'time: '.time();
4337 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4338 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4339 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4340 }
4341
4342 return $ret;
4343}
4344
4345
4354function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4355{
4356 global $conf, $langs;
4357 $level = 1024;
4358
4359 if (!empty($conf->dol_optimize_smallscreen)) {
4360 $shortunit = 1;
4361 }
4362
4363 // Set value text
4364 if (empty($shortvalue) || $size < ($level * 10)) {
4365 $ret = $size;
4366 $textunitshort = $langs->trans("b");
4367 $textunitlong = $langs->trans("Bytes");
4368 } else {
4369 $ret = round($size / $level, 0);
4370 $textunitshort = $langs->trans("Kb");
4371 $textunitlong = $langs->trans("KiloBytes");
4372 }
4373 // Use long or short text unit
4374 if (empty($shortunit)) {
4375 $ret .= ' ' . $textunitlong;
4376 } else {
4377 $ret .= ' ' . $textunitshort;
4378 }
4379
4380 return $ret;
4381}
4382
4393function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4394{
4395 global $langs;
4396
4397 if (empty($url)) {
4398 return '';
4399 }
4400
4401 $linkstart = '<a href="';
4402 if (!preg_match('/^http/i', $url)) {
4403 $linkstart .= 'http://';
4404 }
4405 $linkstart .= $url;
4406 $linkstart .= '"';
4407 if ($target) {
4408 $linkstart .= ' target="' . $target . '"';
4409 }
4410 $linkstart .= ' title="' . $langs->trans("URL") . ': ' . $url . '"';
4411 $linkstart .= '>';
4412
4413 $link = '';
4414 if (!preg_match('/^http/i', $url)) {
4415 $link .= 'http://';
4416 }
4417 $link .= dol_trunc($url, $max);
4418
4419 $linkend = '</a>';
4420
4421 if ($morecss == 'float') { // deprecated
4422 return '<div class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '') . $link . '</div>';
4423 } else {
4424 return $linkstart . '<span class="nospan' . ($morecss ? ' ' . $morecss : '') . '" style="margin-right: 10px">' . ($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '') . $link . '</span>' . $linkend;
4425 }
4426}
4427
4441function dol_print_email($email, $contactid = 0, $socid = 0, $addlink = 0, $max = 0, $showinvalid = 2, $withpicto = 0, $morecss = 'paddingrightonly')
4442{
4443 global $user, $langs, $hookmanager;
4444
4445 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4446 //$showinvalid = 1; $email = 'rrrrr';
4447
4448 $newemail = dol_escape_htmltag($email);
4449
4450 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4451 $withpicto = 0;
4452 }
4453
4454 if (empty($email)) {
4455 return '&nbsp;';
4456 }
4457
4458 if ($addlink == 1) {
4459 $newemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="';
4460 if (!preg_match('/^mailto:/i', $email)) {
4461 $newemail .= 'mailto:';
4462 }
4463 $newemail .= $email;
4464 $newemail .= '" target="_blank">';
4465
4466 $newemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4467
4468 if ($max > 0) {
4469 $newemail .= dol_trunc($email, $max);
4470 } else {
4471 $newemail .= $email;
4472 }
4473 $newemail .= '</a>';
4474
4475 if ($showinvalid) {
4476 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4477 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4478 $emailonly = CMailFile::getValidAddress($email, 2);
4479 if (!isValidEmail($emailonly)) {
4480 $langs->load("errors");
4481 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $emailonly), '', 'paddingrightonly');
4482 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4483 $langs->load("errors");
4484 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly), '', 'paddingrightonly');
4485 }
4486 }
4487
4488 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4489 $type = 'AC_EMAIL';
4490 $linktoaddaction = '';
4491 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4492 $linktoaddaction = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&backtopage=1&actioncode=' . urlencode($type) . '&contactid=' . ((int) $contactid) . '&socid=' . ((int) $socid) . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
4493 }
4494 if ($linktoaddaction) {
4495 $newemail = '<div>' . $newemail . ' ' . $linktoaddaction . '</div>';
4496 }
4497 }
4498 } elseif ($addlink === 'thirdparty') {
4499 $tmpnewemail = '<a class="' . ($morecss ? $morecss : '') . '" style="text-overflow: ellipsis;" href="' . DOL_URL_ROOT . '/societe/card.php?socid=' . $socid . '&action=presend&mode=init#formmailbeforetitle">';
4500 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4501 if ($withpicto == 1) {
4502 $tmpnewemail .= $newemail;
4503 }
4504 $tmpnewemail .= '</a>';
4505
4506 $newemail = $tmpnewemail;
4507 } else {
4508 $newemail = ($withpicto ? img_picto($langs->trans("EMail") . ' : ' . $email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '') . $newemail;
4509
4510 if ($showinvalid) {
4511 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
4512 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4513 $emailonly = CMailFile::getValidAddress($email, 2);
4514 if (!isValidEmail($emailonly)) {
4515 $langs->load("errors");
4516 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadEMail", $email));
4517 } elseif ($showinvalid == 2 && !isValidMailDomain($emailonly)) {
4518 $langs->load("errors");
4519 $newemail .= img_warning($langs->transnoentitiesnoconv("ErrorBadMXDomain", $emailonly));
4520 }
4521 }
4522 }
4523
4524 //$rep = '<div class="nospan" style="margin-right: 10px">';
4525 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4526 //$rep .= '</div>';
4527 $rep = $newemail;
4528 if (getDolGlobalString('MAIN_MAIL_COPY_ON_CLICK')) {
4529 $rep .= showValueWithClipboardCPButton($newemail, 0, 'none');
4530 }
4531
4532 if ($hookmanager) {
4533 $parameters = array('cid' => $contactid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4534
4535 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4536 if ($reshook > 0) {
4537 $rep = '';
4538 }
4539 $rep .= $hookmanager->resPrint;
4540 }
4541
4542 return $rep;
4543}
4544
4545
4561function dolOutputDates($datep, $datef = null, $fullday = 0, $addseconds = 0, $pictotoadd = '', $tzoutput = 'tzuserrel', $reduceformat = 0)
4562{
4563 $tmpa = dol_getdate($datep);
4564 if (empty($datef)) {
4565 $tmpb = $tmpa;
4566 } else {
4567 $tmpb = dol_getdate($datef);
4568 }
4569
4570 $s = '';
4571
4572 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
4573 // The same day
4574 $s .= '<div class="center inline-block">';
4575 if ($tmpa['hours'] != $tmpb['hours'] || $tmpa['minutes'] != $tmpb['minutes']) {
4576 // Not the same hour
4577 $s .= dol_print_date($datep, 'day'.($reduceformat ? 'reduceformat' : ''), $tzoutput);
4578 $s .= $pictotoadd;
4579 if (empty($fullday)) {
4580 $s .= '<br><span class="small opacitymedium">';
4581 $s .= dol_print_date($datep, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
4582 $s .= '-'.dol_print_date($datef, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
4583 $s .= '</span>';
4584 }
4585 } else {
4586 // The same hour
4587 $s .= dol_print_date($datep, 'day'.($reduceformat ? 'reduceformat' : ''), 'tzuserrel');
4588 $s .= $pictotoadd;
4589 if (empty($fullday)) {
4590 $s .= '<br><span class="small opacitymedium">';
4591 $s .= dol_print_date($datep, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
4592 $s .= '</span>';
4593 }
4594 }
4595 $s .= '</div>';
4596 } else {
4597 // Not the same day
4598 $s .= '<div class="center inline-block dateborderright">';
4599 $s .= dol_print_date($datep, 'day'.($reduceformat ? 'reduceformat' : ''), $tzoutput);
4600 if (empty($fullday)) {
4601 $s .= '<br><span class="small opacitymedium">';
4602 $s .= dol_print_date($datep, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
4603 $s .= '</span>';
4604 }
4605 $s .= '</div>';
4606 $s .= '<div class="center inline-block dateborderleft">';
4607 $s .= dol_print_date($datef, 'day'.($reduceformat ? 'reduceformat' : ''), 'tzuserrel');
4608 $s .= $pictotoadd;
4609 if (empty($fullday)) {
4610 $s .= '<br><span class="small opacitymedium">';
4611 $s .= dol_print_date($datef, 'hour'.($addseconds ? 'sec' : '').'reduceformat', $tzoutput);
4612 $s .= '</span>';
4613 }
4614 $s .= '</div>';
4615 }
4616
4617 return $s;
4618}
4619
4620
4626function getArrayOfSocialNetworks()
4627{
4628 global $db;
4629
4630 $socialnetworks = array();
4631 // Enable caching of array
4632 require_once DOL_DOCUMENT_ROOT . '/core/lib/memory.lib.php';
4633 $cachekey = dol_sanitizeKeyCode(str_replace(',', '_', 'socialnetworks_'.getEntity('c_socialnetworks')));
4634 $dataretrieved = dol_getcache($cachekey);
4635
4636 if (!is_null($dataretrieved)) {
4637 $socialnetworks = $dataretrieved;
4638 } else {
4639 $sql = "SELECT rowid, code, label, url, icon, active FROM " . MAIN_DB_PREFIX . "c_socialnetworks";
4640 $sql .= " WHERE entity IN (" . getEntity('c_socialnetworks').")";
4641
4642 $resql = $db->query($sql);
4643 if ($resql) {
4644 while ($obj = $db->fetch_object($resql)) {
4645 $socialnetworks[$obj->code] = array(
4646 'rowid' => $obj->rowid,
4647 'label' => $obj->label,
4648 'url' => $obj->url,
4649 'icon' => $obj->icon,
4650 'active' => $obj->active,
4651 );
4652 }
4653 }
4654 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4655 }
4656
4657 return (is_array($socialnetworks) ? $socialnetworks : array());
4658}
4659
4670function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4671{
4672 global $hookmanager, $langs, $user;
4673
4674 $htmllink = $value;
4675
4676 if (empty($value)) {
4677 return '&nbsp;';
4678 }
4679
4680 if (!empty($type)) {
4681 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4682 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4683 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4684 if ($type == 'skype') {
4685 $htmllink .= dol_escape_htmltag($value);
4686 $htmllink .= '&nbsp; <a href="skype:';
4687 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4688 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4689 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4690 $htmllink .= '</a><a href="skype:';
4691 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4692 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4693 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4694 $htmllink .= '</a>';
4695 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4696 $addlink = 'AC_SKYPE';
4697 $link = '';
4698 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4699 $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>';
4700 }
4701 $htmllink .= ($link ? ' ' . $link : '');
4702 }
4703 } else {
4704 if (!empty($dictsocialnetworks[$type]['url'])) {
4705 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4706 if ($tmpvirginurl) {
4707 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4708 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4709
4710 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4711 if ($tmpvirginurl3) {
4712 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4713 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4714 }
4715
4716 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4717 if ($tmpvirginurl2) {
4718 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4719 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4720 }
4721 }
4722 if (preg_match('/^https?:\/\//i', $value)) {
4723 $link = $value;
4724 } else {
4725 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4726 }
4727 $valuetoshow = $value;
4728 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4729 if (preg_match('/^https?:\/\//i', $link)) {
4730 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4731 } else {
4732 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4733 }
4734 } else {
4735 $htmllink .= dol_escape_htmltag($value);
4736 }
4737 }
4738 $htmllink .= '</div>';
4739 } else {
4740 $langs->load("errors");
4741 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4742 }
4743
4744 if ($hookmanager) {
4745 $parameters = array(
4746 'value' => $value,
4747 'cid' => $contactid,
4748 'socid' => $socid,
4749 'type' => $type,
4750 'dictsocialnetworks' => $dictsocialnetworks,
4751 );
4752
4753 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4754 if ($reshook > 0) {
4755 $htmllink = '';
4756 }
4757 $htmllink .= $hookmanager->resPrint;
4758 }
4759
4760 return $htmllink;
4761}
4762
4772function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4773{
4774 global $mysoc;
4775
4776 if (empty($profID) || empty($profIDtype)) {
4777 return '';
4778 }
4779 if (empty($countrycode)) {
4780 $countrycode = $mysoc->country_code;
4781 }
4782 $newProfID = $profID;
4783 $id = substr($profIDtype, -1);
4784 $ret = '';
4785 if (strtoupper($countrycode) == 'FR') {
4786 // France
4787 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4788
4789 if ($id == 1 && dol_strlen($newProfID) == 9) {
4790 // SIREN (ex: 123 123 123)
4791 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3);
4792 }
4793 if ($id == 2 && dol_strlen($newProfID) == 14) {
4794 // SIRET (ex: 123 123 123 12345)
4795 $newProfID = substr($newProfID, 0, 3) . ' ' . substr($newProfID, 3, 3) . ' ' . substr($newProfID, 6, 3) . ' ' . substr($newProfID, 9, 5);
4796 }
4797 if ($id == 3 && dol_strlen($newProfID) == 5) {
4798 // NAF/APE (ex: 69.20Z)
4799 $newProfID = substr($newProfID, 0, 2) . '.' . substr($newProfID, 2, 3);
4800 }
4801 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4802 // TVA intracommunautaire (ex: FR12 123 123 123)
4803 $newProfID = substr($newProfID, 0, 4) . ' ' . substr($newProfID, 4, 3) . ' ' . substr($newProfID, 7, 3) . ' ' . substr($newProfID, 10, 3);
4804 }
4805 }
4806 if (!empty($addcpButton)) {
4807 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4808 } else {
4809 $ret = $newProfID;
4810 }
4811 return $ret;
4812}
4813
4829function dol_print_phone($phone, $countrycode = '', $contactid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4830{
4831 global $conf, $user, $langs, $mysoc, $hookmanager;
4832
4833 // Clean phone parameter
4834 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4835 if (empty($phone)) {
4836 return '';
4837 }
4838 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4839 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4840 }
4841 if (empty($countrycode) && is_object($mysoc)) {
4842 $countrycode = $mysoc->country_code;
4843 }
4844
4845 // Short format for small screens
4846 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4847 $separ = '';
4848 }
4849
4850 $newphone = $phone;
4851 $newphonewa = $phone;
4852 if (strtoupper($countrycode) == "FR") {
4853 // France
4854 if (dol_strlen($phone) == 10) {
4855 $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);
4856 } elseif (dol_strlen($phone) == 7) {
4857 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2);
4858 } elseif (dol_strlen($phone) == 9) {
4859 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2);
4860 } elseif (dol_strlen($phone) == 11) {
4861 $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);
4862 } elseif (dol_strlen($phone) == 12) {
4863 $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);
4864 } elseif (dol_strlen($phone) == 13) {
4865 $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);
4866 }
4867 } elseif (strtoupper($countrycode) == "CA") {
4868 if (dol_strlen($phone) == 10) {
4869 $newphone = ($separ != '' ? '(' : '') . substr($newphone, 0, 3) . ($separ != '' ? ')' : '') . $separ . substr($newphone, 3, 3) . ($separ != '' ? '-' : '') . substr($newphone, 6, 4);
4870 }
4871 } elseif (strtoupper($countrycode) == "PT") { //Portugal
4872 if (dol_strlen($phone) == 13) { //ex: +351_ABC_DEF_GHI
4873 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4874 }
4875 } elseif (strtoupper($countrycode) == "SR") { //Suriname
4876 if (dol_strlen($phone) == 10) { //ex: +597_ABC_DEF
4877 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3);
4878 } elseif (dol_strlen($phone) == 11) { //ex: +597_ABC_DEFG
4879 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
4880 }
4881 } elseif (strtoupper($countrycode) == "DE") { //Deutschland
4882 if (dol_strlen($phone) == 14) { //ex: +49_ABCD_EFGH_IJK
4883 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 4) . $separ . substr($newphone, 11, 3);
4884 } elseif (dol_strlen($phone) == 13) { //ex: +49_ABC_DEFG_HIJ
4885 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 4) . $separ . substr($newphone, 10, 3);
4886 }
4887 } elseif (strtoupper($countrycode) == "ES") { //Spain
4888 if (dol_strlen($phone) == 12) { //ex: +34_ABC_DEF_GHI
4889 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4890 }
4891 } elseif (strtoupper($countrycode) == "BF") { // Burkina Faso
4892 if (dol_strlen($phone) == 12) { //ex : +22 A BC_DE_FG_HI
4893 $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);
4894 }
4895 } elseif (strtoupper($countrycode) == "RO") { // Roumanie
4896 if (dol_strlen($phone) == 12) { //ex : +40 AB_CDE_FG_HI
4897 $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);
4898 }
4899 } elseif (strtoupper($countrycode) == "TR") { //Turquie
4900 if (dol_strlen($phone) == 13) { //ex : +90 ABC_DEF_GHIJ
4901 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4902 }
4903 } elseif (strtoupper($countrycode) == "US") { //Etat-Unis
4904 if (dol_strlen($phone) == 12) { //ex: +1 ABC_DEF_GHIJ
4905 $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4906 }
4907 } elseif (strtoupper($countrycode) == "MX") { //Mexique
4908 if (dol_strlen($phone) == 12) { //ex: +52 ABCD_EFG_HI
4909 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4910 } elseif (dol_strlen($phone) == 11) { //ex: +52 AB_CD_EF_GH
4911 $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);
4912 } elseif (dol_strlen($phone) == 13) { //ex: +52 ABC_DEF_GHIJ
4913 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
4914 }
4915 } elseif (strtoupper($countrycode) == "ML") { //Mali
4916 if (dol_strlen($phone) == 12) { //ex: +223 AB_CD_EF_GH
4917 $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);
4918 }
4919 } elseif (strtoupper($countrycode) == "TH") { //Thailand
4920 if (dol_strlen($phone) == 11) { //ex: +66_ABC_DE_FGH
4921 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4922 } elseif (dol_strlen($phone) == 12) { //ex: +66_A_BCD_EF_GHI
4923 $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);
4924 }
4925 } elseif (strtoupper($countrycode) == "MU") {
4926 //Maurice
4927 if (dol_strlen($phone) == 11) { //ex: +230_ABC_DE_FG
4928 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4929 } elseif (dol_strlen($phone) == 12) { //ex: +230_ABCD_EF_GH
4930 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
4931 }
4932 } elseif (strtoupper($countrycode) == "ZA") { //Afrique du sud
4933 if (dol_strlen($phone) == 12) { //ex: +27_AB_CDE_FG_HI
4934 $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);
4935 }
4936 } elseif (strtoupper($countrycode) == "SY") { //Syrie
4937 if (dol_strlen($phone) == 12) { //ex: +963_AB_CD_EF_GH
4938 $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);
4939 } elseif (dol_strlen($phone) == 13) { //ex: +963_AB_CD_EF_GHI
4940 $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);
4941 }
4942 } elseif (strtoupper($countrycode) == "AE") { //Emirats Arabes Unis
4943 if (dol_strlen($phone) == 12) { //ex: +971_ABC_DEF_GH
4944 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
4945 } elseif (dol_strlen($phone) == 13) { //ex: +971_ABC_DEF_GHI
4946 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4947 } elseif (dol_strlen($phone) == 14) { //ex: +971_ABC_DEF_GHIK
4948 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 4);
4949 }
4950 } elseif (strtoupper($countrycode) == "DZ") { //Algeria
4951 if (dol_strlen($phone) == 13) { //ex: +213_ABC_DEF_GHI
4952 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4953 }
4954 } elseif (strtoupper($countrycode) == "BE") { //Belgique
4955 if (dol_strlen($phone) == 11) { //ex: +32_ABC_DE_FGH
4956 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
4957 } elseif (dol_strlen($phone) == 12) { //ex: +32_ABC_DEF_GHI
4958 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4959 }
4960 } elseif (strtoupper($countrycode) == "PF") { //French Polynesia
4961 if (dol_strlen($phone) == 12) { //ex: +689_AB_CD_EF_GH
4962 $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);
4963 }
4964 } elseif (strtoupper($countrycode) == "CO") { //Colombie
4965 if (dol_strlen($phone) == 13) { //ex: +57_ABC_DEF_GH_IJ
4966 $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);
4967 }
4968 } elseif (strtoupper($countrycode) == "JO") { //Jordanie
4969 if (dol_strlen($phone) == 12) { //ex: +962_A_BCD_EF_GH
4970 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 1) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
4971 }
4972 } elseif (strtoupper($countrycode) == "JM") { //Jamaica
4973 if (dol_strlen($newphone) == 12) { //ex: +1867_ABC_DEFG
4974 $newphone = substr($newphone, 0, 5) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
4975 }
4976 } elseif (strtoupper($countrycode) == "MG") { //Madagascar
4977 if (dol_strlen($phone) == 13) { //ex: +261_AB_CD_EFG_HI
4978 $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);
4979 }
4980 } elseif (strtoupper($countrycode) == "GB") { //Royaume uni
4981 if (dol_strlen($phone) == 13) { //ex: +44_ABCD_EFG_HIJ
4982 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
4983 }
4984 } elseif (strtoupper($countrycode) == "CH") { //Suisse
4985 if (dol_strlen($phone) == 12) { //ex: +41_AB_CDE_FG_HI
4986 $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);
4987 } elseif (dol_strlen($phone) == 15) { // +41_AB_CDE_FGH_IJKL
4988 $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);
4989 }
4990 } elseif (strtoupper($countrycode) == "TN") { //Tunisie
4991 if (dol_strlen($phone) == 12) { //ex: +216_AB_CDE_FGH
4992 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
4993 }
4994 } elseif (strtoupper($countrycode) == "GF") { //Guyane francaise
4995 if (dol_strlen($phone) == 13) { //ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4996 $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);
4997 }
4998 } elseif (strtoupper($countrycode) == "GP") { //Guadeloupe
4999 if (dol_strlen($phone) == 13) { //ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
5000 $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);
5001 }
5002 } elseif (strtoupper($countrycode) == "MQ") { //Martinique
5003 if (dol_strlen($phone) == 13) { //ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
5004 $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);
5005 }
5006 } elseif (strtoupper($countrycode) == "IT") { //Italie
5007 if (dol_strlen($phone) == 12) { //ex: +39_ABC_DEF_GHI
5008 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
5009 } elseif (dol_strlen($phone) == 13) { //ex: +39_ABC_DEF_GH_IJ
5010 $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);
5011 }
5012 } elseif (strtoupper($countrycode) == "AU") {
5013 //Australie
5014 if (dol_strlen($phone) == 12) {
5015 //ex: +61_A_BCDE_FGHI
5016 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 4);
5017 }
5018 } elseif (strtoupper($countrycode) == "LU") {
5019 // Luxembourg
5020 if (dol_strlen($phone) == 10) { // fix 6 digits +352_AA_BB_CC
5021 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2);
5022 } elseif (dol_strlen($phone) == 11) { // fix 7 digits +352_AA_BB_CC_D
5023 $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);
5024 } elseif (dol_strlen($phone) == 12) { // fix 8 digits +352_AA_BB_CC_DD
5025 $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);
5026 } elseif (dol_strlen($phone) == 13) { // mobile +352_AAA_BB_CC_DD
5027 $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);
5028 }
5029 } elseif (strtoupper($countrycode) == "PE") {
5030 // Peru
5031 if (dol_strlen($phone) == 7) { // fix 7 numbers without code AAA_BBBB
5032 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4);
5033 } elseif (dol_strlen($phone) == 9) { // mobile add code and fix 9 numbers +51_AAA_BBB_CCC
5034 $newphonewa = '+51' . $newphone;
5035 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 10, 3);
5036 } elseif (dol_strlen($phone) == 11) { // fix 11 numbers +511_AAA_BBBB
5037 $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 8, 4);
5038 } elseif (dol_strlen($phone) == 12) { // mobile +51_AAA_BBB_CCC
5039 $newphonewa = $newphone;
5040 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 10, 3) . $separ . substr($newphone, 14, 3);
5041 }
5042 } elseif (strtoupper($countrycode) == "IN") { //India
5043 if (dol_strlen($phone) == 13) {
5044 if ($withpicto == 'phone') { //ex: +91_AB_CDEF_GHIJ
5045 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 4) . $separ . substr($newphone, 9, 4);
5046 } else { //ex: +91_ABCDE_FGHIJ
5047 $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 5) . $separ . substr($newphone, 8, 5);
5048 }
5049 }
5050 }
5051
5052 $newphoneastart = $newphoneaend = '';
5053 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
5054 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
5055 $newphoneastart = '<a href="tel:' . urlencode($phone) . '">';
5056 $newphoneaend .= '</a>';
5057 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
5058 if (empty($user->clicktodial_loaded)) {
5059 $user->fetch_clicktodial();
5060 }
5061
5062 // Define urlmask
5063 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
5064 if (!empty($user->clicktodial_url)) {
5065 $urlmask = $user->clicktodial_url;
5066 }
5067
5068 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
5069 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
5070 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
5071 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
5072 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
5073 // Those lines are for substitution
5074 $substitarray = array(
5075 '__PHONEFROM__' => $clicktodial_poste,
5076 '__PHONETO__' => urlencode($phone),
5077 '__LOGIN__' => $clicktodial_login,
5078 '__PASS__' => $clicktodial_password
5079 );
5080 $url = make_substitutions($url, $substitarray);
5081 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
5082 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
5083 $newphoneastart = '<a href="' . $url . '" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
5084 $newphoneaend = '</a>';
5085 } else {
5086 // Old method
5087 $newphoneastart = '<a href="' . $url . '"';
5088 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
5089 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
5090 }
5091 $newphoneastart .= '>';
5092 $newphoneaend .= '</a>';
5093 }
5094 }
5095
5096 //if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
5097 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
5098 $type = 'AC_TEL';
5099 $addlinktoagenda = '';
5100 if ($addlink == 'AC_FAX') {
5101 $type = 'AC_FAX';
5102 }
5103 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
5104 $addlinktoagenda = '<a href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&backtopage=' . urlencode($_SERVER['REQUEST_URI']) . '&actioncode=' . $type . ($contactid ? '&contactid=' . $contactid : '') . ($socid ? '&socid=' . $socid : '') . '">' . img_object($langs->trans("AddAction"), "calendar") . '</a>';
5105 }
5106 if ($addlinktoagenda) {
5107 $newphone = '<span>' . $newphone . ' ' . $addlinktoagenda . '</span>';
5108 }
5109 }
5110 }
5111
5112 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
5113 // Link to Whatsapp
5114 $newphone .= ' <a href="https://wa.me/' . $newphonewa . '" target="_blank"'; // Use api to whatasapp contacts
5115 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
5116 }
5117
5118 if (empty($titlealt)) {
5119 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
5120 }
5121 $rep = '';
5122
5123 if ($hookmanager) {
5124 $parameters = array('countrycode' => $countrycode, 'cid' => $contactid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
5125 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
5126 $rep .= $hookmanager->resPrint;
5127 }
5128 if (empty($reshook)) {
5129 $picto = '';
5130 if ($withpicto) {
5131 if ($withpicto == 'fax') {
5132 $picto = 'phoning_fax';
5133 } elseif ($withpicto == 'phone') {
5134 $picto = 'phone';
5135 } elseif ($withpicto == 'mobile') {
5136 $picto = 'phoning_mobile';
5137 } else {
5138 $picto = '';
5139 }
5140 }
5141 if ($adddivfloat == 1) {
5142 $rep .= '<div class="nospan float' . ($morecss ? ' ' . $morecss : '') . '">';
5143 } elseif (empty($adddivfloat)) {
5144 $rep .= '<span' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
5145 }
5146
5147 $rep .= $newphoneastart;
5148 $rep .= ($withpicto ? img_picto($titlealt, $picto) : '');
5149 if ($separ != 'hidenum') {
5150 $rep .= ($withpicto ? ' ' : '') . $newphone;
5151 }
5152 $rep .= $newphoneaend;
5153
5154 if ($adddivfloat == 1) {
5155 $rep .= '</div>';
5156 } elseif (empty($adddivfloat)) {
5157 $rep .= '</span>';
5158 }
5159 }
5160
5161 return $rep;
5162}
5163
5172function dol_print_ip($ip, $mode = 0, $showname = 0)
5173{
5174 global $conf;
5175
5176 $ret = '';
5177 if (!isset($conf->cache['resolveips'])) {
5178 $conf->cache['resolveips'] = array();
5179 }
5180
5181 if ($mode != 2) {
5182 $countrycode = dolGetCountryCodeFromIp($ip);
5183 if ($countrycode) { // If success, countrycode is us, fr, ...
5184 if (file_exists(DOL_DOCUMENT_ROOT . '/theme/common/flags/' . $countrycode . '.png')) {
5185 $ret .= picto_from_langcode($countrycode);
5186 } else {
5187 $ret .= '(' . $countrycode . ')';
5188 }
5189 $ret .= '&nbsp;';
5190 } else {
5191 // Nothing
5192 }
5193 }
5194
5195 if (in_array($mode, [0, 2])) {
5196 $domain = '';
5197 if ($showname) {
5198 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
5199 $domain = gethostbyaddr($ip);
5200 $conf->cache['resolveips'][$ip] = $domain; // false or domain
5201 } else {
5202 $domain = $conf->cache['resolveips'][$ip];
5203 }
5204 }
5205 if ($domain) {
5206 $ret .= $domain;
5207 } else {
5208 $ret .= $ip;
5209 }
5210 }
5211
5212 return $ret;
5213}
5214
5227function getUserRemoteIP($trusted = 0)
5228{
5229 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
5230 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
5231 // 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)
5232 // This can happen if the proxy were added in the list of trusted proxy.
5233 return $ip;
5234 }
5235
5236 // Try to guess the real IP of client (but this may not be reliable)
5237 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
5238 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
5239 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
5240 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
5241 } else {
5242 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
5243 }
5244 } else {
5245 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
5246 }
5247 } else {
5248 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
5249 }
5250 return $ip;
5251}
5252
5259function dolGetCountryCodeFromIp($ip)
5260{
5261 $countrycode = '';
5262
5263 if (isModEnabled('geoipmaxmind')) {
5264 if (getDolGlobalString('GEOIP_VERSION') == 'php') {
5265 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5266 } else {
5267 $diroffile = getMultidirOutput(null, 'geoipmaxmind');
5268 $datafile = $diroffile . '/' . getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE_EMBEDDED');
5269 }
5270 //$ip='24.24.24.24';
5271 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
5272 if ($datafile) {
5273 try {
5274 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5275 $geoip = new DolGeoIP('country', $datafile);
5276 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
5277 $countrycode = $geoip->getCountryCodeFromIP($ip);
5278 } catch (Exception $e) {
5279 //print 'Error with GeoIP database: '.$e->getMessage();
5280 }
5281 }
5282 }
5283
5284 return $countrycode;
5285}
5286
5287
5294function dol_user_country()
5295{
5296 global $conf, $langs, $user;
5297
5298 //$ret=$user->xxx;
5299 $ret = '';
5300 if (isModEnabled('geoipmaxmind')) {
5301 $ip = getUserRemoteIP();
5302 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
5303 //$ip='24.24.24.24';
5304 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
5305 include_once DOL_DOCUMENT_ROOT . '/core/class/dolgeoip.class.php';
5306 $geoip = new DolGeoIP('country', $datafile);
5307 $countrycode = $geoip->getCountryCodeFromIP($ip);
5308 $ret = $countrycode;
5309 }
5310 return $ret;
5311}
5312
5325function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
5326{
5327 global $hookmanager;
5328
5329 $out = '';
5330
5331 if ($address) {
5332 if ($hookmanager) {
5333 $parameters = array('element' => $element, 'id' => $id);
5334 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
5335 $out .= $hookmanager->resPrint;
5336 }
5337 if (empty($reshook)) {
5338 if (empty($charfornl)) {
5339 $out .= nl2br((string) $address);
5340 } else {
5341 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
5342 }
5343
5344 // TODO Remove this block, we can add this using the hook now
5345 $showgmap = $showomap = 0;
5346 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
5347 $showgmap = 1;
5348 }
5349 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
5350 $showgmap = 1;
5351 }
5352 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
5353 $showgmap = 1;
5354 }
5355 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
5356 $showgmap = 1;
5357 }
5358 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
5359 $showomap = 1;
5360 }
5361 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
5362 $showomap = 1;
5363 }
5364 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
5365 $showomap = 1;
5366 }
5367 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
5368 $showomap = 1;
5369 }
5370 if ($showgmap) {
5371 $url = dol_buildpath('/google/gmaps.php?mode=' . $element . '&id=' . $id, 1);
5372 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5373 }
5374 if ($showomap) {
5375 $url = dol_buildpath('/openstreetmap/maps.php?mode=' . $element . '&id=' . $id, 1);
5376 $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '_openstreetmap" class="valigntextbottom" src="' . DOL_URL_ROOT . '/theme/common/gmap.png"></a>';
5377 }
5378 }
5379 }
5380 if ($noprint) {
5381 return $out;
5382 } else {
5383 print $out;
5384 return null;
5385 }
5386}
5387
5388
5398function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5399{
5400 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5401 return true;
5402 }
5403 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5404 return true;
5405 }
5406 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5407 return true;
5408 }
5409
5410 return false;
5411}
5412
5422function isValidMXRecord($domain)
5423{
5424 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5425 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5426 return 0;
5427 }
5428 if (function_exists('getmxrr')) {
5429 $mxhosts = array();
5430 $weight = array();
5431 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5432 if (count($mxhosts) > 1) {
5433 return 1;
5434 }
5435 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5436 return 1;
5437 }
5438
5439 return 0;
5440 }
5441 }
5442
5443 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5444 return -1;
5445}
5446
5454function isValidPhone($phone)
5455{
5456 return true;
5457}
5458
5459
5469function dolGetFirstLetters($s, $nbofchar = 1)
5470{
5471 $ret = '';
5472 $tmparray = explode(' ', $s);
5473 foreach ($tmparray as $tmps) {
5474 $ret .= dol_substr($tmps, 0, $nbofchar);
5475 }
5476
5477 return $ret;
5478}
5479
5480
5488function dol_strlen($string, $stringencoding = 'UTF-8')
5489{
5490 if (is_null($string)) {
5491 return 0;
5492 }
5493
5494 if (function_exists('mb_strlen')) {
5495 return mb_strlen($string, $stringencoding);
5496 } else {
5497 return strlen($string);
5498 }
5499}
5500
5511function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5512{
5513 global $langs;
5514
5515 if (empty($stringencoding)) {
5516 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5517 }
5518
5519 $ret = '';
5520 if (empty($trunconbytes)) {
5521 if (function_exists('mb_substr')) {
5522 $ret = mb_substr($string, $start, $length, $stringencoding);
5523 } else {
5524 $ret = substr($string, $start, $length);
5525 }
5526 } else {
5527 if (function_exists('mb_strcut')) {
5528 $ret = mb_strcut($string, $start, $length, $stringencoding);
5529 } else {
5530 $ret = substr($string, $start, $length);
5531 }
5532 }
5533 return $ret;
5534}
5535
5536
5550function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5551{
5552 global $conf;
5553
5554 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5555 return $string;
5556 }
5557
5558 if (empty($stringencoding)) {
5559 $stringencoding = 'UTF-8';
5560 }
5561 // reduce for small screen
5562 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5563 $size = round($size / 3);
5564 }
5565
5566 // We go always here
5567 if ($trunc == 'right') {
5568 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5569 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5570 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add '...'
5571 return dol_substr($newstring, 0, $size, $stringencoding) . ($nodot ? '' : '…');
5572 } else {
5573 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5574 return $string;
5575 }
5576 } elseif ($trunc == 'middle') {
5577 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5578 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5579 $size1 = (int) round($size / 2);
5580 $size2 = (int) round($size / 2);
5581 return dol_substr($newstring, 0, $size1, $stringencoding) . '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5582 } else {
5583 return $string;
5584 }
5585 } elseif ($trunc == 'left') {
5586 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5587 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5588 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add '...'
5589 return '…' . dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5590 } else {
5591 return $string;
5592 }
5593 } elseif ($trunc == 'wrap') {
5594 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5595 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5596 return dol_substr($newstring, 0, $size, $stringencoding) . "\n" . dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5597 } else {
5598 return $string;
5599 }
5600 } else {
5601 return 'BadParam3CallingDolTrunc';
5602 }
5603}
5604
5612function getPictoForType($key, $morecss = '')
5613{
5614 // Set array with type -> picto
5615 $type2picto = array(
5616 'varchar' => 'font',
5617 'text' => 'font',
5618 'html' => 'code',
5619 'int' => 'sort-numeric-down',
5620 'double' => 'sort-numeric-down',
5621 'price' => 'currency',
5622 'pricecy' => 'multicurrency',
5623 'password' => 'key',
5624 'boolean' => 'check-square',
5625 'date' => 'calendar',
5626 'datetime' => 'calendar',
5627 'duration' => 'hourglass',
5628 'phone' => 'phone',
5629 'mail' => 'email',
5630 'url' => 'url',
5631 'ip' => 'country',
5632 'select' => 'list',
5633 'sellist' => 'list',
5634 'stars' => 'fontawesome_star_fas',
5635 'radio' => 'check-circle',
5636 'checkbox' => 'list',
5637 'chkbxlst' => 'list',
5638 'link' => 'link',
5639 'icon' => "question",
5640 'point' => "country",
5641 'multipts' => 'country',
5642 'linestrg' => "country",
5643 'polygon' => "country",
5644 'separate' => 'minus'
5645 );
5646
5647 if (!empty($type2picto[$key])) {
5648 return img_picto('', $type2picto[$key], 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5649 }
5650
5651 return img_picto('', 'generic', 'class="pictofixedwidth' . ($morecss ? ' ' . $morecss : '') . '"');
5652}
5653
5654
5678function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5679{
5680 global $conf;
5681
5682 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5683 $url = DOL_URL_ROOT;
5684 $theme = isset($conf->theme) ? $conf->theme : null;
5685 $path = 'theme/' . $theme;
5686 if (empty($picto)) {
5687 $picto = 'generic';
5688 }
5689
5690 // Define fullpathpicto to use into src
5691 if ($pictoisfullpath) {
5692 // Clean parameters
5693 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5694 $picto .= '.png';
5695 }
5696 $fullpathpicto = $picto;
5697 $reg = array();
5698 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5699 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5700 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5701 }
5702 } else {
5703 // $picto can not be null since replaced with 'generic' in that case
5704 // $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5705 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5706 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5707 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5708
5709 // Fix some values of $pictowithouttext
5710 $pictoconvertkey = array(
5711 'facture' => 'bill',
5712 'shipping' => 'shipment',
5713 'fichinter' => 'intervention',
5714 'agenda' => 'calendar',
5715 'invoice_supplier' => 'supplier_invoice',
5716 'order_supplier' => 'supplier_order');
5717 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5718 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5719 }
5720
5721 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5722 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5723 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5724 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5725
5726 // Compatibility with old fontawesome versions
5727 if ($pictowithouttext == 'file-o') {
5728 $pictowithouttext = 'file';
5729 }
5730
5731 $pictowithouttextarray = explode('_', $pictowithouttext);
5732 $marginleftonlyshort = 0;
5733
5734 if (!empty($pictowithouttextarray[1])) {
5735 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5736 $fakey = 'fa-' . $pictowithouttextarray[0];
5737 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5738 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5739 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5740 } else {
5741 $fakey = 'fa-' . $pictowithouttext;
5742 $faprefix = 'fas';
5743 $facolor = '';
5744 $fasize = '';
5745 }
5746
5747 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5748 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5749 $morestyle = '';
5750 $reg = array();
5751 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5752 $morecss .= ($morecss ? ' ' : '') . $reg[1];
5753 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
5754 }
5755 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5756 $morestyle = $reg[1];
5757 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
5758 }
5759 $moreatt = trim($moreatt);
5760
5761 $enabledisablehtml = '<span class="' . $faprefix . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5762 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
5763 $enabledisablehtml .= '</span>';
5764
5765 return $enabledisablehtml;
5766 }
5767
5768 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
5769 $fakey = $pictowithouttext;
5770 $facolor = '';
5771 $fasize = '';
5772 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5773 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'))) {
5774 $fa = 'far';
5775 }
5776 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'))) {
5777 $fa = 'fab';
5778 }
5779
5780 $arrayconvpictotofa = getImgPictoConv('fa');
5781
5782 if ($pictowithouttext == 'off') {
5783 $fakey = 'fa-square';
5784 $fasize = '1.3em';
5785 } elseif ($pictowithouttext == 'on') {
5786 $fakey = 'fa-check-square';
5787 $fasize = '1.3em';
5788 } elseif ($pictowithouttext == 'listlight') {
5789 $fakey = 'fa-download';
5790 $marginleftonlyshort = 1;
5791 } elseif ($pictowithouttext == 'printer') {
5792 $fakey = 'fa-print';
5793 $fasize = '1.2em';
5794 } elseif ($pictowithouttext == 'note') {
5795 $fakey = 'fa-sticky-note';
5796 $marginleftonlyshort = 1;
5797 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5798 $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');
5799 $fakey = 'fa-' . $convertarray[$pictowithouttext];
5800 if (preg_match('/selected/', $pictowithouttext)) {
5801 $facolor = '#888';
5802 }
5803 $marginleftonlyshort = 1;
5804 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5805 $fakey = 'fa-' . $arrayconvpictotofa[$pictowithouttext];
5806 } else {
5807 $fakey = 'fa-' . $pictowithouttext;
5808 }
5809
5810 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5811 $morecss .= ' em092';
5812 }
5813 if (in_array($pictowithouttext, array('conferenceorbooth', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5814 $morecss .= ' em088';
5815 }
5816 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5817 $morecss .= ' em080';
5818 }
5819
5820 // Define $marginleftonlyshort
5821 $arrayconvpictotomarginleftonly = array(
5822 'bank',
5823 'check',
5824 'delete',
5825 'generic',
5826 'grip',
5827 'grip_title',
5828 'jabber',
5829 'grip_title',
5830 'grip',
5831 'listlight',
5832 'note',
5833 'on',
5834 'off',
5835 'playdisabled',
5836 'printer',
5837 'resize',
5838 'sign-out',
5839 'stats',
5840 'switch_on',
5841 'switch_on_grey',
5842 'switch_on_red',
5843 'switch_off',
5844 'switch_off_grey',
5845 'switch_off_red',
5846 'uparrow',
5847 '1uparrow',
5848 '1downarrow',
5849 '1leftarrow',
5850 '1rightarrow',
5851 '1uparrow_selected',
5852 '1downarrow_selected',
5853 '1leftarrow_selected',
5854 '1rightarrow_selected'
5855 );
5856 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5857 $marginleftonlyshort = 0;
5858 }
5859
5860 // Add CSS
5861 $arrayconvpictotomorcess = array(
5862 'action' => 'infobox-action',
5863 'account' => 'infobox-bank_account',
5864 'accounting_account' => 'infobox-bank_account',
5865 'accountline' => 'infobox-bank_account',
5866 'accountancy' => 'infobox-bank_account',
5867 'admin' => 'opacitymedium',
5868 'asset' => 'infobox-bank_account',
5869 'bank_account' => 'infobox-bank_account',
5870 'bill' => 'infobox-commande',
5871 'billa' => 'infobox-commande',
5872 'billr' => 'infobox-commande',
5873 'billd' => 'infobox-commande',
5874 'bookcal' => 'infobox-portal',
5875 'margin' => 'infobox-bank_account',
5876 'conferenceorbooth' => 'infobox-project',
5877 'cash-register' => 'infobox-portal',
5878 'contract' => 'infobox-contrat',
5879 'check' => 'font-status4',
5880 'conversation' => 'infobox-contrat',
5881 'donation' => 'infobox-commande',
5882 'dolly' => 'infobox-commande',
5883 'dollyrevert' => 'flip infobox-order_supplier',
5884 'ecm' => 'infobox-action',
5885 'eventorganization' => 'infobox-project',
5886 'hrm' => 'infobox-adherent',
5887 'group' => 'infobox-adherent',
5888 'intervention' => 'infobox-contrat',
5889 'incoterm' => 'infobox-supplier_proposal',
5890 'intracommreport' => 'infobox-bank_account',
5891 'currency' => 'infobox-bank_account',
5892 'multicurrency' => 'infobox-bank_account',
5893 'members' => 'infobox-adherent',
5894 'member' => 'infobox-adherent',
5895 'money-bill-alt' => 'infobox-bank_account',
5896 'order' => 'infobox-commande',
5897 'user' => 'infobox-adherent',
5898 'users' => 'infobox-adherent',
5899 'error' => 'pictoerror',
5900 'warning' => 'pictowarning',
5901 'switch_on' => 'font-status4',
5902 'switch_on_warning' => 'font-status4 warning',
5903 'switch_on_red' => 'font-status8',
5904 'switch_off_warning' => 'font-status4 warning',
5905 'switch_off_red' => 'font-status8',
5906 'holiday' => 'infobox-holiday',
5907 'info' => 'opacityhigh',
5908 'info_black' => 'purple',
5909 'invoice' => 'infobox-commande',
5910 'knowledgemanagement' => 'infobox-contrat rotate90',
5911 'loan' => 'infobox-commande',
5912 'payment' => 'infobox-bank_account',
5913 'payment_vat' => 'infobox-bank_account',
5914 'poll' => 'infobox-portal',
5915 'pos' => 'infobox-bank_account',
5916 'project' => 'infobox-project',
5917 'projecttask' => 'infobox-project',
5918 'propal' => 'infobox-propal',
5919 'proposal' => 'infobox-propal',
5920 'private' => 'infobox-project',
5921 'reception' => 'flip infobox-order_supplier',
5922 'recruitmentjobposition' => 'infobox-adherent',
5923 'recruitmentcandidature' => 'infobox-adherent',
5924 'resource' => 'infobox-action',
5925 'salary' => 'infobox-commande',
5926 'shapes' => 'infobox-adherent',
5927 'shipment' => 'infobox-commande',
5928 'store' => 'infobox-portal',
5929 'stripe' => 'infobox-bank_account',
5930 'supplier_invoice' => 'infobox-order_supplier',
5931 'supplier_invoicea' => 'infobox-order_supplier',
5932 'supplier_invoiced' => 'infobox-order_supplier',
5933 'supplier_invoicer' => 'infobox-order_supplier',
5934 'supplier' => 'infobox-order_supplier',
5935 'supplier_order' => 'infobox-order_supplier',
5936 'supplier_proposal' => 'infobox-supplier_proposal',
5937 'ticket' => 'infobox-contrat',
5938 'title_accountancy' => 'infobox-bank_account',
5939 'title_hrm' => 'infobox-holiday',
5940 'expensereport' => 'infobox-expensereport',
5941 'trip' => 'infobox-expensereport',
5942 'title_agenda' => 'infobox-action',
5943 'vat' => 'infobox-bank_account',
5944 'webportal' => 'infobox-portal',
5945 'website' => 'infobox-portal',
5946 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5947 'list-alt' => 'imgforviewmode',
5948 'calendar' => 'imgforviewmode',
5949 'calendarweek' => 'imgforviewmode',
5950 'calendarmonth' => 'imgforviewmode',
5951 'calendarday' => 'imgforviewmode',
5952 'calendarperuser' => 'imgforviewmode',
5953 'calendarpertype' => 'imgforviewmode'
5954 );
5955 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5956 $morecss .= ($morecss ? ' ' : '') . $arrayconvpictotomorcess[$pictowithouttext];
5957 }
5958
5959 // Define $color
5960 $arrayconvpictotocolor = array(
5961 'address' => '#6c6aa8',
5962 'building' => '#6c6aa8',
5963 'bom' => '#a69944',
5964 'clone' => '#999',
5965 'cog' => '#999',
5966 'companies' => '#6c6aa8',
5967 'company' => '#6c6aa8',
5968 'contact' => '#6c6aa8',
5969 'cron' => '#555',
5970 'dynamicprice' => '#a69944',
5971 'edit' => '#444',
5972 'note' => '#999',
5973 'error' => '',
5974 'help' => '#bbb',
5975 'listlight' => '#999',
5976 'language' => '#555',
5977 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5978 'lock' => '#ddd',
5979 'lot' => '#a69944',
5980 'map-marker-alt' => '#aaa',
5981 'mrp' => '#a69944',
5982 'product' => '#a69944',
5983 'service' => '#a69944',
5984 'inventory' => '#a69944',
5985 'stock' => '#a69944',
5986 'movement' => '#a69944',
5987 'other' => '#ddd',
5988 'world' => '#986c6a',
5989 'partnership' => '#6c6aa8',
5990 'playdisabled' => '#ccc',
5991 'printer' => '#444',
5992 'projectpub' => '#986c6a',
5993 'resize' => '#444',
5994 'rss' => '#cba',
5995 //'shipment'=>'#a69944',
5996 'search-plus' => '#808080',
5997 'security' => '#999',
5998 'square' => '#888',
5999 'stop-circle' => '#888',
6000 'stats' => '#444',
6001 'superadmin' => '#600',
6002 'switch_off' => '#999',
6003 'technic' => '#999',
6004 'tick' => '#282',
6005 'timespent' => '#555',
6006 'uncheck' => '#800',
6007 'uparrow' => '#555',
6008 'user-cog' => '#999',
6009 'country' => '#aaa',
6010 'globe-americas' => '#aaa',
6011 'region' => '#aaa',
6012 'state' => '#aaa',
6013 //'website' => '#304',
6014 'workstation' => '#a69944'
6015 );
6016 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
6017 $facolor = $arrayconvpictotocolor[$pictowithouttext];
6018 }
6019
6020 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
6021 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
6022 $morestyle = '';
6023 $reg = array();
6024 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
6025 $morecss .= ($morecss ? ' ' : '') . $reg[1];
6026 $moreatt = str_replace('class="' . $reg[1] . '"', '', $moreatt);
6027 }
6028 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
6029 $morestyle = $reg[1];
6030 $moreatt = str_replace('style="' . $reg[1] . '"', '', $moreatt);
6031 }
6032 $moreatt = trim($moreatt);
6033
6034 $enabledisablehtml = '<span class="' . $fa . ' ' . $fakey . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
6035 $enabledisablehtml .= ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . ($morestyle ? ' ' . $morestyle : '') . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
6036 $enabledisablehtml .= '</span>';
6037
6038 return $enabledisablehtml;
6039 }
6040
6041 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
6042 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/' . $theme; // If the theme does not have the same name as the module
6043 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
6044 $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
6045 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
6046 $path = $theme . '/theme/' . $theme; // If the theme have the same name as the module
6047 }
6048
6049 // If we ask an image into $url/$mymodule/img (instead of default path)
6050 $regs = array();
6051 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
6052 $picto = $regs[1];
6053 $path = $regs[2]; // $path is $mymodule
6054 }
6055
6056 // Clean parameters
6057 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
6058 $picto .= '.png';
6059 }
6060 // If alt path are defined, define url where img file is, according to physical path
6061 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
6062 foreach ($conf->file->dol_document_root as $type => $dirroot) {
6063 if ($type == 'main') {
6064 continue;
6065 }
6066 // This consumes a lot of time, that's why enabling alternative dir like "custom" dir should be avoid
6067 if (file_exists($dirroot . '/' . $path . '/img/' . $picto) && !empty($conf->file->dol_url_root)) {
6068 $url = DOL_URL_ROOT . $conf->file->dol_url_root[$type];
6069 break;
6070 }
6071 }
6072
6073 // $url is '' or '/custom', $path is current theme or
6074 $fullpathpicto = $url . '/' . $path . '/img/' . $picto;
6075 }
6076
6077 if ($srconly) {
6078 return $fullpathpicto;
6079 }
6080
6081 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
6082 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
6083}
6084
6092function getImgPictoConv($mode = 'fa')
6093{
6094 global $conf;
6095
6096 if (empty($mode) || $mode == 'fa') {
6097 // Array when the fa picto key is different than the Dolibarr picto key.
6098 $arrayconvpictotofa = array(
6099 'account' => 'university',
6100 'accounting_account' => 'clipboard-list',
6101 'accountline' => 'receipt',
6102 'accountancy' => 'search-dollar',
6103 'action' => 'calendar-alt',
6104 'add' => 'plus-circle',
6105 'address' => 'address-book',
6106 'ai' => 'magic',
6107 'admin' => 'star',
6108 'asset' => 'money-check-alt',
6109 'autofill' => 'fill',
6110 'back' => 'arrow-left',
6111 'bank_account' => 'university',
6112 'bill' => 'file-invoice-dollar',
6113 'billa' => 'file-excel',
6114 'billr' => 'file-invoice-dollar',
6115 'billd' => 'file-medical',
6116 'blockedlog' => 'file-archive',
6117 'bookcal' => 'calendar-check',
6118 'supplier_invoice' => 'file-invoice-dollar',
6119 'supplier_invoicea' => 'file-excel',
6120 'supplier_invoicer' => 'file-invoice-dollar',
6121 'supplier_invoiced' => 'file-medical',
6122 'bom' => 'shapes',
6123 'card' => 'address-card',
6124 'chart' => 'chart-line',
6125 'company' => 'building',
6126 'contact' => 'address-book',
6127 'contract' => 'suitcase',
6128 'collab' => 'people-arrows',
6129 'conversation' => 'comments',
6130 'country' => 'globe-americas',
6131 'cron' => 'business-time',
6132 'cross' => 'times',
6133 'chevron-double-left' => 'angle-double-left',
6134 'chevron-double-right' => 'angle-double-right',
6135 'chevron-double-down' => 'angle-double-down',
6136 'chevron-double-top' => 'angle-double-up',
6137 'donation' => 'gift',
6138 'dynamicprice' => 'hand-holding-usd',
6139 'setup' => 'cog',
6140 'companies' => 'building',
6141 'products' => 'cube',
6142 'commercial' => 'suitcase',
6143 'invoicing' => 'coins',
6144 'accounting' => 'search-dollar',
6145 'category' => 'tag',
6146 'dollyrevert' => 'dolly',
6147 'file-o' => 'file',
6148 'generate' => 'plus-square',
6149 'hrm' => 'user-tie',
6150 'incoterm' => 'truck-loading',
6151 'margin' => 'calculator',
6152 'members' => 'user-friends',
6153 'ticket' => 'ticket-alt',
6154 'globe' => 'external-link-alt',
6155 'lot' => 'barcode',
6156 'email' => 'at',
6157 'establishment' => 'building',
6158 'edit' => 'pencil-alt',
6159 'entity' => 'globe',
6160 'graph' => 'chart-line',
6161 'grip_title' => 'arrows-alt',
6162 'grip' => 'arrows-alt',
6163 'help' => 'question-circle',
6164 'generic' => 'file',
6165 'holiday' => 'umbrella-beach',
6166 'info' => 'info-circle',
6167 'info_black' => 'info-circle',
6168 'inventory' => 'boxes',
6169 'intracommreport' => 'globe-europe',
6170 'jobprofile' => 'cogs',
6171 'knowledgemanagement' => 'ticket-alt',
6172 'label' => 'layer-group',
6173 'layout' => 'columns',
6174 'line' => 'bars',
6175 'loan' => 'money-bill-alt',
6176 'member' => 'user-alt',
6177 'meeting' => 'chalkboard-teacher',
6178 'mrp' => 'cubes',
6179 'next' => 'arrow-alt-circle-right',
6180 'trip' => 'wallet',
6181 'expensereport' => 'wallet',
6182 'group' => 'users',
6183 'movement' => 'people-carry',
6184 'sign-out' => 'sign-out-alt',
6185 'superadmin' => 'star',
6186 'switch_off' => 'toggle-off',
6187 'switch_off_grey' => 'toggle-off',
6188 'switch_off_warning' => 'toggle-off',
6189 'switch_off_red' => 'toggle-off',
6190 'switch_on' => 'toggle-on',
6191 'switch_on_grey' => 'toggle-on',
6192 'switch_on_warning' => 'toggle-on',
6193 'switch_on_red' => 'toggle-on',
6194 'check' => 'check',
6195 'bookmark' => 'star',
6196 'bank' => 'university',
6197 'close_title' => 'times',
6198 'delete' => 'trash',
6199 'filter' => 'filter',
6200 'list-alt' => 'list-alt',
6201 'calendarlist' => 'bars',
6202 'calendar' => 'calendar-alt',
6203 'calendarmonth' => 'calendar-alt',
6204 'calendarweek' => 'calendar-week',
6205 'calendarday' => 'calendar-day',
6206 'calendarperuser' => 'table',
6207 'calendarpertype' => 'table',
6208 'intervention' => 'ambulance',
6209 'invoice' => 'file-invoice-dollar',
6210 'order' => 'file-invoice',
6211 'error' => 'exclamation-triangle',
6212 'warning' => 'exclamation-triangle',
6213 'other' => 'square',
6214 'playdisabled' => 'play',
6215 'pdf' => 'file-pdf',
6216 'poll' => 'check-double',
6217 'pos' => 'cash-register',
6218 'preview' => 'binoculars',
6219 'project' => 'project-diagram',
6220 'projectpub' => 'project-diagram',
6221 'projecttask' => 'tasks',
6222 'propal' => 'file-signature',
6223 'proposal' => 'file-signature',
6224 'partnership' => 'handshake',
6225 'payment' => 'money-check-alt',
6226 'payment_vat' => 'money-check-alt',
6227 'pictoconfirm' => 'check-square',
6228 'phoning' => 'phone',
6229 'phoning_mobile' => 'mobile-alt',
6230 'phoning_fax' => 'fax',
6231 'previous' => 'arrow-alt-circle-left',
6232 'printer' => 'print',
6233 'product' => 'cube',
6234 'puce' => 'angle-right',
6235 'recent' => 'check-square',
6236 'reception' => 'dolly',
6237 'recruitmentjobposition' => 'id-card-alt',
6238 'recruitmentcandidature' => 'id-badge',
6239 'resize' => 'crop',
6240 'supplier_order' => 'dol-order_supplier',
6241 'supplier_proposal' => 'file-signature',
6242 'refresh' => 'redo',
6243 'region' => 'map-marked',
6244 'replacement' => 'exchange-alt',
6245 'resource' => 'laptop-house',
6246 'recurring' => 'history',
6247 'service' => 'concierge-bell',
6248 'skill' => 'shapes',
6249 'state' => 'map-marked-alt',
6250 'security' => 'key',
6251 'salary' => 'wallet',
6252 'shipment' => 'dolly',
6253 'stock' => 'box-open',
6254 'stats' => 'chart-bar',
6255 'split' => 'code-branch',
6256 'status' => 'stop-circle',
6257 'stripe' => 'stripe-s',
6258 'supplier' => 'building',
6259 'technic' => 'cogs',
6260 'tick' => 'check',
6261 'timespent' => 'clock',
6262 'title_setup' => 'tools',
6263 'title_accountancy' => 'money-check-alt',
6264 'title_bank' => 'university',
6265 'title_hrm' => 'umbrella-beach',
6266 'title_agenda' => 'calendar-alt',
6267 'uncheck' => 'times',
6268 'uparrow' => 'share',
6269 'url' => 'external-link-alt',
6270 'vat' => 'money-check-alt',
6271 'vcard' => 'arrow-alt-circle-down',
6272 'jabber' => 'comment',
6273 'website' => 'globe-americas',
6274 'workstation' => 'pallet',
6275 'webhook' => 'bullseye',
6276 'world' => 'globe',
6277 'private' => 'user-lock',
6278 'conferenceorbooth' => 'chalkboard-teacher',
6279 'eventorganization' => 'project-diagram',
6280 'webportal' => 'door-open'
6281 );
6282
6283 if ($conf->currency == 'EUR') {
6284 $arrayconvpictotofa['currency'] = 'euro-sign';
6285 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
6286 } else {
6287 $arrayconvpictotofa['currency'] = 'dollar-sign';
6288 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
6289 }
6290 } else {
6291 $arrayconvpictotofa = array();
6292 }
6293
6294 return $arrayconvpictotofa;
6295}
6296
6297
6312function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
6313{
6314 if (strpos($picto, '^') === 0) {
6315 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6316 } else {
6317 return img_picto($titlealt, 'object_' . $picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
6318 }
6319}
6320
6332function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
6333{
6334 global $conf;
6335
6336 if (is_numeric($picto)) {
6337 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
6338 //$picto = $leveltopicto[$picto];
6339 return '<i class="fa fa-weather-level' . $picto . '"></i>';
6340 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6341 $picto .= '.png';
6342 }
6343
6344 $path = DOL_URL_ROOT . '/theme/' . $conf->theme . '/img/weather/' . $picto;
6345
6346 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
6347}
6348
6360function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
6361{
6362 global $conf;
6363
6364 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
6365 $picto .= '.png';
6366 }
6367
6368 if ($pictoisfullpath) {
6369 $path = $picto;
6370 } else {
6371 $path = DOL_URL_ROOT . '/theme/common/' . $picto;
6372
6373 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
6374 $themepath = DOL_DOCUMENT_ROOT . '/theme/' . $conf->theme . '/img/' . $picto;
6375
6376 if (file_exists($themepath)) {
6377 $path = $themepath;
6378 }
6379 }
6380 }
6381
6382 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
6383}
6384
6398function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
6399{
6400 global $langs;
6401
6402 if (empty($titlealt) || $titlealt == 'default') {
6403 if ($numaction == '-1' || $numaction == 'ST_NO') {
6404 $numaction = -1;
6405 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
6406 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
6407 $numaction = 0;
6408 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
6409 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
6410 $numaction = 1;
6411 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
6412 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
6413 $numaction = 2;
6414 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
6415 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
6416 $numaction = 3;
6417 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
6418 } else {
6419 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus ' . $numaction);
6420 $numaction = 0;
6421 }
6422 }
6423 if (!is_numeric($numaction)) {
6424 $numaction = 0;
6425 }
6426
6427 return img_picto($titlealt, (empty($picto) ? 'stcomm' . $numaction . '.png' : $picto), $moreatt);
6428}
6429
6437function img_edit_add($titlealt = 'default', $other = '')
6438{
6439 global $langs;
6440
6441 if ($titlealt == 'default') {
6442 $titlealt = $langs->trans('Add');
6443 }
6444
6445 return img_picto($titlealt, 'edit_add.png', $other);
6446}
6454function img_edit_remove($titlealt = 'default', $other = '')
6455{
6456 global $langs;
6457
6458 if ($titlealt == 'default') {
6459 $titlealt = $langs->trans('Remove');
6460 }
6461
6462 return img_picto($titlealt, 'edit_remove.png', $other);
6463}
6464
6473function img_edit($titlealt = 'default', $float = 0, $other = '')
6474{
6475 global $langs;
6476
6477 if ($titlealt == 'default') {
6478 $titlealt = $langs->trans('Modify');
6479 }
6480
6481 return img_picto($titlealt, 'edit', ($float ? 'style="float: ' . ($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right') . '"' : "") . ($other ? ' ' . $other : ''));
6482}
6483
6492function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
6493{
6494 global $langs;
6495
6496 if ($titlealt == 'default') {
6497 $titlealt = $langs->trans('View');
6498 }
6499
6500 $moreatt = ($float ? 'style="float: right" ' : '') . $other;
6501
6502 return img_picto($titlealt, 'eye', $moreatt);
6503}
6504
6513function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
6514{
6515 global $langs;
6516
6517 if ($titlealt == 'default') {
6518 $titlealt = $langs->trans('Delete');
6519 }
6520
6521 return img_picto($titlealt, 'delete', $other, 0, 0, 0, '', $morecss);
6522}
6523
6531function img_printer($titlealt = "default", $other = '')
6532{
6533 global $langs;
6534 if ($titlealt == "default") {
6535 $titlealt = $langs->trans("Print");
6536 }
6537 return img_picto($titlealt, 'printer', $other);
6538}
6539
6547function img_split($titlealt = 'default', $other = 'class="pictosplit"')
6548{
6549 global $langs;
6550
6551 if ($titlealt == 'default') {
6552 $titlealt = $langs->trans('Split');
6553 }
6554
6555 return img_picto($titlealt, 'split', $other);
6556}
6557
6565function img_help($usehelpcursor = 1, $usealttitle = 1)
6566{
6567 global $langs;
6568
6569 if ($usealttitle) {
6570 if (is_string($usealttitle)) {
6571 $usealttitle = dol_escape_htmltag($usealttitle);
6572 } else {
6573 $usealttitle = $langs->trans('Info');
6574 }
6575 }
6576
6577 return img_picto($usealttitle, 'info', 'style="vertical-align: middle;' . ($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')) . '"');
6578}
6579
6586function img_info($titlealt = 'default')
6587{
6588 global $langs;
6589
6590 if ($titlealt == 'default') {
6591 $titlealt = $langs->trans('Informations');
6592 }
6593
6594 return img_picto($titlealt, 'info', 'style="vertical-align: middle;"');
6595}
6596
6605function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6606{
6607 global $langs;
6608
6609 if ($titlealt == 'default') {
6610 $titlealt = $langs->trans('Warning');
6611 }
6612
6613 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6614 return img_picto($titlealt, 'warning', 'class="' . $morecss . '"' . ($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' ' . $moreatt) : ''));
6615}
6616
6623function img_error($titlealt = 'default')
6624{
6625 global $langs;
6626
6627 if ($titlealt == 'default') {
6628 $titlealt = $langs->trans('Error');
6629 }
6630
6631 return img_picto($titlealt, 'error');
6632}
6633
6641function img_next($titlealt = 'default', $moreatt = '')
6642{
6643 global $langs;
6644
6645 if ($titlealt == 'default') {
6646 $titlealt = $langs->trans('Next');
6647 }
6648
6649 //return img_picto($titlealt, 'next.png', $moreatt);
6650 return '<span class="fa fa-chevron-right paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6651}
6652
6660function img_previous($titlealt = 'default', $moreatt = '')
6661{
6662 global $langs;
6663
6664 if ($titlealt == 'default') {
6665 $titlealt = $langs->trans('Previous');
6666 }
6667
6668 //return img_picto($titlealt, 'previous.png', $moreatt);
6669 return '<span class="fa fa-chevron-left paddingright paddingleft" title="' . dol_escape_htmltag($titlealt) . '"></span>';
6670}
6671
6680function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6681{
6682 global $langs;
6683
6684 if ($titlealt == 'default') {
6685 $titlealt = $langs->trans('Down');
6686 }
6687
6688 return img_picto($titlealt, ($selected ? '1downarrow_selected' : '1downarrow'), 'class="imgdown' . ($moreclass ? " " . $moreclass : "") . '"');
6689}
6690
6699function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6700{
6701 global $langs;
6702
6703 if ($titlealt == 'default') {
6704 $titlealt = $langs->trans('Up');
6705 }
6706
6707 return img_picto($titlealt, ($selected ? '1uparrow_selected' : '1uparrow'), 'class="imgup' . ($moreclass ? " " . $moreclass : "") . '"');
6708}
6709
6718function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6719{
6720 global $langs;
6721
6722 if ($titlealt == 'default') {
6723 $titlealt = $langs->trans('Left');
6724 }
6725
6726 return img_picto($titlealt, ($selected ? '1leftarrow_selected' : '1leftarrow'), $moreatt);
6727}
6728
6737function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6738{
6739 global $langs;
6740
6741 if ($titlealt == 'default') {
6742 $titlealt = $langs->trans('Right');
6743 }
6744
6745 return img_picto($titlealt, ($selected ? '1rightarrow_selected' : '1rightarrow'), $moreatt);
6746}
6747
6755function img_allow($allow, $titlealt = 'default')
6756{
6757 global $langs;
6758
6759 if ($titlealt == 'default') {
6760 $titlealt = $langs->trans('Active');
6761 }
6762
6763 if ($allow == 1) {
6764 return img_picto($titlealt, 'tick');
6765 }
6766
6767 return '-';
6768}
6769
6777function img_credit_card($brand, $morecss = 'fa-2x inline-block valignmiddle')
6778{
6779 if (is_null($morecss)) {
6780 $morecss = 'fa-2x';
6781 }
6782
6783 if ($brand == 'visa' || $brand == 'Visa') {
6784 $brand = 'cc-visa';
6785 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6786 $brand = 'cc-mastercard';
6787 } elseif ($brand == 'amex' || $brand == 'American Express') {
6788 $brand = 'cc-amex';
6789 } elseif ($brand == 'discover' || $brand == 'Discover') {
6790 $brand = 'cc-discover';
6791 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6792 $brand = 'cc-jcb';
6793 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6794 $brand = 'cc-diners-club';
6795 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6796 $brand = 'credit-card';
6797 }
6798
6799 return '<span class="fa fa-' . $brand . ' fa-fw' . ($morecss ? ' ' . $morecss : '') . '"></span>';
6800}
6801
6810function img_mime($file, $titlealt = '', $morecss = '')
6811{
6812 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
6813
6814 $mimetype = dol_mimetype($file, '', 1);
6815 //$mimeimg = dol_mimetype($file, '', 2);
6816 $mimefa = dol_mimetype($file, '', 4);
6817
6818 if (empty($titlealt)) {
6819 $titlealt = 'Mime type: ' . $mimetype;
6820 }
6821
6822 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6823 return '<i class="fa fa-' . $mimefa . ' ' . (preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ') . ($morecss ? ' ' . $morecss : '') . '"' . ($titlealt ? ' title="' . dolPrintHTMLForAttribute($titlealt) . '"' : '') . '></i>';
6824}
6825
6826
6834function img_search($titlealt = 'default', $other = '')
6835{
6836 global $langs;
6837
6838 if ($titlealt == 'default') {
6839 $titlealt = $langs->trans('Search');
6840 }
6841
6842 $img = img_picto($titlealt, 'search', $other, 0, 1);
6843
6844 $input = '<input type="image" class="liste_titre" name="button_search" src="' . $img . '" ';
6845 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6846
6847 return $input;
6848}
6849
6857function img_searchclear($titlealt = 'default', $other = '')
6858{
6859 global $langs;
6860
6861 if ($titlealt == 'default') {
6862 $titlealt = $langs->trans('Search');
6863 }
6864
6865 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6866
6867 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="' . $img . '" ';
6868 $input .= 'value="' . dol_escape_htmltag($titlealt) . '" title="' . dol_escape_htmltag($titlealt) . '" >';
6869
6870 return $input;
6871}
6872
6887function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '', $textonpictotooltip = '', $cssfordropdown = 'info_admin')
6888{
6889 global $conf, $langs;
6890
6891 if ($infoonimgalt) {
6892 $result = img_picto($text, 'info', 'class="' . ($morecss ? ' ' . $morecss : '') . '"');
6893 } else {
6894 if (empty($conf->use_javascript_ajax)) {
6895 $textfordropdown = '';
6896 }
6897
6898 $class = (empty($admin) ? 'undefined' : ((string) $admin == '1' ? 'info' : $admin));
6899 $fa = 'info-circle';
6900 if ($picto == 'warning') {
6901 $fa = 'exclamation-triangle';
6902 }
6903 $result = ($nodiv ? '' : '<div class="wordbreak ' . $class . ($cssfordropdown ? ' ' . $cssfordropdown : '') . ($morecss ? ' ' . $morecss : '') . ($textfordropdown ? ' hidden' : '') . '">');
6904 $result .= img_picto(((string) $admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).($textonpictotooltip ? ' : '.$textonpictotooltip : ''), $fa);
6905 $result .= ' ';
6906 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6907 $result .= ($nodiv ? '' : '</div>');
6908
6909 if ($textfordropdown) {
6910 $tmpresult = '<span class="' . $class . ' '. $cssfordropdown.'text opacitymedium cursorpointer">' . $langs->trans($textfordropdown) . ' ' . img_picto($langs->trans($textfordropdown), '1downarrow') . '</span>';
6911 $tmpresult .= '<script nonce="' . getNonce() . '" type="text/javascript">
6912 jQuery(document).ready(function() {
6913 jQuery(".' . $cssfordropdown . 'text").click(function() {
6914 console.log("toggle text of .'.$cssfordropdown.'");
6915 jQuery(".' . $cssfordropdown . '").toggle().removeClass("hidden");
6916 });
6917 });
6918 </script>';
6919
6920 $result = $tmpresult . $result;
6921 }
6922 }
6923
6924 return $result;
6925}
6926
6927
6939function dol_print_error($db = null, $error = '', $errors = null)
6940{
6941 global $conf, $langs, $user, $argv;
6942 global $dolibarr_main_prod;
6943
6944 $out = '';
6945 $syslog = '';
6946
6947 // If error occurs before the $lang object was loaded
6948 if (!$langs) {
6949 require_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
6950 $langs = new Translate('', $conf);
6951 $langs->load("main");
6952 }
6953
6954 // Load translation files required by the error messages
6955 $langs->loadLangs(array('main', 'errors'));
6956
6957 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6958 $out .= $langs->trans("DolibarrHasDetectedError") . ".<br>\n";
6959 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6960 $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";
6961 }
6962 $out .= $langs->trans("InformationToHelpDiagnose") . ":<br>\n";
6963
6964 $out .= "<b>" . $langs->trans("Date") . ":</b> " . dol_print_date(time(), 'dayhourlog') . "<br>\n";
6965 $out .= "<b>" . $langs->trans("Dolibarr") . ":</b> " . DOL_VERSION . " - https://www.dolibarr.org<br>\n";
6966 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6967 $out .= "<b>" . $langs->trans("LevelOfFeature") . ":</b> " . getDolGlobalInt('MAIN_FEATURES_LEVEL') . "<br>\n";
6968 }
6969 if ($user instanceof User) {
6970 $out .= "<b>" . $langs->trans("Login") . ":</b> " . $user->login . "<br>\n";
6971 }
6972 if (function_exists("phpversion")) {
6973 $out .= "<b>" . $langs->trans("PHP") . ":</b> " . phpversion() . "<br>\n";
6974 }
6975 $out .= "<b>" . $langs->trans("Server") . ":</b> " . (isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '') . "<br>\n";
6976 if (function_exists("php_uname")) {
6977 $out .= "<b>" . $langs->trans("OS") . ":</b> " . php_uname() . "<br>\n";
6978 }
6979 $out .= "<b>" . $langs->trans("UserAgent") . ":</b> " . (isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '') . "<br>\n";
6980 $out .= "<br>\n";
6981 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6982 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6983 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6984 $out .= "<br>\n";
6985 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6986 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6987 } else { // Mode CLI
6988 $out .= '> ' . $langs->transnoentities("ErrorInternalErrorDetected") . ":\n" . $argv[0] . "\n";
6989 $syslog .= "pid=" . dol_getmypid();
6990 }
6991
6992 if (!empty($conf->modules)) {
6993 $out .= "<b>" . $langs->trans("Modules") . ":</b> " . implode(', ', $conf->modules) . "<br>\n";
6994 }
6995
6996 if (is_object($db)) {
6997 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6998 $out .= "<b>" . $langs->trans("DatabaseTypeManager") . ":</b> " . $db->type . "<br>\n";
6999 $lastqueryerror = $db->lastqueryerror();
7000 if (!utf8_check($lastqueryerror)) {
7001 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
7002 }
7003 $out .= "<b>" . $langs->trans("RequestLastAccessInError") . ":</b> " . ($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
7004 $out .= "<b>" . $langs->trans("ReturnCodeLastAccessInError") . ":</b> " . ($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
7005 $out .= "<b>" . $langs->trans("InformationLastAccessInError") . ":</b> " . ($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError")) . "<br>\n";
7006 $out .= "<br>\n";
7007 } else { // Mode CLI
7008 // No dol_escape_htmltag for output, we are in CLI mode
7009 $out .= '> ' . $langs->transnoentities("DatabaseTypeManager") . ":\n" . $db->type . "\n";
7010 $out .= '> ' . $langs->transnoentities("RequestLastAccessInError") . ":\n" . ($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
7011 $out .= '> ' . $langs->transnoentities("ReturnCodeLastAccessInError") . ":\n" . ($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
7012 $out .= '> ' . $langs->transnoentities("InformationLastAccessInError") . ":\n" . ($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError")) . "\n";
7013 }
7014 $syslog .= ", sql=" . $db->lastquery();
7015 $syslog .= ", db_error=" . $db->lasterror();
7016 }
7017
7018 if ($error || $errors) {
7019 // Merge all into $errors array
7020 if (is_array($error) && is_array($errors)) {
7021 $errors = array_merge($error, $errors);
7022 } elseif (is_array($error)) { // deprecated, use second parameters
7023 $errors = $error;
7024 } elseif (is_array($errors) && !empty($error)) {
7025 $errors = array_merge(array($error), $errors);
7026 } elseif (!empty($error)) {
7027 $errors = array_merge(array($error), array($errors));
7028 }
7029
7030 $langs->load("errors");
7031
7032 foreach ($errors as $msg) {
7033 if (empty($msg)) {
7034 continue;
7035 }
7036 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
7037 $out .= "<b>" . $langs->trans("Message") . ":</b> " . dol_escape_htmltag($msg) . "<br>\n";
7038 } else { // Mode CLI
7039 $out .= '> ' . $langs->transnoentities("Message") . ":\n" . $msg . "\n";
7040 }
7041 $syslog .= ", msg=" . $msg;
7042 }
7043 }
7044 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
7045 xdebug_print_function_stack();
7046 $out .= '<b>XDebug information:</b>' . "<br>\n";
7047 $out .= 'File: ' . xdebug_call_file() . "<br>\n";
7048 $out .= 'Line: ' . xdebug_call_line() . "<br>\n";
7049 $out .= 'Function: ' . xdebug_call_function() . "<br>\n";
7050 $out .= "<br>\n";
7051 }
7052
7053 // Return a http header with error code if possible
7054 if (!headers_sent()) {
7055 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
7056 top_httphead();
7057 }
7058 //http_response_code(500); // If we use 500, message is not output with some command line tools
7059 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
7060 }
7061
7062 if (empty($dolibarr_main_prod)) {
7063 print $out;
7064 } else {
7065 if (empty($langs->defaultlang)) {
7066 $langs->setDefaultLang();
7067 }
7068 $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.
7069 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
7070 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";
7071 print $langs->trans("DolibarrHasDetectedError") . '. ';
7072 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
7073 if (!defined("MAIN_CORE_ERROR")) {
7074 define("MAIN_CORE_ERROR", 1);
7075 }
7076 }
7077
7078 dol_syslog("Error " . $syslog, LOG_ERR);
7079}
7080
7091function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
7092{
7093 global $langs;
7094
7095 if (empty($email)) {
7096 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
7097 }
7098
7099 $langs->load("errors");
7100 $now = dol_now();
7101
7102 print '<br><div class="center login_main_message"><div class="' . $morecss . '">';
7103 print $langs->trans("ErrorContactEMail", $email, $prefixcode . '-' . dol_print_date($now, '%Y%m%d%H%M%S'));
7104 if ($errormessage) {
7105 print '<br><br>' . $errormessage;
7106 }
7107 if (is_array($errormessages) && count($errormessages)) {
7108 foreach ($errormessages as $mesgtoshow) {
7109 print '<br><br>' . $mesgtoshow;
7110 }
7111 }
7112 print '</div></div>';
7113}
7114
7131function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
7132{
7133 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
7134}
7135
7154function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
7155{
7156 global $langs, $form;
7157 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
7158
7159 if ($moreattrib == 'class="right"') {
7160 $prefix .= 'right '; // For backward compatibility
7161 }
7162
7163 $tooltip = (string) $tooltip; // In case $tooltip is null
7164
7165 $sortorder = strtoupper((string) $sortorder);
7166 $out = '';
7167 $sortimg = '';
7168
7169 $tag = 'th';
7170 if ($thead == 2) {
7171 $tag = 'div';
7172 }
7173
7174 $tmpsortfield = explode(',', (string) $sortfield);
7175 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
7176 $tmpfield = explode(',', $field);
7177 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
7178
7179 if (strpos((string) $tooltip, ':') !== false) {
7180 $tmptooltip = explode(':', (string) $tooltip);
7181 } else {
7182 $tmptooltip = array($tooltip);
7183 }
7184
7185 $wrapcolumntitle = (empty($forcenowrapcolumntitle) || (!empty($tmptooltip[2]) && $tmptooltip[2] == '-1'));
7186
7187 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle) {
7188 $prefix = 'wrapcolumntitle ' . $prefix;
7189 }
7190
7191 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
7192 // If field is used as sort criteria we use a specific css class liste_titre_sel
7193 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
7194 $liste_titre = 'liste_titre';
7195 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
7196 $liste_titre = 'liste_titre_sel';
7197 }
7198
7199 $tagstart = '<' . $tag . ' class="' . $prefix . $liste_titre . '" ' . $moreattrib;
7200 //$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)).'"' : '');
7201 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && $wrapcolumntitle && !dol_textishtml($name)) ? ' title="' . dolPrintHTMLForAttribute($langs->trans($name)) . '"' : '';
7202 $tagstart .= '>';
7203
7204 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7205 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7206 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7207 $options = preg_replace('/&+/i', '&', $options);
7208 if (!preg_match('/^&/', $options)) {
7209 $options = '&' . $options;
7210 }
7211
7212 $sortordertouseinlink = '';
7213 if ($field1 != $sortfield1) { // We are on another field than current sorted field
7214 if (preg_match('/^DESC/i', $sortorder)) {
7215 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7216 } else { // We reverse the var $sortordertouseinlink
7217 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7218 }
7219 } else { // We are on field that is the first current sorting criteria
7220 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
7221 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
7222 } else {
7223 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
7224 }
7225 }
7226 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
7227 $out .= '<a class="reposition" href="' . dolBuildUrl($file, ['sortfield' => $field, 'sortorder' => $sortordertouseinlink, 'begin' => $begin]) . $options . '"';
7228 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
7229 $out .= '>';
7230 }
7231 if ($tooltip && $tmptooltip[0]) {
7232 // You can also use 'TranslationString:[keyfortooltiponclick]:[tooltipdirection]' for a tooltip on click or to change tooltip position.
7233 $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]));
7234 } else {
7235 $out .= $langs->trans((string) $name);
7236 }
7237
7238 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
7239 $out .= '</a>';
7240 }
7241
7242 if (empty($thead) && $field) { // If this is a sort field
7243 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
7244 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
7245 $options = preg_replace('/&+/i', '&', $options);
7246 if (!preg_match('/^&/', $options)) {
7247 $options = '&' . $options;
7248 }
7249
7250 if (!$sortorder || ($field1 != $sortfield1)) {
7251 // Nothing
7252 } else {
7253 if (preg_match('/^DESC/', $sortorder)) {
7254 $sortimg .= '<span class="nowrap">' . img_up("Z-A", 0, 'paddingright') . '</span>';
7255 }
7256 if (preg_match('/^ASC/', $sortorder)) {
7257 $sortimg .= '<span class="nowrap">' . img_down("A-Z", 0, 'paddingright') . '</span>';
7258 }
7259 }
7260 }
7261
7262 $tagend = '</' . $tag . '>';
7263
7264 $out = $tagstart . $sortimg . $out . $tagend;
7265
7266 return $out;
7267}
7268
7277function print_titre($title)
7278{
7279 dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
7280
7281 print '<div class="titre">' . $title . '</div>';
7282}
7283
7295function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
7296{
7297 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
7298}
7299
7314function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '', $morecssonpicto = 'widthpictotitle')
7315{
7316 $return = '';
7317
7318 if ($picto == 'setup') {
7319 $picto = 'generic';
7320 }
7321
7322 $return .= "\n";
7323 $return .= '<table ' . ($id ? 'id="' . $id . '" ' : '') . 'class="centpercent notopnoleftnoright table-fiche-title' . ($morecssontable ? ' ' . $morecssontable : '') . '">'; // margin bottom must be same than into print_barre_list
7324 $return .= '<tr class="toptitle">';
7325 if ($picto) {
7326 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">' . img_picto('', $picto, 'class="valignmiddle pictotitle'.($morecssonpicto ? ' '.$morecssonpicto : '').'"', $pictoisfullpath) . '</td>';
7327 }
7328 $return .= '<td class="nobordernopadding valignmiddle col-title">';
7329 $return .= '<div class="titre inline-block">';
7330 $return .= '<span class="inline-block valignmiddle print-barre-liste">' . $title . '</span>'; // $title is already HTML sanitized content
7331 $return .= '</div>';
7332 $return .= '</td>';
7333 if (dol_strlen($morehtmlcenter)) {
7334 $return .= '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7335 }
7336 if (dol_strlen($morehtmlright)) {
7337 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">' . $morehtmlright . '</td>';
7338 }
7339 $return .= '</tr></table>' . "\n";
7340
7341 return $return;
7342}
7343
7367function 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 = '')
7368{
7369 global $conf, $langs;
7370
7371 $savlimit = $limit;
7372 $savtotalnboflines = $totalnboflines;
7373 if (is_numeric($totalnboflines)) {
7374 $totalnboflines = abs($totalnboflines);
7375 }
7376
7377 // Detect if there is a subtitle
7378 $subtitle = '';
7379 $tmparray = preg_split('/<br>/i', $title, 2);
7380 if (!empty($tmparray[1])) {
7381 $title = $tmparray[0];
7382 $subtitle = $tmparray[1];
7383 }
7384
7385 $page = (int) $page;
7386
7387 if ($picto == 'setup') {
7388 $picto = 'title_setup';
7389 }
7390 if (($conf->browser->name == 'ie') && $picto == 'generic') {
7391 $picto = 'title.gif';
7392 }
7393 if ($limit < 0) {
7394 $limit = $conf->liste_limit;
7395 }
7396
7397 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
7398 $nextpage = 1;
7399 } else {
7400 $nextpage = 0;
7401 }
7402 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
7403
7404 print "\n";
7405 print "<!-- Begin print_barre_liste -->\n";
7406 print '<table class="centpercent notopnoleftnoright table-fiche-title' . ($morecss ? ' ' . $morecss : '') . '">';
7407 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
7408
7409 // Left
7410
7411 if ($picto && $title) {
7412 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
7413 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
7414 print '</td>';
7415 }
7416
7417 print '<td class="nobordernopadding valignmiddle col-title">';
7418 print '<div class="titre inline-block nowrap">';
7419 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()
7420 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
7421 if (is_numeric($totalnboflines) && (int) $totalnboflines > 0) {
7422 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="' . $langs->trans("NbRecordQualified") . '">(' . $totalnboflines . ')</span>';
7423 } else {
7424 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle">(' . $totalnboflines . ')</span>';
7425 }
7426 }
7427 print '</div>';
7428 if (!empty($subtitle)) {
7429 print '<br><div class="subtitle inline-block hideonsmartphone">' . $subtitle . '</div>';
7430 }
7431 print '</td>';
7432
7433 // Center
7434 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
7435 print '<td class="nobordernopadding center valignmiddle col-center">' . $morehtmlcenter . '</td>';
7436 }
7437
7438 // Right
7439 print '<td class="nobordernopadding valignmiddle right col-right">';
7440 print '<input type="hidden" name="pageplusoneold" value="' . ((int) $page + 1) . '">';
7441 $query = [];
7442 parse_str($options, $query);
7443 if ($sortfield) {
7444 $query += ['sortfield' => $sortfield];
7445 }
7446 if ($sortorder) {
7447 $query += ['sortorder' => $sortorder];
7448 }
7449
7450 $options = '&' . http_build_query($query);
7451 if ($page) {
7452 $query = array_merge($query, ['page' => $page]);
7453 }
7454 // Show navigation bar
7455 $pagelist = '';
7456 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
7457 if ($totalnboflines) { // If we know total nb of lines
7458 // Define nb of extra page links before and after selected page + ... + first or last
7459 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
7460
7461 if ($limit > 0) {
7462 $nbpages = ceil($totalnboflines / $limit);
7463 } else {
7464 $nbpages = 1;
7465 }
7466 $cpt = ($page - $maxnbofpage);
7467 if ($cpt < 0) {
7468 $cpt = 0;
7469 }
7470
7471 if ($cpt >= 1) {
7472 if (empty($pagenavastextinput)) {
7473 $query['page'] = 0;
7474 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">1</a></li>';
7475 if ($cpt > 2) {
7476 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7477 } elseif ($cpt == 2) {
7478 $query['page'] = 0;
7479 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">2</a></li>';
7480 }
7481 }
7482 }
7483
7484 do {
7485 if ($pagenavastextinput) {
7486 if ($cpt == $page) {
7487 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="' . ($totalnboflines > 100 ? 'width40' : 'width25') . ' center pageplusone heightofcombo" name="pageplusone" value="' . ($page + 1) . '"></li>';
7488 $pagelist .= '/';
7489 }
7490 } else {
7491 if ($cpt == $page) {
7492 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . '</span></li>';
7493 } else {
7494 $query['page'] = $cpt;
7495 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($cpt + 1) . '</a></li>';
7496 }
7497 }
7498 $cpt++;
7499 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
7500
7501 if (empty($pagenavastextinput)) {
7502 if ($cpt < $nbpages) {
7503 if ($cpt < $nbpages - 2) {
7504 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
7505 } elseif ($cpt == $nbpages - 2) {
7506 $query['page'] = ($nbpages - 2);
7507 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . ($nbpages - 1) . '</a></li>';
7508 }
7509 $query['page'] = ($nbpages - 1);
7510 $pagelist .= '<li class="pagination"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7511 }
7512 } else {
7513 $query['page'] = ($nbpages - 1);
7514 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="' . dolBuildUrl($file, $query) . '">' . $nbpages . '</a></li>';
7515 }
7516 } else {
7517 $pagelist .= '<li class="pagination"><span class="active">' . ($page + 1) . "</li>";
7518 }
7519 }
7520
7521 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
7522 // Show the combolist to select number of record per page and the navigation arrows.
7523 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
7524 }
7525
7526 // js to autoselect page field on focus
7527 if ($pagenavastextinput) {
7528 print ajax_autoselect('.pageplusone');
7529 }
7530
7531 print '</td>';
7532 print '</tr>';
7533
7534 print "</table>\n";
7535
7536 // Center
7537 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
7538 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">' . $morehtmlcenter . '</div>';
7539 }
7540
7541 print "<!-- End title -->\n\n";
7542}
7543
7560function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
7561{
7562 global $conf, $langs;
7563
7564 print '<div class="pagination"><ul>';
7565 if ($beforearrows) {
7566 print '<li class="paginationbeforearrows">';
7567 print $beforearrows;
7568 print '</li>';
7569 }
7570
7571 if (empty($hidenavigation)) {
7572 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
7573 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
7574 $pagesizechoices .= ',5000:5000';
7575 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
7576 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
7577 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
7578 //$pagesizechoices .= ',2:2';
7579 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
7580 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
7581 }
7582
7583 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7584 print '<li class="pagination">';
7585 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 . '">';
7586 print '<datalist id="limitlist">';
7587 } else {
7588 print '<li class="paginationcombolimit valignmiddle">';
7589 print '<select id="limit' . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix) . '" name="'.(is_numeric($selectlimitsuffix) ? 'limit' : $selectlimitsuffix).'" class="flat selectlimit nopadding maxwidth75 center' . (is_numeric($selectlimitsuffix) ? '' : ' ' . $selectlimitsuffix) . '" title="' . dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")) . '">';
7590 }
7591 $tmpchoice = explode(',', $pagesizechoices);
7592 $tmpkey = $limit . ':' . $limit;
7593 if (!in_array($tmpkey, $tmpchoice)) {
7594 $tmpchoice[$tmpkey] = $tmpkey;
7595 }
7596 $tmpkey = $conf->liste_limit . ':' . $conf->liste_limit;
7597 if (!in_array($tmpkey, $tmpchoice)) {
7598 $tmpchoice[$tmpkey] = $tmpkey;
7599 }
7600 asort($tmpchoice, SORT_NUMERIC);
7601 foreach ($tmpchoice as $val) {
7602 $selected = '';
7603 $tmp = explode(':', $val);
7604 $key = $tmp[0];
7605 $val = $tmp[1];
7606 if ($key != '' && $val != '') {
7607 if ((int) $key == (int) $limit) {
7608 $selected = ' selected="selected"';
7609 }
7610 print '<option name="' . $key . '"' . $selected . '>' . dol_escape_htmltag($val) . '</option>' . "\n";
7611 }
7612 }
7613 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
7614 print '</datalist>';
7615 } else {
7616 print '</select>';
7617 print ajax_combobox("limit" . (is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
7618 //print ajax_combobox("limit");
7619 }
7620
7621 if ($conf->use_javascript_ajax) {
7622 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
7623 <script>
7624 jQuery(document).ready(function () {
7625 jQuery(".selectlimit").change(function() {
7626 console.log("We change limit so we submit the form");
7627 $(this).parents(\'form:first\').submit();
7628 });
7629 });
7630 </script>
7631 ';
7632 }
7633 print '</li>';
7634 }
7635 if ($page > 0) {
7636 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>';
7637 }
7638 if ($betweenarrows) {
7639 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7640 print $betweenarrows;
7641 print '<!--</div>-->';
7642 }
7643 if ($nextpage > 0) {
7644 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>';
7645 }
7646 if ($afterarrows) {
7647 print '<li class="paginationafterarrows">';
7648 print $afterarrows;
7649 print '</li>';
7650 }
7651 }
7652 print '</ul></div>' . "\n";
7653}
7654
7655
7667function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7668{
7669 $morelabel = '';
7670
7671 if (preg_match('/%/', $rate)) {
7672 $rate = str_replace('%', '', $rate);
7673 $addpercent = true;
7674 }
7675 $reg = array();
7676 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7677 $morelabel = ' (' . $reg[1] . ')';
7678 $rate = preg_replace('/\s*' . preg_quote($morelabel, '/') . '/', '', $rate);
7679 $morelabel = ' ' . ($html ? '<span class="opacitymedium small">' : '') . '(' . $reg[1] . ')' . ($html ? '</span>' : '');
7680 }
7681 if (preg_match('/\*/', $rate)) {
7682 $rate = str_replace('*', '', $rate);
7683 $info_bits |= 1;
7684 }
7685
7686 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7687 if (!preg_match('/\//', $rate)) {
7688 $ret = price($rate, 0, '', 0, 0) . ($addpercent ? '%' : '');
7689 } else {
7690 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7691 $ret = $rate . ($addpercent ? '%' : '');
7692 }
7693 if (($info_bits & 1) && $usestarfornpr >= 0) {
7694 $ret .= ' *';
7695 }
7696 $ret .= $morelabel;
7697 return $ret;
7698}
7699
7700
7715function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7716{
7717 global $langs, $conf;
7718
7719 // Clean parameters
7720 if (empty($amount)) {
7721 $amount = 0; // To have a numeric value if amount not defined or = ''
7722 }
7723 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7724 if ($rounding == -1) {
7725 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7726 }
7727 $nbdecimal = $rounding;
7728
7729 if ($outlangs === 'none') {
7730 // Use international separators
7731 $dec = '.';
7732 $thousand = '';
7733 } else {
7734 // Output separators by default (french)
7735 $dec = ',';
7736 $thousand = ' ';
7737
7738 // If $outlangs not forced, we use use language
7739 if (!($outlangs instanceof Translate)) {
7740 $outlangs = $langs;
7741 }
7742
7743 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7744 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7745 }
7746 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7747 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7748 }
7749 if ($thousand == 'None') {
7750 $thousand = '';
7751 } elseif ($thousand == 'Space') {
7752 $thousand = ' ';
7753 }
7754 }
7755 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7756
7757 //print "amount=".$amount."-";
7758 $amount = str_replace(',', '.', $amount); // should be useless
7759 //print $amount."-";
7760 $data = explode('.', $amount);
7761 $decpart = isset($data[1]) ? $data[1] : '';
7762 $decpart = preg_replace('/0+$/i', '', $decpart); // Remove 0 at end of decimal part
7763 //print "decpart=".$decpart."<br>";
7764 $end = '';
7765
7766 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7767 if (dol_strlen($decpart) > $nbdecimal) {
7768 $nbdecimal = dol_strlen($decpart);
7769 }
7770
7771 // If nbdecimal is higher than max to show
7772 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7773 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7774 $nbdecimal = $nbdecimalmaxshown;
7775 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7776 // If output is truncated, we show ...
7777 $end = '...';
7778 }
7779 }
7780
7781 // If force rounding
7782 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7783 if ($forcerounding === 'MU') {
7784 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7785 } elseif ($forcerounding === 'MT') {
7786 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7787 } elseif ($forcerounding >= 0) {
7788 $nbdecimal = (int) $forcerounding;
7789 }
7790 }
7791
7792 // Format number
7793 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7794 // Add symbol of currency if requested
7795 $cursymbolbefore = $cursymbolafter = '';
7796 if ($currency_code && is_object($outlangs)) {
7797 if ($currency_code == 'auto') {
7798 $currency_code = $conf->currency;
7799 }
7800
7801 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7802 $listoflanguagesbefore = array('nl_NL');
7803 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7804 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7805 } else {
7806 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7807 $cursymbolafter .= ($tmpcur == $currency_code ? ' ' . $tmpcur : $tmpcur);
7808 }
7809 }
7810 if ($form) {
7811 $output = preg_replace('/\s/', '&nbsp;', $output);
7812 $output = $cursymbolbefore . $output . $end . ($cursymbolafter ? ' <span class="small">'.$cursymbolafter.'</span>' : '');
7813 $output = preg_replace('/\'/', '&#039;', $output);
7814 } else {
7815 $output = $cursymbolbefore . $output . $end . ($cursymbolafter ? ' '.$cursymbolafter : '');
7816 }
7817
7818 return $output;
7819}
7820
7846function price2num($amount, $rounding = '', $option = 0)
7847{
7848 global $langs;
7849
7850 // Clean parameters
7851 if (is_null($amount)) {
7852 $amount = '';
7853 }
7854
7855 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7856 // Numbers must be '1234.56'
7857 // Decimal delimiter for PHP and database SQL requests must be '.'
7858 $dec = ',';
7859 $thousand = ' ';
7860 if (is_null($langs)) { // $langs is not defined, we use english values.
7861 $dec = '.';
7862 $thousand = ',';
7863 } else {
7864 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7865 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7866 }
7867 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7868 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7869 }
7870 }
7871 if ($thousand == 'None') {
7872 $thousand = '';
7873 } elseif ($thousand == 'Space') {
7874 $thousand = ' ';
7875 }
7876 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7877
7878 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7879 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7880 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7881 if (!is_numeric($amount)) {
7882 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7883 }
7884
7885 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
7886 $amount = str_replace($thousand, '', $amount);
7887 }
7888
7889 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7890 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7891 // So if number was already a good number, it is converted into local Dolibarr setup.
7892 if (is_numeric($amount)) {
7893 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7894 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7895 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7896 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7897 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7898 }
7899 //print "QQ".$amount."<br>\n";
7900
7901 // Now make replaceents (the main goal of function)
7902
7903 if ($thousand != ',' && $thousand != '.') {
7904 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7905
7906 // REGEX: Find the integral and decimal parts.
7907 //
7908 // We require that the decimal point only appears once in $amount.
7909 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7910 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7911 // It covers two cases:
7912 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7913 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7914 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7915 $matches = array();
7916 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7917 $intPart = $matches['int'];
7918 $decPart = $matches['dec'];
7919
7920 // Remove all commas and dots from intPart
7921 $intPart = str_replace(['.', ','], '', $intPart);
7922
7923 // Combine intPart and decPart with a dot
7924 $amount = $intPart . $dec . $decPart;
7925 }
7926 }
7927
7928 $amount = str_replace(' ', '', $amount); // To avoid spaces
7929 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7930 $amount = str_replace($dec, '.', $amount);
7931
7932 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7933 }
7934 //print ' XX'.$amount.' '.$rounding;
7935
7936 // Now, $amount is a real PHP float number. We make a rounding if required.
7937 if ($rounding) {
7938 $nbofdectoround = '';
7939 if ($rounding == 'MU') {
7940 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7941 } elseif ($rounding == 'MT') {
7942 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7943 } elseif ($rounding == 'MS') {
7944 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK', 5);
7945 } elseif ($rounding == 'CU') {
7946 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7947 } elseif ($rounding == 'CT') {
7948 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7949 } elseif (is_numeric($rounding)) {
7950 $nbofdectoround = (int) $rounding;
7951 }
7952
7953 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7954 if (dol_strlen($nbofdectoround)) {
7955 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7956 } else {
7957 return 'ErrorBadParameterProvidedToFunction';
7958 }
7959 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7960
7961 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7962 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7963 if (is_numeric($amount)) {
7964 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7965 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7966 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7967 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7968 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7969 }
7970 //print "TT".$amount.'<br>';
7971
7972 // Always make replace because each math function (like round) replace
7973 // with local values and we want a number that has a SQL string format x.y
7974 if ($thousand != ',' && $thousand != '.') {
7975 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7976 }
7977
7978 $amount = str_replace(' ', '', $amount); // To avoid spaces
7979 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7980 $amount = str_replace($dec, '.', $amount);
7981
7982 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7983 }
7984
7985 return $amount;
7986}
7987
8000function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
8001{
8002 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
8003
8004 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
8005 $dimension *= 1000000;
8006 $unit -= 6;
8007 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
8008 $dimension *= 1000;
8009 $unit -= 3;
8010 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
8011 $dimension /= 1000000;
8012 $unit += 6;
8013 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
8014 $dimension /= 1000;
8015 $unit += 3;
8016 }
8017 // Special case when we want output unit into pound or ounce
8018 /* TODO
8019 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
8020 {
8021 $dimension = // convert dimension from standard unit into ounce or pound
8022 $unit = $forceunitoutput;
8023 }
8024 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
8025 {
8026 $dimension = // convert dimension from standard unit into ounce or pound
8027 $unit = $forceunitoutput;
8028 }*/
8029
8030 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
8031 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
8032 $ret .= ' ' . measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
8033
8034 return $ret;
8035}
8036
8037
8050function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
8051{
8052 global $db, $conf, $mysoc;
8053
8054 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
8055 $thirdparty_seller = $mysoc;
8056 }
8057
8058 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);
8059
8060 $vatratecleaned = $vatrate;
8061 $reg = array();
8062 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
8063 $vatratecleaned = trim($reg[1]);
8064 $vatratecode = $reg[2];
8065 }
8066
8067 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
8068 {
8069 return 0;
8070 }*/
8071
8072 // Some test to guess with no need to make database access
8073 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
8074 if ($local == 1) {
8075 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
8076 return 0;
8077 }
8078 if ($thirdparty_seller->id == $mysoc->id) {
8079 if (!$thirdparty_buyer->localtax1_assuj) {
8080 return 0;
8081 }
8082 } else {
8083 if (!$thirdparty_seller->localtax1_assuj) {
8084 return 0;
8085 }
8086 }
8087 }
8088
8089 if ($local == 2) {
8090 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
8091 if (!$mysoc->localtax2_assuj) {
8092 return 0; // If main vat is 0, IRPF may be different than 0.
8093 }
8094 if ($thirdparty_seller->id == $mysoc->id) {
8095 if (!$thirdparty_buyer->localtax2_assuj) {
8096 return 0;
8097 }
8098 } else {
8099 if (!$thirdparty_seller->localtax2_assuj) {
8100 return 0;
8101 }
8102 }
8103 }
8104 } else {
8105 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
8106 return 0;
8107 }
8108 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
8109 return 0;
8110 }
8111 }
8112
8113 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
8114 if (in_array($mysoc->country_code, array('ES'))) {
8115 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
8116 }
8117
8118 // Search local taxes
8119 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
8120 if ($local == 1) {
8121 if ($thirdparty_seller != $mysoc) {
8122 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
8123 return $thirdparty_seller->localtax1_value;
8124 }
8125 } else { // i am the seller
8126 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
8127 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
8128 }
8129 }
8130 }
8131 if ($local == 2) {
8132 if ($thirdparty_seller != $mysoc) {
8133 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
8134 // TODO We should also return value defined on thirdparty only if defined
8135 return $thirdparty_seller->localtax2_value;
8136 }
8137 } else { // i am the seller
8138 if (in_array($mysoc->country_code, array('ES'))) {
8139 return $thirdparty_buyer->localtax2_value;
8140 } else {
8141 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
8142 }
8143 }
8144 }
8145 }
8146
8147 // By default, search value of local tax on line of common tax
8148 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
8149 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8150 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdparty_seller->country_code) . "'";
8151 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8152 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8153 if (!empty($vatratecode)) {
8154 $sql .= " AND t.code ='" . $db->escape($vatratecode) . "'"; // If we have the code, we use it in priority
8155 } else {
8156 $sql .= " AND t.recuperableonly = '" . $db->escape((string) $vatnpr) . "'";
8157 }
8158
8159 $resql = $db->query($sql);
8160
8161 if ($resql) {
8162 $obj = $db->fetch_object($resql);
8163 if ($obj) {
8164 if ($local == 1) {
8165 return $obj->localtax1;
8166 } elseif ($local == 2) {
8167 return $obj->localtax2;
8168 }
8169 }
8170 }
8171
8172 return 0;
8173}
8174
8175
8184function isOnlyOneLocalTax($local)
8185{
8186 $tax = get_localtax_by_third($local);
8187
8188 $valors = explode(":", $tax);
8189
8190 if (count($valors) > 1) {
8191 return false;
8192 } else {
8193 return true;
8194 }
8195}
8196
8203function get_localtax_by_third($local)
8204{
8205 global $db, $mysoc;
8206
8207 $sql = " SELECT t.localtax" . ((int) $local) . " as localtax";
8208 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t INNER JOIN " . MAIN_DB_PREFIX . "c_country as c ON c.rowid = t.fk_pays";
8209 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.active = 1 AND t.entity IN (" . getEntity('c_tva') . ") AND t.taux = (";
8210 $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";
8211 $sql .= " WHERE c.code = '" . $db->escape($mysoc->country_code) . "' AND t.entity IN (" . getEntity('c_tva') . ") AND tt.active = 1)";
8212 $sql .= " AND t.localtax" . ((int) $local) . "_type <> '0'";
8213 $sql .= " ORDER BY t.rowid DESC";
8214
8215 $resql = $db->query($sql);
8216 if ($resql) {
8217 $obj = $db->fetch_object($resql);
8218 if ($obj) {
8219 return $obj->localtax;
8220 } else {
8221 return '0';
8222 }
8223 }
8224
8225 return 'Error';
8226}
8227
8228
8240function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
8241{
8242 global $db;
8243
8244 dol_syslog("getTaxesFromId vat id or rate = " . $vatrate);
8245
8246 // Search local taxes
8247 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
8248 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
8249 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8250 if ($firstparamisid) {
8251 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8252 } else {
8253 $vatratecleaned = $vatrate;
8254 $vatratecode = '';
8255 $reg = array();
8256 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
8257 $vatratecleaned = $reg[1];
8258 $vatratecode = $reg[2];
8259 }
8260
8261 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8262 /*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 ??
8263 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
8264 $sql .= " WHERE t.fk_pays = c.rowid";
8265 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8266 $sql .= " AND c.code = '" . $db->escape($buyer->country_code) . "'";
8267 } else {
8268 $sql .= " AND c.code = '" . $db->escape($seller->country_code) . "'";
8269 }
8270 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8271 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8272 if ($vatratecode) {
8273 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8274 }
8275 }
8276
8277 $resql = $db->query($sql);
8278 if ($resql) {
8279 $obj = $db->fetch_object($resql);
8280 if ($obj) {
8281 return array(
8282 'rowid' => $obj->rowid,
8283 'code' => $obj->code,
8284 'rate' => $obj->rate,
8285 'localtax1' => $obj->localtax1,
8286 'localtax1_type' => $obj->localtax1_type,
8287 'localtax2' => $obj->localtax2,
8288 'localtax2_type' => $obj->localtax2_type,
8289 'npr' => $obj->npr,
8290 'accountancy_code_sell' => $obj->accountancy_code_sell,
8291 'accountancy_code_buy' => $obj->accountancy_code_buy
8292 );
8293 } else {
8294 return array();
8295 }
8296 } else {
8298 }
8299
8300 return array();
8301}
8302
8319function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
8320{
8321 global $db, $mysoc;
8322
8323 dol_syslog("getLocalTaxesFromRate vatrate=" . $vatrate . " local=" . $local);
8324
8325 // Search local taxes
8326 $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";
8327 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
8328 if ($firstparamisid) {
8329 $sql .= " WHERE t.rowid = " . (int) $vatrate;
8330 } else {
8331 $vatratecleaned = $vatrate;
8332 $vatratecode = '';
8333 $reg = array();
8334 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
8335 $vatratecleaned = $reg[1];
8336 $vatratecode = $reg[2];
8337 }
8338
8339 $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
8340 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
8341 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
8342 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'"; // local tax in spain use the buyer country ??
8343 } else {
8344 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
8345 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $db->escape($countrycodetouse) . "'";
8346 }
8347 $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
8348 if ($vatratecode) {
8349 $sql .= " AND t.code = '" . $db->escape($vatratecode) . "'";
8350 }
8351 }
8352
8353 $resql = $db->query($sql);
8354 if ($resql) {
8355 $obj = $db->fetch_object($resql);
8356
8357 if ($obj) {
8358 $vateratestring = $obj->rate . ($obj->code ? ' (' . $obj->code . ')' : '');
8359
8360 if ($local == 1) {
8361 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8362 } elseif ($local == 2) {
8363 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
8364 } else {
8365 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);
8366 }
8367 }
8368 }
8369
8370 return array();
8371}
8372
8383function get_product_vat_for_country($idprod, $thirdpartytouseforcountry, $idprodfournprice = 0)
8384{
8385 global $db, $mysoc;
8386
8387 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8388
8389 $ret = 0;
8390 $found = 0;
8391
8392 if ($idprod > 0) {
8393 // Load product
8394 $product = new Product($db);
8395 $product->fetch($idprod);
8396
8397 if (($mysoc->country_code == $thirdpartytouseforcountry->country_code)
8398 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouseforcountry->country_code, array('FR', 'MC')))
8399 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouseforcountry->country_code, array('MQ', 'GP')))
8400 ) {
8401 // If country of thirdparty to consider is ours
8402 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
8403 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
8404 if ($result > 0) {
8405 $ret = $product->vatrate_supplier;
8406 if ($product->default_vat_code_supplier) {
8407 $ret .= ' (' . $product->default_vat_code_supplier . ')';
8408 }
8409 $found = 1;
8410 }
8411 }
8412 if (!$found) {
8413 $ret = $product->tva_tx; // Default sales vat of product
8414 if ($product->default_vat_code) {
8415 $ret .= ' (' . $product->default_vat_code . ')';
8416 }
8417 $found = 1;
8418 }
8419 } else {
8420 // TODO Read default product vat according to product and an other countrycode.
8421 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8422 }
8423 }
8424
8425 if (!$found) {
8426 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8427 // 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).
8428 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
8429 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8430 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8431 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8432 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8433 $sql .= $db->plimit(1);
8434
8435 $resql = $db->query($sql);
8436 if ($resql) {
8437 $obj = $db->fetch_object($resql);
8438 if ($obj) {
8439 $ret = $obj->vat_rate;
8440 if ($obj->default_vat_code) {
8441 $ret .= ' (' . $obj->default_vat_code . ')';
8442 }
8443 }
8444 $db->free($resql);
8445 } else {
8447 }
8448 } else {
8449 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
8450 // '1.23'
8451 // or '1.23 (CODE)'
8452 $defaulttx = '';
8453 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8454 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8455 }
8456 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8457 $defaultcode = $reg[1];
8458 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8459 }*/
8460
8461 $ret = $defaulttx;
8462 }
8463 }
8464
8465 dol_syslog("get_product_vat_for_country: ret=" . $ret);
8466
8467 return $ret;
8468}
8469
8479function get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
8480{
8481 global $db, $mysoc;
8482
8483 if (!class_exists('Product')) {
8484 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8485 }
8486
8487 $ret = 0;
8488 $found = 0;
8489
8490 if ($idprod > 0) {
8491 // Load product
8492 $product = new Product($db);
8493 $result = $product->fetch($idprod);
8494
8495 if ($mysoc->country_code == $thirdpartytouseforcountry->country_code) { // If selling country is ours
8496 /* Not defined yet, so we don't use this
8497 if ($local==1) $ret=$product->localtax1_tx;
8498 elseif ($local==2) $ret=$product->localtax2_tx;
8499 $found=1;
8500 */
8501 } else {
8502 // TODO Read default product vat according to product and another countrycode.
8503 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
8504 }
8505 }
8506
8507 if (!$found) {
8508 // If vat of product for the country not found or not defined, we return higher vat of country.
8509 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
8510 $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
8511 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='" . $db->escape($thirdpartytouseforcountry->country_code) . "'";
8512 $sql .= " AND t.entity IN (" . getEntity('c_tva') . ")";
8513 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
8514 $sql .= $db->plimit(1);
8515
8516 $resql = $db->query($sql);
8517 if ($resql) {
8518 $obj = $db->fetch_object($resql);
8519 if ($obj) {
8520 if ($local == 1) {
8521 $ret = $obj->localtax1;
8522 } elseif ($local == 2) {
8523 $ret = $obj->localtax2;
8524 }
8525 }
8526 } else {
8528 }
8529 }
8530
8531 dol_syslog("get_product_localtax_for_country: ret=" . $ret);
8532 return $ret;
8533}
8534
8553function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8554{
8555 global $mysoc, $db, $hookmanager;
8556
8557 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8558
8559 // Note: possible values for tva_assuj are 0/1 or franchise/reel
8560 $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;
8561
8562 if (empty($thirdparty_seller->country_code)) {
8563 $thirdparty_seller->country_code = $mysoc->country_code;
8564 }
8565 $seller_country_code = $thirdparty_seller->country_code;
8566 $seller_in_cee = isInEEC($thirdparty_seller);
8567
8568 if (empty($thirdparty_buyer->country_code)) {
8569 $thirdparty_buyer->country_code = $mysoc->country_code;
8570 }
8571 $buyer_country_code = $thirdparty_buyer->country_code;
8572 $buyer_in_cee = isInEEC($thirdparty_buyer);
8573
8574 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'));
8575
8576 $vatvalue = 0;
8577 $vatrule = '';
8578
8579 // 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)
8580 // we use the buyer VAT.
8581 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
8582 if ($seller_in_cee && $buyer_in_cee) {
8583 $isacompany = $thirdparty_buyer->isACompany();
8584 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8585 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8586 if (!isValidVATID($thirdparty_buyer)) {
8587 $isacompany = 0;
8588 }
8589 }
8590
8591 if (!$isacompany) {
8592 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
8593 $vatrule = 'VATRULE 0';
8594 }
8595 }
8596 }
8597
8598 // If seller does not use VAT, default VAT is 0. End of rule.
8599 if (empty($vatrule) && !$seller_use_vat) {
8600 //print 'VATRULE 1';
8601 // TODO get the VAT Code of exemption asked into setup if country isInEEC (from an array list of possible
8602 // values like VATEX-EU-132-*, VATEX-FR-FRANCHISE, VATEX-EU-AE...
8603 // When we had recorded it, we also added a corresponding entry into table of vat code if it does not exists yet.
8604 // Here we test if entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-132-xx)'
8605 // If not, we add it and we return '0 (VATEX-EU-132-xx)'
8606 $vatvalue = 0;
8607 $vatrule = 'VATRULE 1';
8608 }
8609
8610 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
8611 if (empty($vatrule) && !empty($thirdparty_buyer->state_id)) {
8612 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
8613 $sql .= " FROM " . $db->prefix() . "c_tva as t";
8614 $sql .= " INNER JOIN " . $db->prefix() . "c_departements as d ON t.fk_department_buyer = d.rowid";
8615 $sql .= " WHERE d.rowid = " . ((int) $thirdparty_buyer->state_id);
8616 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
8617
8618 $res = $db->query($sql);
8619 if ($res) {
8620 if ($db->num_rows($res)) {
8621 $obj = $db->fetch_object($res);
8622
8623 $vatvalue = $obj->vat_default_rate . ' (' . $obj->vat_default_code . ')';
8624 $vatrule = 'VATRULE 2';
8625 }
8626 $db->free($res);
8627 }
8628 }
8629
8630 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
8631 if (empty($vatrule) && (
8632 ($seller_country_code == $buyer_country_code)
8633 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
8634 || (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.
8635 )) { // Warning ->country_code not always defined
8636 //print 'VATRULE 3';
8637 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8638
8639 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
8640 // Special case for india.
8641 //print 'VATRULE 3b';
8642 $reg = array();
8643 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
8644 // we must revert the C+S into I
8645 $tmpvat = str_replace("C+S", "I", $tmpvat);
8646 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8647 // we must revert the I into C+S
8648 $tmpvat = str_replace("I", "C+S", $tmpvat);
8649 }
8650 }
8651
8652 $vatvalue = $tmpvat;
8653 $vatrule = 'VATRULE 3b';
8654 }
8655
8656 // 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.
8657 // 'VATRULE 4' - Not supported
8658
8659 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8660 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8661 if (empty($vatrule) && ($seller_in_cee && $buyer_in_cee)) {
8662 $isacompany = $thirdparty_buyer->isACompany();
8663 if ($isacompany && !getDolGlobalString('MAIN_USE_VAT_ZERO_FOR_COMPANIES_IN_EEC_EVEN_IF_VAT_ID_UNKNOWN')) {
8664 require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
8665 if (!isValidVATID($thirdparty_buyer)) {
8666 $isacompany = 0;
8667 }
8668 }
8669
8670 if (!$isacompany) {
8671 //print 'VATRULE 5';
8672 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8673 $vatrule = 'VATRULE 5';
8674 } else {
8675 //print 'VATRULE 6';
8676 // TODO This is the case of VAT exemption 'VATEX-EU-IC'
8677 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-EU-IC)'
8678 // If not, we add it and we return '0 (VATEX-EU-IC)'
8679 $vatvalue = 0;
8680 $vatrule = 'VATRULE 6';
8681 }
8682 }
8683
8684 // 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
8685 // 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
8686 if (empty($vatrule) && getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8687 $isacompany = $thirdparty_buyer->isACompany();
8688 if (!$isacompany) {
8689 $vatvalue = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8690 $vatrule = 'VATRULE extra';
8691 //print 'VATRULE extra';
8692 }
8693 }
8694
8695 // Otherwise the VAT proposed by default=0. End of rule.
8696 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8697 //print 'VATRULE 7';
8698 // TODO This is the case of VAT exemption 'VATEX-EU-G'
8699 // If entry for the VAT exemption code exists in llx_vat, we can return '0 (VATEX-xxx)'
8700 // If not, we add it and we return '0 (VATEX-xxx)'
8701
8702 // Allow an external module to bypass the calculation of prices
8703 $parameters = array('vatvalue' => $vatvalue, 'vatrule' => $vatrule);
8704 $tmpobject = null;
8705 $tmpaction = '';
8706 // @phan-suppress-next-line PhanPluginConstantVariableNull
8707 $reshook = $hookmanager->executeHooks('get_default_tva', $parameters, $tmpobject, $tmpaction); // @phan-suppress-current-line PhanPluginConstantVariableNull
8708 if ($reshook > 0 && !empty($hookmanager->resArray['vatvalue'])) {
8709 $vatvalue = $hookmanager->resArray['vatvalue'];
8710 $vatrule = $hookmanager->resArray['vatrule']; // For information
8711 }
8712
8713 return $vatvalue;
8714}
8715
8716
8727function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8728{
8729 global $db;
8730
8731 if ($idprodfournprice > 0) {
8732 if (!class_exists('ProductFournisseur')) {
8733 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
8734 }
8735 $prodprice = new ProductFournisseur($db);
8736 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8737 return $prodprice->fourn_tva_npr;
8738 } elseif ($idprod > 0) {
8739 if (!class_exists('Product')) {
8740 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
8741 }
8742 $prod = new Product($db);
8743 $prod->fetch($idprod);
8744 return $prod->tva_npr;
8745 }
8746
8747 return 0;
8748}
8749
8763function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8764{
8765 global $mysoc;
8766
8767 if (!is_object($thirdparty_seller)) {
8768 return -1;
8769 }
8770 if (!is_object($thirdparty_buyer)) {
8771 return -1;
8772 }
8773
8774 if (empty($thirdparty_seller->country_code)) {
8775 $thirdparty_seller->country_code = $mysoc->country_code;
8776 }
8777 $seller_country_code = $thirdparty_seller->country_code;
8778 //$seller_in_cee = isInEEC($thirdparty_seller);
8779
8780 if (empty($thirdparty_buyer->country_code)) {
8781 $thirdparty_buyer->country_code = $mysoc->country_code;
8782 }
8783 $buyer_country_code = $thirdparty_buyer->country_code;
8784 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8785
8786 if ($local == 1) { // Localtax 1
8787 if ($mysoc->country_code == 'ES') {
8788 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8789 return 0;
8790 }
8791 } else {
8792 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8793 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8794 return 0;
8795 }
8796 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8797 return 0;
8798 }
8799 }
8800 } elseif ($local == 2) { //I Localtax 2
8801 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8802 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8803 return 0;
8804 }
8805 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8806 return 0;
8807 }
8808 }
8809
8810 if ($seller_country_code == $buyer_country_code) {
8811 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8812 }
8813
8814 return 0;
8815}
8816
8825function yn($yesno, $format = 1, $color = 0)
8826{
8827 global $langs;
8828
8829 $result = 'unknown';
8830 $classname = '';
8831 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'
8832 $result = $langs->trans('yes');
8833 if ($format == 1 || $format == 3) {
8834 $result = $langs->trans("Yes");
8835 }
8836 if ($format == 2) {
8837 $result = '<input type="checkbox" value="1" checked disabled>';
8838 }
8839 if ($format == 3) {
8840 $result = '<input type="checkbox" value="1" checked disabled> ' . $result;
8841 }
8842 if ($format == 4 || !is_numeric($format)) {
8843 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8844 }
8845
8846 $classname = 'ok';
8847 } else {
8848 $result = $langs->trans("no");
8849 if ($format == 1 || $format == 3) {
8850 $result = $langs->trans("No");
8851 }
8852 if ($format == 2) {
8853 $result = '<input type="checkbox" value="0" disabled>';
8854 }
8855 if ($format == 3) {
8856 $result = '<input type="checkbox" value="0" disabled> ' . $result;
8857 }
8858 if ($format == 4 || !is_numeric($format)) {
8859 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8860 }
8861
8862 if ($color == 2) {
8863 $classname = 'ok';
8864 } else {
8865 $classname = 'error';
8866 }
8867 }
8868 if ($color) {
8869 return '<span class="' . $classname . '">' . $result . '</span>';
8870 }
8871 return $result;
8872}
8873
8892function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8893{
8894 if (empty($modulepart) && is_object($object)) {
8895 if (!empty($object->module)) {
8896 $modulepart = $object->module;
8897 } elseif (!empty($object->element)) {
8898 $modulepart = $object->element;
8899 }
8900 }
8901
8902 $path = '';
8903
8904 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8905 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8906 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8907 $arrayforoldpath['product'] = 2;
8908 }
8909
8910 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8911 $level = $arrayforoldpath[$modulepart];
8912 }
8913 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8914 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8915 if (empty($num) && is_object($object)) {
8916 $num = ((int) $object->id);
8917 }
8918 if (empty($alpha)) {
8919 $num = preg_replace('/([^0-9])/i', '', $num);
8920 } else {
8921 $num = preg_replace('/^.*\-/i', '', $num);
8922 }
8923 $num = substr("000" . $num, -$level);
8924 if ($level == 1) {
8925 $path = substr($num, 0, 1);
8926 }
8927 if ($level == 2) {
8928 $path = substr($num, 1, 1) . '/' . substr($num, 0, 1);
8929 }
8930 if ($level == 3) {
8931 $path = substr($num, 2, 1) . '/' . substr($num, 1, 1) . '/' . substr($num, 0, 1);
8932 }
8933 } else {
8934 // We will enhance here a common way of forging path for document storage.
8935 // In a future, we may distribute directories on several levels depending on setup and object.
8936 // Here, $object->id, $object->ref and $modulepart are required.
8937 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceof Societe) {
8938 // 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
8939 $path = dol_sanitizeFileName((string) $object->id);
8940 } else {
8941 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? ((int) $object->id) : '') : $object->ref);
8942 }
8943 }
8944
8945 if (empty($withoutslash) && !empty($path)) {
8946 $path .= '/';
8947 }
8948
8949 return $path;
8950}
8951
8960function dol_mkdir($dir, $dataroot = '', $newmask = '')
8961{
8962 dol_syslog("functions.lib::dol_mkdir: dir=" . $dir, LOG_INFO);
8963
8964 $dir = dol_sanitizePathName($dir, '_', 0);
8965
8966 $dir_osencoded = dol_osencode($dir);
8967 if (@is_dir($dir_osencoded)) {
8968 return 0;
8969 }
8970
8971 $nberr = 0;
8972 $nbcreated = 0;
8973
8974 $ccdir = '';
8975 if (!empty($dataroot)) {
8976 // Remove data root from loop
8977 $dir = str_replace($dataroot . '/', '', $dir);
8978 $ccdir = $dataroot . '/';
8979 }
8980
8981 $cdir = explode("/", $dir);
8982 $num = count($cdir);
8983 for ($i = 0; $i < $num; $i++) {
8984 if ($i > 0) {
8985 $ccdir .= '/' . $cdir[$i];
8986 } else {
8987 $ccdir .= $cdir[$i];
8988 }
8989 $regs = array();
8990 if (preg_match("/^.:$/", $ccdir, $regs)) {
8991 continue; // If the Windows path is incomplete, continue with next directory
8992 }
8993
8994 // Attention, is_dir() can fail event if the directory exists
8995 // (i.e. according the open_basedir configuration)
8996 if ($ccdir) {
8997 $ccdir_osencoded = dol_osencode($ccdir);
8998 if (!@is_dir($ccdir_osencoded)) {
8999 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
9000
9001 umask(0);
9002 $dirmaskdec = octdec((string) $newmask);
9003 if (empty($newmask)) {
9004 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
9005 }
9006 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
9007 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
9008 // If the is_dir has returned a false information, we arrive here
9009 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '" . $ccdir . "' (no permission to write into parent or directory already exists).", LOG_WARNING);
9010 $nberr++;
9011 } else {
9012 dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' created", LOG_DEBUG);
9013 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
9014 $nbcreated++;
9015 }
9016 } else {
9017 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
9018 }
9019 }
9020 }
9021 return ($nberr ? -$nberr : $nbcreated);
9022}
9023
9024
9032function dolChmod($filepath, $newmask = '')
9033{
9034 if (!empty($newmask)) {
9035 @chmod($filepath, octdec($newmask));
9036 } elseif (getDolGlobalString('MAIN_UMASK')) {
9037 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
9038 }
9039}
9040
9041
9047function picto_required()
9048{
9049 return '<span class="fieldrequired">*</span>';
9050}
9051
9052
9069function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
9070{
9071 if (is_null($stringtoclean)) {
9072 return '';
9073 }
9074
9075 if ($removelinefeed == 2) {
9076 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
9077 }
9078 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
9079
9080 // 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)
9081 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9082
9083 $temp = str_replace('< ', '__ltspace__', $temp);
9084 $temp = str_replace('<:', '__lttwopoints__', $temp);
9085
9086 if ($strip_tags) {
9087 $temp = strip_tags($temp);
9088 } else {
9089 // 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).
9090 $pattern = "/<[^<>]+>/";
9091 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
9092 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
9093 // pass 2 - $temp after pass 2: 0000-021
9094 $tempbis = $temp;
9095 do {
9096 $temp = $tempbis;
9097 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
9098 $tempbis = preg_replace($pattern, '', $tempbis);
9099 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
9100 } while ($tempbis != $temp);
9101
9102 $temp = $tempbis;
9103
9104 // 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).
9105 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
9106 }
9107
9108 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
9109
9110 // Remove also carriage returns
9111 if ($removelinefeed == 1) {
9112 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
9113 }
9114
9115 // And double spaces
9116 if ($removedoublespaces) {
9117 while (strpos($temp, " ") !== false) {
9118 $temp = str_replace(" ", " ", $temp);
9119 }
9120 }
9121
9122 $temp = str_replace('__ltspace__', '< ', $temp);
9123 $temp = str_replace('__lttwopoints__', '<:', $temp);
9124
9125 return trim($temp);
9126}
9127
9147function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
9148{
9149 $sav_allowed_tags = $allowed_tags;
9150
9151 if (empty($allowed_tags) || (is_string($allowed_tags) && preg_match('/^common/', $allowed_tags))) {
9152 $allowed_tags = array(
9153 // HTML 4
9154 "html",
9155 "head",
9156 "body",
9157 "article",
9158 "a",
9159 "abbr",
9160 "b",
9161 "blockquote",
9162 "br",
9163 "cite",
9164 "div",
9165 "dl",
9166 "dd",
9167 "dt",
9168 "em",
9169 "font",
9170 "img",
9171 "ins",
9172 "hr",
9173 "i",
9174 "li",
9175 "ol",
9176 "p",
9177 "q",
9178 "s",
9179 "span",
9180 "strike",
9181 "strong",
9182 "title",
9183 "table",
9184 "tr",
9185 "th",
9186 "td",
9187 "u",
9188 "ul",
9189 "sup",
9190 "sub",
9191 "blockquote",
9192 "pre",
9193 "h1",
9194 "h2",
9195 "h3",
9196 "h4",
9197 "h5",
9198 "h6",
9199
9200 // HTML 5
9201 "footer",
9202 "header",
9203 "menu",
9204 "menuitem",
9205 "nav",
9206 "section"
9207 );
9208 }
9209 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
9210 if ($allowiframe) {
9211 if (!in_array('iframe', $allowed_tags)) {
9212 $allowed_tags[] = "iframe";
9213 }
9214 }
9215 if ($allowlink) {
9216 if (!in_array('link', $allowed_tags)) {
9217 $allowed_tags[] = "link";
9218 }
9219 if (!in_array('meta', $allowed_tags)) {
9220 $allowed_tags[] = "meta";
9221 }
9222 }
9223 if ($allowscript) {
9224 if (!in_array('script', $allowed_tags)) {
9225 $allowed_tags[] = "script";
9226 }
9227 }
9228 if ($allowstyle) {
9229 if (!in_array('style', $allowed_tags)) {
9230 $allowed_tags[] = "style";
9231 }
9232 }
9233 if (is_string($sav_allowed_tags)) {
9234 $tmptags = explode(',', $sav_allowed_tags);
9235 foreach ($tmptags as $tag) {
9236 if ($tag != 'common') {
9237 $allowed_tags[] = $tag;
9238 }
9239 }
9240 }
9241
9242
9243 $allowed_tags_string = implode("><", $allowed_tags);
9244 $allowed_tags_string = '<' . $allowed_tags_string . '>';
9245
9246 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
9247
9248 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
9249
9250 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
9251 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
9252
9253 if ($allowphp) {
9254 $allowed_tags[] = "commentphp";
9255 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
9256 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
9257 }
9258
9259 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
9260 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
9261
9262 // Remove all HTML tags
9263 $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
9264
9265 if ($cleanalsosomestyles) { // Clean for remaining html tags
9266 //$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
9267 $temp = preg_replace('/position\s*:\s*(absolute|fixed)/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
9268 $temp = preg_replace('/z-index\s*:/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
9269 }
9270 if ($removeclassattribute) { // Clean for remaining html tags
9271 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
9272 }
9273
9274 // Remove 'javascript:' that we should not find into a text
9275 // 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)).
9276 if ($cleanalsojavascript) {
9277 $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);
9278 }
9279
9280 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
9281
9282 if ($allowphp) {
9283 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
9284 }
9285
9286 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
9287
9288
9289 return $temp;
9290}
9291
9292
9305function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
9306{
9307 if (is_null($allowed_attributes)) {
9308 $allowed_attributes = array(
9309 // HTML 4
9310 "allow",
9311 "allowfullscreen",
9312 "alt",
9313 "async",
9314 "class",
9315 "contenteditable",
9316 "crossorigin",
9317 "data-html",
9318 "frameborder",
9319 "height",
9320 "href",
9321 "id",
9322 "name",
9323 "property",
9324 "rel",
9325 "src",
9326 "style",
9327 "target",
9328 "title",
9329 "type",
9330 "width",
9331
9332 // HTML5
9333 "footer",
9334 "header",
9335 "menu",
9336 "menuitem",
9337 "nav",
9338 "section"
9339 );
9340 }
9341 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
9342 if (!in_array("content", $allowed_attributes)) {
9343 $allowed_attributes[] = "content";
9344 }
9345 if (!in_array("http-equiv", $allowed_attributes)) {
9346 $allowed_attributes[] = "http-equiv";
9347 }
9348
9349 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
9350 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
9351 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . $stringtoclean . '</body></html>';
9352
9353 // Warning: loadHTML does not support HTML5 on old libxml versions.
9354 $dom = new DOMDocument('', 'UTF-8');
9355 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
9356 $savwarning = error_reporting();
9357 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
9358 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
9359 error_reporting($savwarning);
9360
9361 if ($dom instanceof DOMDocument) {
9362 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
9363 $el = $els->item($i);
9364 if (!$el instanceof DOMElement) {
9365 continue;
9366 }
9367 $attrs = $el->attributes;
9368 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
9369 //var_dump($attrs->item($ii));
9370 if (!empty($attrs->item($ii)->name)) {
9371 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
9372 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
9373 $els->item($i)->removeAttribute($attrs->item($ii)->name);
9374 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
9375 // If attribute is 'style'
9376 $valuetoclean = $attrs->item($ii)->value;
9377
9378 if (isset($valuetoclean)) {
9379 do {
9380 $oldvaluetoclean = $valuetoclean;
9381 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
9382 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
9383 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
9384 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
9385 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
9386 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
9387 }
9388
9389 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
9390 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
9391 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
9392 } while ($oldvaluetoclean != $valuetoclean);
9393 }
9394
9395 $attrs->item($ii)->value = $valuetoclean;
9396 }
9397 }
9398 }
9399 }
9400 }
9401
9402 $dom->encoding = 'UTF-8';
9403
9404 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
9405 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
9406
9407 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
9408 $return = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]*' . preg_quote('></head><body>', '/') . '/', '', $return);
9409 $return = preg_replace('/' . preg_quote('</body></html>', '/') . '$/', '', trim($return));
9410
9411 return trim($return);
9412 } else {
9413 return $stringtoclean;
9414 }
9415}
9416
9428function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
9429{
9430 $temp = $stringtoclean;
9431 foreach ($disallowed_tags as $tagtoremove) {
9432 $temp = preg_replace('/<\/?' . $tagtoremove . '>/', '', $temp);
9433 $temp = preg_replace('/<\/?' . $tagtoremove . '\s+[^>]*>/', '', $temp);
9434 }
9435
9436 if ($cleanalsosomestyles) {
9437 $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
9438 }
9439
9440 return $temp;
9441}
9442
9443
9453function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
9454{
9455 if ($nboflines == 1) {
9456 if (dol_textishtml($text)) {
9457 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
9458 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
9459 } else {
9460 if (isset($text)) {
9461 $firstline = preg_replace('/[\n\r].*/', '', $text);
9462 } else {
9463 $firstline = '';
9464 }
9465 }
9466 return $firstline . (isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
9467 } else {
9468 $ishtml = 0;
9469 if (dol_textishtml($text)) {
9470 $text = preg_replace('/\n/', '', $text);
9471 $ishtml = 1;
9472 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9473 } else {
9474 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9475 }
9476
9477 $text = strtr($text, $repTable);
9478 if ($charset == 'UTF-8') {
9479 $pattern = '/(<br[^>]*>)/Uu';
9480 } else {
9481 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9482 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9483 }
9484 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9485
9486 $firstline = '';
9487 $i = 0;
9488 $countline = 0;
9489 $lastaddediscontent = 1;
9490 while ($countline < $nboflines && isset($a[$i])) {
9491 if (preg_match('/<br[^>]*>/', $a[$i])) {
9492 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
9493 $firstline .= ($ishtml ? "<br>\n" : "\n");
9494 // Is it a br for a new line of after a printed line ?
9495 if (!$lastaddediscontent) {
9496 $countline++;
9497 }
9498 $lastaddediscontent = 0;
9499 }
9500 } else {
9501 $firstline .= $a[$i];
9502 $lastaddediscontent = 1;
9503 $countline++;
9504 }
9505 $i++;
9506 }
9507
9508 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
9509 //unset($a);
9510 $ret = $firstline . ($adddots ? '...' : '');
9511 //exit;
9512 return $ret;
9513 }
9514}
9515
9516
9528function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
9529{
9530 if (is_null($stringtoencode)) {
9531 return '';
9532 }
9533
9534 if (!$nl2brmode) {
9535 return nl2br($stringtoencode, $forxml);
9536 } else {
9537 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
9538 return $ret;
9539 }
9540}
9541
9551function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
9552{
9553 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
9554 // TODO using sandbox on inline html content is not possible yet with current browsers
9555 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
9556 //$s .= $stringtoencode;
9557 //$s .= '</body></html></iframe>';
9558 return $stringtoencode;
9559 } else {
9560 $out = $stringtoencode;
9561
9562 // First clean HTML content
9563 do {
9564 $oldstringtoclean = $out;
9565
9566 $outishtml = 0;
9567 if (dol_textishtml($out)) {
9568 $outishtml = 1;
9569 }
9570
9571 // HTML sanitizer by DOMDocument
9572 if (!empty($out) && getDolGlobalInt('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
9573 try {
9574 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
9575 if (LIBXML_VERSION < 20900) {
9576 // Avoid load of external entities (security problem).
9577 // Required only if LIBXML_VERSION < 20900
9578 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
9579 libxml_disable_entity_loader(true);
9580 }
9581
9582 $dom = new DOMDocument();
9583 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
9584 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
9585 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
9586 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
9587 // I don't know what the xml encoding is the trick for
9588 if ($outishtml) {
9589 //$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>';
9590 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">' . $out . '</div></body></html>';
9591 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
9592 } else {
9593 //$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>';
9594 $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>';
9595 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
9596 }
9597
9598 // Note: <a href="https://__[aaa]__/aaa.html"> is transformed into <a href="https://__[aaa]__/aaa.html">
9599 // We don't want that, so we protect __[xxx]__ by replacing [ and ] before loadHTML and restore them after saveHTML
9600 $out = preg_replace_callback(
9601 '/__\[([0-9a-zA-Z_]+)\]__/',
9606 function ($m) {
9607 return '__BRACKETSTART' . $m[1] . 'BRACKETEND__';
9608 },
9609 $out
9610 );
9611
9612 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
9613
9614 $dom->encoding = 'UTF-8';
9615
9616 // Add a layer to remove some styles
9617 if (getDolGlobalInt('MAIN_RESTRICTHTML_ONLY_VALID_HTML') == 2) {
9618 foreach ($dom->getElementsByTagName('*') as $el) {
9619 if (!$el instanceof DOMElement) {
9620 continue;
9621 }
9622
9623 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9624 if ($el->hasAttribute('style')) {
9625 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9626 $style = $el->getAttribute('style');
9627
9628 // delete some styles
9629 $style = preg_replace('/z-index\s*:/i', '', $style);
9630 $style = preg_replace('/position\s*:/i', '', $style);
9631 $style = preg_replace('/top\s*:/i', '', $style);
9632 $style = preg_replace('/left\s*:/i', '', $style);
9633 $style = preg_replace('/background\s*:/i', '', $style);
9634 /*
9635 $style = preg_replace('/width\s*:/i', '', $style);
9636 $style = preg_replace('/height\s*:/i', '', $style);
9637 $style = preg_replace('/backdrop-filter\s*:/i', '', $style);
9638 */
9639 if (trim($style) === '') {
9640 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9641 $el->removeAttribute('style');
9642 } else {
9643 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9644 $el->setAttribute('style', $style);
9645 }
9646 }
9647 }
9648 }
9649
9650 $out = trim($dom->saveHTML());
9651
9652 // Restore [ and ] that were protected before loadHTML
9653 $out = preg_replace_callback(
9654 '/__BRACKETSTART([0-9a-zA-Z_]+)BRACKETEND__/',
9659 function ($m) {
9660 return '__[' . $m[1] . ']__';
9661 },
9662 $out
9663 );
9664
9665 // Remove the trick added to solve pb with text in utf8 and text without parent tag
9666 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
9667 $out = preg_replace('/^' . preg_quote('<html><head><', '/') . '[^<>]+' . preg_quote('></head><body><div class="tricktoremove">', '/') . '/', '', $out);
9668 $out = preg_replace('/' . preg_quote('</div></body></html>', '/') . '$/', '', trim($out));
9669 //$out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
9670 //$out = preg_replace('/<\/div>$/', '', $out);
9671
9672 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
9673 $out = str_replace('<br>', '', $out);
9674 }
9675 } catch (Exception $e) {
9676 // If error, invalid HTML string with no way to clean it
9677 //print $e->getMessage();
9678 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9679 }
9680 }
9681
9682 // HTML sanitizer by Tidy
9683 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
9684 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
9685 if (!empty($out) && getDolGlobalInt('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
9686 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
9687 try {
9688 //var_dump($out);
9689
9690 // Try cleaning using tidy
9691 if (extension_loaded('tidy') && class_exists("tidy")) {
9692 //print "aaa".$out."\n";
9693
9694 // See options at https://tidy.sourceforge.net/docs/quickref.html
9695 $config = array(
9696 'clean' => false,
9697 // 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;
9698 'quote-marks' => false,
9699 'doctype' => 'strict',
9700 'show-body-only' => true,
9701 "indent-attributes" => false,
9702 "vertical-space" => false,
9703 //'ident' => false, // Not always supported
9704 "wrap" => 0,
9705 'preserve-entities' => true
9706 // HTML5 tags
9707 //'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',
9708 //'new-blocklevel-tags' => 'footer header section menu menuitem'
9709 //'new-empty-tags' => 'command embed keygen source track wbr',
9710 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
9711 );
9712
9713 // Tidy
9714 $tidy = new tidy();
9715 $out = $tidy->repairString($out, $config, 'utf8');
9716
9717 //print "xxx".$out;exit;
9718 }
9719
9720 //var_dump($out);
9721 } catch (Exception $e) {
9722 // If error, invalid HTML string with no way to clean it
9723 //print $e->getMessage();
9724 $out = 'InvalidHTMLStringCantBeCleaned ' . $e->getMessage();
9725 }
9726 }
9727
9728 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
9729 // TODO $out = preg_replace('/[\x{2000}-\x{200D}\x{FEFF}]/u', ' ', $out);
9730 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
9731
9732 // Clean some html entities that are useless so text is cleaner
9733 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
9734
9735 // Ckeditor uses the numeric entity for apostrophe, so we force it to
9736 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
9737 $out = preg_replace('/&#39;/i', '&apos;', $out);
9738
9739 // 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).
9740 // 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
9741 // using a non conventionnal way to be encoded, to not have them sanitized just after)
9742 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
9743 $out = preg_replace_callback(
9744 '/&#(x?[0-9][0-9a-f]+;?)/i',
9749 static function ($m) {
9750 return realCharForNumericEntities($m);
9751 },
9752 $out
9753 );
9754 }
9755
9756 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
9757 $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'.
9758
9759 // Keep only some html tags and remove also some 'javascript:' strings
9760 if ($check == 'restricthtmlallowlinkscript') {
9761 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
9762 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
9763 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
9764 } elseif ($check == 'restricthtmlallowiframe') {
9765 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
9766 } else {
9767 $out = dol_string_onlythesehtmltags($out, 0, 1, 1); // styles are allowed to allow rich text editor features of ckeditor managed by the "style=" attribute
9768 }
9769
9770 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
9771 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
9773 }
9774
9775 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
9776 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
9777 $out = preg_replace('/&apos;/i', "&#39;", $out);
9778
9779 // Now remove js
9780 // 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
9781 $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)>
9782 $out = preg_replace('/on(abort|after|animation|auxclick|before|blur|cancel|canplay|canplaythrough|change|click|close|command|contentvisibility|context|cuechange|copy|cut)[a-z]*\s*=/i', '', $out);
9783 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
9784 $out = preg_replace('/on(lost|offline|online|message|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
9785 $out = preg_replace('/on(paste|pause|play|playing|progress|ratechange|rejectionn|reset|resize|scroll|search|security|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', '', $out);
9786 $out = preg_replace('/on(timeupdate|toggle|unhandled|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
9787 // More not into the previous list
9788 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
9789 // Add also a generic removal of any onxxx= attribute
9790 $out = preg_replace('/\son[a-z]+\s*=/i', ' ', $out);
9791 } while ($oldstringtoclean != $out);
9792
9793 // Check the limit of external links that are automatically executed in a Rich text content. We count:
9794 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
9795 // 'url(' to avoid inline style like background: url(http...
9796 // '<link' to avoid <link href="http...">
9797 $reg = array();
9798 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
9799 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
9800 $nblinks = count($reg[0]);
9801 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
9802 $out = 'ErrorTooManyLinksIntoHTMLString';
9803 }
9804
9805 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
9806 if ($nblinks > 0) {
9807 $out = 'ErrorHTMLLinksNotAllowed';
9808 }
9809 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
9810 // Refuse any links except it they are to the wrapper document.php or viewimage.php
9811 $nblinks = 0;
9812
9813 // Loop on each url in src= and url(
9814 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
9815
9817
9818 $matches = array();
9819 if (preg_match_all($pattern, $out, $matches)) {
9820 // URLs are into $matches[1] or $matches[2]
9821 $urls = array();
9822 foreach ($matches[1] as $tmpval) {
9823 if (!empty($tmpval)) {
9824 $urls[] = $tmpval;
9825 }
9826 }
9827 foreach ($matches[2] as $tmpval) {
9828 if (!empty($tmpval)) {
9829 $urls[] = $tmpval;
9830 }
9831 }
9832
9833 // Show URLs
9834 $firstexturl = '';
9835 $secondexturl = '';
9836 foreach ($urls as $url) {
9837 $urlok = 0;
9838 $parsedurl = parse_url($url);
9839 if (!empty($parsedurl)) {
9840 if (preg_match('/'.preg_quote($dolibarr_main_url_root, '/').'/', $url)
9841 //&& preg_match('/(document|viewimage)\.php$/', $parsedurl['path']) && preg_match('/modulepart=(media|mycompany)/', $parsedurl['query'])
9842 ) {
9843 $urlok = 1;
9844 }
9845 }
9846 if (!$urlok) {
9847 $nblinks++;
9848 if (empty($firstexturl)) {
9849 $firstexturl = $url;
9850 } elseif (empty($secondexturl)) {
9851 $secondexturl = $url;
9852 }
9853 //echo "Found url = ".$url . "\n";
9854 }
9855 }
9856 if ($nblinks > 0) {
9857 $out = 'ErrorHTMLExternalLinksNotAllowed (Example: '.$firstexturl.($secondexturl ? ' '.$secondexturl : '').')';
9858 }
9859 }
9860 }
9861
9862 return $out;
9863 }
9864}
9865
9886function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9887{
9888 if (is_null($stringtoencode)) {
9889 return '';
9890 }
9891
9892 $newstring = $stringtoencode;
9893 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9894 $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.
9895 if ($removelasteolbr) {
9896 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9897 }
9898 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9899 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9900 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9901 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9902 } else {
9903 if ($removelasteolbr) {
9904 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9905 }
9906 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9907 }
9908 // Other substitutions that htmlentities does not do
9909 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9910 return $newstring;
9911}
9912
9920function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9921{
9922 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9923 $ret = preg_replace('/' . "\r\n" . '<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9924 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\r\n" . '/i', "\r\n", $ret);
9925 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\n" . '/i', "\n", $ret);
9926 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9927 return $ret;
9928}
9929
9936function dol_htmlcleanlastbr($stringtodecode)
9937{
9938 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9939 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|' . "\n" . '|' . "\r" . ')+$/i', "", $ret);
9940 return $ret;
9941}
9942
9952function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9953{
9954 $newstring = $a;
9955 if ($keepsomeentities) {
9956 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9957 }
9958 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9959 if ($keepsomeentities) {
9960 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9961 }
9962 return $newstring;
9963}
9964
9976function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9977{
9978 return htmlentities($string, $flags, $encoding, $double_encode);
9979}
9980
9992function dol_string_is_good_iso($s, $clean = 0)
9993{
9994 $len = dol_strlen($s);
9995 $out = '';
9996 $ok = 1;
9997 for ($scursor = 0; $scursor < $len; $scursor++) {
9998 $ordchar = ord($s[$scursor]);
9999 //print $scursor.'-'.$ordchar.'<br>';
10000 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
10001 $ok = 0;
10002 break;
10003 } elseif ($ordchar > 126 && $ordchar < 160) {
10004 $ok = 0;
10005 break;
10006 } elseif ($clean) {
10007 $out .= $s[$scursor];
10008 }
10009 }
10010 if ($clean) {
10011 return $out;
10012 }
10013 return $ok;
10014}
10015
10024function dol_nboflines($s, $maxchar = 0)
10025{
10026 if ($s == '') {
10027 return 0;
10028 }
10029 $arraystring = explode("\n", $s);
10030 $nb = count($arraystring);
10031
10032 return $nb;
10033}
10034
10035
10045function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
10046{
10047 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
10048 if (dol_textishtml($text)) {
10049 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
10050 }
10051
10052 $text = strtr($text, $repTable);
10053 if ($charset == 'UTF-8') {
10054 $pattern = '/(<br[^>]*>)/Uu';
10055 } else {
10056 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
10057 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
10058 }
10059 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
10060
10061 $nblines = (int) floor((count($a) + 1) / 2);
10062 // count possible auto line breaks
10063 if ($maxlinesize) {
10064 foreach ($a as $line) {
10065 if (dol_strlen($line) > $maxlinesize) {
10066 //$line_dec = html_entity_decode(strip_tags($line));
10067 $line_dec = html_entity_decode($line);
10068 if (dol_strlen($line_dec) > $maxlinesize) {
10069 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
10070 $nblines += substr_count($line_dec, '\n');
10071 }
10072 }
10073 }
10074 }
10075
10076 unset($a);
10077 return $nblines;
10078}
10079
10088function dol_textishtml($msg, $option = 0)
10089{
10090 if (is_null($msg)) {
10091 return false;
10092 }
10093
10094 if ($option == 1) {
10095 if (preg_match('/<(html|link|script)/i', $msg)) {
10096 return true;
10097 } elseif (preg_match('/<body/i', $msg)) {
10098 return true;
10099 } elseif (preg_match('/<\/textarea/i', $msg)) {
10100 return true;
10101 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
10102 return true;
10103 } elseif (preg_match('/<br/i', $msg)) {
10104 return true;
10105 }
10106 return false;
10107 } else {
10108 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
10109 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
10110 if (preg_match('/<(html|link|script|body)/i', $msg)) {
10111 return true;
10112 } elseif (preg_match('/<\/textarea/i', $msg)) {
10113 return true;
10114 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
10115 return true;
10116 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
10117 return true;
10118 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
10119 return true;
10120 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
10121 return true;
10122 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
10123 return true; // must accept <img src="http://example.com/aaa.png" />
10124 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
10125 return true; // must accept <a href="http://example.com/aaa.png" />
10126 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
10127 return true;
10128 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
10129 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
10130 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
10131 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
10132 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
10133 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
10134 return true; // Html entities numbers in hexa
10135 }
10136
10137 return false;
10138 }
10139}
10140
10155function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
10156{
10157 if (!empty($invert)) {
10158 $tmp = $text1;
10159 $text1 = $text2;
10160 $text2 = $tmp;
10161 }
10162
10163 $ret = '';
10164 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
10165 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
10166 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
10167 return $ret;
10168}
10169
10178function dol_concat($text1, $text2)
10179{
10180 return $text1.$text2;
10181}
10182
10190function safeArrayMap($callback, array $array)
10191{
10192 if (!is_string($callback)) {
10193 throw new InvalidArgumentException("Les callbacks sont désactivés.");
10194 }
10195 // Check that $callback is a sure function
10196 $allowed_callbacks = ['strtolower', 'strtoupper', 'intval'];
10197 if (!in_array($callback, $allowed_callbacks, true)) {
10198 throw new InvalidArgumentException("Callback function not allowed.");
10199 }
10200 return array_map($callback, $array);
10201}
10202
10203
10217function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
10218{
10219 global $db, $conf, $mysoc, $user, $extrafields;
10220
10221 $substitutionarray = array();
10222
10223 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
10224 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
10225 // this will include signature content first and then replace var found into content of signature
10226 //var_dump($onlykey);
10227 $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()
10228 $usersignature = $user->signature;
10229 $substitutionarray = array_merge($substitutionarray, array(
10230 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
10231 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
10232 ));
10233
10234 if (is_object($user) && ($user instanceof User)) {
10235 $substitutionarray = array_merge($substitutionarray, array(
10236 '__USER_ID__' => (string) $user->id,
10237 '__USER_LOGIN__' => (string) $user->login,
10238 '__USER_EMAIL__' => (string) $user->email,
10239 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
10240 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
10241 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
10242 '__USER_FAX__' => (string) $user->office_fax,
10243 '__USER_LASTNAME__' => (string) $user->lastname,
10244 '__USER_FIRSTNAME__' => (string) $user->firstname,
10245 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
10246 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
10247 '__USER_JOB__' => (string) $user->job,
10248 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
10249 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
10250 ));
10251 }
10252 }
10253 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
10254 $substitutionarray = array_merge($substitutionarray, array(
10255 '__MYCOMPANY_NAME__' => $mysoc->name,
10256 '__MYCOMPANY_EMAIL__' => $mysoc->email,
10257 '__MYCOMPANY_URL__' => $mysoc->url,
10258 '__MYCOMPANY_PHONE__' => dol_print_phone((string) $mysoc->phone, '', 0, 0, '', " ", '', '', -1),
10259 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone((string) $mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
10260 '__MYCOMPANY_FAX__' => dol_print_phone((string) $mysoc->fax, '', 0, 0, '', " ", '', '', -1),
10261 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
10262 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
10263 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
10264 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
10265 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
10266 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
10267 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
10268 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
10269 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
10270 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
10271 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
10272 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
10273 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
10274 '__MYCOMPANY_VATNUMBER__' => $mysoc->tva_intra,
10275 '__MYCOMPANY_ZIP__' => $mysoc->zip,
10276 '__MYCOMPANY_TOWN__' => $mysoc->town,
10277 '__MYCOMPANY_STATE__' => $mysoc->state,
10278 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
10279 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
10280 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
10281 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
10282 ));
10283 }
10284
10285 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
10286 if ($onlykey) {
10287 $substitutionarray['__ID__'] = '__ID__';
10288 $substitutionarray['__REF__'] = '__REF__';
10289 $substitutionarray['__NEWREF__'] = '__NEWREF__';
10290 $substitutionarray['__LABEL__'] = '__LABEL__';
10291 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
10292 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
10293 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
10294 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
10295 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
10296
10297 if (isModEnabled("societe")) { // Most objects are concerned
10298 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
10299 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
10300 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
10301 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
10302 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
10303 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
10304 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
10305 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
10306 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
10307 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
10308 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
10309 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
10310 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
10311 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
10312 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
10313 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
10314 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
10315 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
10316 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
10317 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
10318 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
10319 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
10320 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
10321 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
10322 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
10323 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
10324 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
10325 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
10326 }
10327 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
10328 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
10329 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
10330 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
10331 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
10332 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
10333 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
10334 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
10335 }
10336 // add substitution variables for ticket
10337 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
10338 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
10339 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
10340 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
10341 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
10342 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
10343 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
10344 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
10345 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
10346 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
10347 }
10348 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
10349 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
10350 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
10351 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
10352 }
10353 if (isModEnabled('holiday') && (!is_object($object) || $object->element == 'holiday') && (empty($exclude) || !in_array('holiday', $exclude)) && (empty($include) || in_array('holiday', $include))) {
10354 $substitutionarray['__HOLIDAY_ARRAY_PER_EMPLOYEE_FOR_PERIOD__'] = '__HOLIDAY_ARRAY_PER_EMPLOYEE_FOR_PERIOD__';
10355 }
10356 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
10357 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
10358 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
10359 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
10360 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
10361 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
10362 }
10363 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
10364 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
10365 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
10366 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
10367 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
10368 }
10369 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
10370 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
10371 }
10372 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
10373 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
10374 }
10375 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
10376 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
10377 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
10378 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
10379 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
10380 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
10381 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
10382
10383 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
10384 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
10385 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
10386 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
10387 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
10388 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_ORDER__'] = 'Direct download url of a supplier order';
10389 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_INVOICE__'] = 'Direct download url of a supplier invoice';
10390
10391 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
10392 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
10393 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
10394 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
10395 }
10396 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
10397 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
10398 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
10399 }
10400 } else {
10401 '@phan-var-force Adherent|Delivery $object';
10403 $substitutionarray['__ID__'] = $object->id;
10404 $substitutionarray['__REF__'] = $object->ref;
10405 $substitutionarray['__NEWREF__'] = $object->newref;
10406 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
10407 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10408 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10409 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
10410 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
10411
10412 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
10413 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
10414 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
10415
10416 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
10417 $date_delivery = null;
10418 if (property_exists($object, 'date_delivery')) {
10419 $date_delivery = $object->date_delivery;
10420 } elseif (property_exists($object, 'delivery_date')) {
10421 $date_delivery = $object->delivery_date;
10422 }
10423 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10424 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
10425 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
10426 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
10427 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
10428 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
10429 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
10430 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
10431 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
10432
10433 // For backward compatibility (deprecated)
10434 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
10435 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
10436
10437 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
10438 $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 : '')) : '');
10439 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
10440
10441 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
10442 '@phan-var-force Adherent $object';
10444 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
10445
10446 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
10447 if (method_exists($object, 'getCivilityLabel')) {
10448 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
10449 }
10450 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
10451 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
10452 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
10453 if (method_exists($object, 'getFullName')) {
10454 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
10455 }
10456 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
10457 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
10458 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
10459 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
10460 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
10461 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
10462 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
10463 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
10464 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
10465 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
10466 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
10467 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
10468 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
10469 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
10470 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
10471 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
10472
10473 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
10474 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
10475 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
10476 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
10477 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
10478 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
10479 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
10480 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
10481 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
10482 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
10483 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
10484 }
10485
10486 if (is_object($object) && $object->element == 'societe') {
10488 '@phan-var-force Societe $object';
10489 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
10490 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
10491 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
10492 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
10493 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
10494 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
10495 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
10496 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
10497 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
10498 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
10499 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
10500 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
10501 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
10502 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
10503 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
10504 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ?: '');
10505 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
10506 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
10507 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
10508 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
10509 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
10510 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
10511 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
10512 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
10513 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
10514 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
10515 } elseif (is_object($object) && is_object($object->thirdparty)) {
10516 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
10517 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
10518 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
10519 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
10520 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
10521 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
10522 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
10523 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
10524 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
10525 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
10526 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
10527 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
10528 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
10529 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ?: '');
10530 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
10531 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
10532 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
10533 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
10534 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
10535 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
10536 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
10537 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
10538 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
10539 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
10540 }
10541
10542 if (is_object($object) && $object->element == 'recruitmentcandidature') {
10543 '@phan-var-force RecruitmentCandidature $object';
10545 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
10546 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10547 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10548 }
10549 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
10550 '@phan-var-force ConferenceOrBoothAttendee $object';
10552 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
10553 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
10554 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
10555 }
10556
10557 if (is_object($object) && $object->element == 'project') {
10558 '@phan-var-force Project $object';
10560 $substitutionarray['__PROJECT_ID__'] = $object->id;
10561 $substitutionarray['__PROJECT_REF__'] = $object->ref;
10562 $substitutionarray['__PROJECT_NAME__'] = $object->title;
10563 } elseif (is_object($object)) {
10564 $project = null;
10565 if (!empty($object->project)) {
10566 $project = $object->project;
10567 }
10568 if (!is_null($project) && is_object($project)) {
10569 $substitutionarray['__PROJECT_ID__'] = $project->id;
10570 $substitutionarray['__PROJECT_REF__'] = $project->ref;
10571 $substitutionarray['__PROJECT_NAME__'] = $project->title;
10572 } else {
10573 // can substitute variables for project : uses lazy load in "make_substitutions" method
10574 $project_id = 0;
10575 if (!empty($object->fk_project) && $object->fk_project > 0) {
10576 $project_id = $object->fk_project;
10577 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
10578 $project_id = $object->fk_project;
10579 }
10580 if ($project_id > 0) {
10581 // path:class:method:id
10582 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10583 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10584 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
10585 }
10586 }
10587 }
10588
10589 if (is_object($object) && $object->element == 'facture') {
10590 '@phan-var-force Facture $object';
10592 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
10593 }
10594 if (is_object($object) && $object->element == 'shipping') {
10595 '@phan-var-force Expedition $object';
10597 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
10598 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
10599 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
10600 }
10601 if (is_object($object) && $object->element == 'reception') {
10602 '@phan-var-force Reception $object';
10604 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
10605 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
10606 }
10607
10608 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
10609 '@phan-var-force Contrat $object';
10611 $dateplannedstart = '';
10612 $datenextexpiration = '';
10613 foreach ($object->lines as $line) {
10614 if ($line->date_start > $dateplannedstart) {
10615 $dateplannedstart = $line->date_start;
10616 }
10617 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
10618 $datenextexpiration = $line->date_end;
10619 }
10620 }
10621 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
10622 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
10623 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
10624
10625 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
10626 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
10627 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
10628 }
10629 // add substitution variables for ticket
10630 if (is_object($object) && $object->element == 'ticket') {
10631 '@phan-var-force Ticket $object';
10633 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
10634 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
10635 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
10636 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
10637 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
10638 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
10639 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
10640 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
10641 $userstat = new User($db);
10642 if ($object->fk_user_assign > 0) {
10643 $userstat->fetch($object->fk_user_assign);
10644 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10645 }
10646
10647 if ($object->fk_user_create > 0) {
10648 $userstat->fetch($object->fk_user_create);
10649 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
10650 }
10651 }
10652
10653 // Create dynamic tags for __EXTRAFIELD_FIELD__
10654 if ($object->table_element && $object->id > 0) {
10655 if (!is_object($extrafields)) {
10656 $extrafields = new ExtraFields($db);
10657 }
10658 $extrafields->fetch_name_optionals_label($object->table_element, true);
10659
10660 if ($object->fetch_optionals() > 0) { // @FIXME: Remove this, the fetch should have been done already, by the caller of getCommonSubstitutionArray()
10661 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
10662 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
10663 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
10664 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_date($object->array_options['options_' . $key], 'day');
10665 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = dol_print_date($object->array_options['options_' . $key], 'day', 'tzserver', $outputlangs);
10666 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = dol_print_date($object->array_options['options_' . $key], 'dayrfc');
10667 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
10668 $datetime = $object->array_options['options_' . $key];
10669 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
10670 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
10671 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
10672 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
10673 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
10674 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = dol_print_phone($object->array_options['options_' . $key]);
10675 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
10676 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
10677 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATED__'] = price($object->array_options['options_' . $key]); // For compatibility
10678 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '_FORMATTED__'] = price($object->array_options['options_' . $key]);
10679 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'select') {
10680 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = !empty($object->array_options['options_' . $key]) ? $object->array_options['options_' . $key] : '';
10681 $val = $extrafields->attributes[$object->table_element]['param'][$key]['options'][$object->array_options['options_'.$key]] ?? $object->array_options['options_'.$key];
10682 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LABEL__'] = $val;
10683 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
10684 $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = !empty($object->array_options['options_' . $key]) ? $object->array_options['options_' . $key] : '';
10685 }
10686 }
10687 }
10688 }
10689 }
10690
10691 // Complete substitution array with the url to make online payment
10692 if (empty($substitutionarray['__REF__'])) {
10693 $paymenturl = '';
10694 } else {
10695 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
10696 require_once DOL_DOCUMENT_ROOT . '/core/lib/payments.lib.php';
10697 $outputlangs->loadLangs(array('paypal', 'other'));
10698
10699 $amounttouse = 0;
10700 $typeforonlinepayment = 'free';
10701 if (is_object($object) && $object->element == 'commande') {
10702 $typeforonlinepayment = 'order';
10703 }
10704 if (is_object($object) && $object->element == 'facture') {
10705 $typeforonlinepayment = 'invoice';
10706 }
10707 if (is_object($object) && $object->element == 'member') {
10708 $typeforonlinepayment = 'member';
10709 if (!empty($object->last_subscription_amount)) {
10710 $amounttouse = $object->last_subscription_amount;
10711 }
10712 }
10713 if (is_object($object) && $object->element == 'contrat') {
10714 $typeforonlinepayment = 'contract';
10715 }
10716 if (is_object($object) && $object->element == 'fichinter') {
10717 $typeforonlinepayment = 'ficheinter';
10718 }
10719
10720 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], (float) $amounttouse);
10721 $paymenturl = $url;
10722 }
10723
10724 if ($object->id > 0) {
10725 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
10726 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
10727
10728 // Show structured communication
10729 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
10730 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions_be.lib.php';
10731 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication((string) $object->ref, $object->type);
10732 }
10733
10734 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
10735 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10736 } else {
10737 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
10738 }
10739 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
10740 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
10741 } else {
10742 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
10743 }
10744 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
10745 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
10746 } else {
10747 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
10748 }
10749 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
10750 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
10751 } else {
10752 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
10753 }
10754 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
10755 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
10756 } else {
10757 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
10758 }
10759 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
10760 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
10761 } else {
10762 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
10763 }
10764 if (getDolGlobalString('SUPPLIER_ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'order_supplier') {
10765 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_ORDER__'] = $object->getLastMainDocLink($object->element);
10766 } else {
10767 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_ORDER__'] = '';
10768 }
10769 if (getDolGlobalString('SUPPLIER_INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'invoice_supplier') {
10770 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_INVOICE__'] = $object->getLastMainDocLink($object->element);
10771 } else {
10772 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_INVOICE__'] = '';
10773 }
10774
10775 if (is_object($object) && $object->element == 'propal') {
10776 '@phan-var-force Propal $object';
10778 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/comm/propal/card.php?id=" . $object->id;
10779 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10780 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', (string) $object->ref, 1, $object);
10781 }
10782 if (is_object($object) && $object->element == 'commande') {
10783 '@phan-var-force Commande $object';
10785 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT . "/commande/card.php?id=" . $object->id;
10786 }
10787 if (is_object($object) && $object->element == 'facture') {
10788 '@phan-var-force Facture $object';
10790 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT . "/compta/facture/card.php?id=" . $object->id;
10791 }
10792 if (is_object($object) && $object->element == 'contrat') {
10793 '@phan-var-force Contrat $object';
10795 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT . "/contrat/card.php?id=" . $object->id;
10796 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10797 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', (string) $object->ref, 1, $object);
10798 }
10799 if (is_object($object) && $object->element == 'fichinter') {
10800 '@phan-var-force Fichinter $object';
10802 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT . "/fichinter/card.php?id=" . $object->id;
10803 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10804 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', (string) $object->ref, 1, $object);
10805 }
10806 if (is_object($object) && $object->element == 'supplier_proposal') {
10807 '@phan-var-force SupplierProposal $object';
10809 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT . "/supplier_proposal/card.php?id=" . $object->id;
10810 }
10811 if (is_object($object) && $object->element == 'invoice_supplier') {
10812 '@phan-var-force FactureFournisseur $object';
10814 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT . "/fourn/facture/card.php?id=" . $object->id;
10815 }
10816 if (is_object($object) && $object->element == 'payment_supplier') {
10817 '@phan-var-force PaiementFourn $object';
10819 //print_r($object);
10820 $liste_factures = [];
10821 $total = 0;
10822
10823 // @FIXME We must not have any repeated SQL access into this function.
10824 $sql = 'SELECT f.ref,f.multicurrency_code as f_mccode, pf.*
10825 FROM '.MAIN_DB_PREFIX.'paiementfourn_facturefourn as pf
10826 JOIN '.MAIN_DB_PREFIX.'facture_fourn as f ON pf.fk_facturefourn = f.rowid
10827 WHERE pf.fk_paiementfourn = '.((int) $object->id);
10828
10829 $resql = $db->query($sql);
10830 if ($resql) {
10831 while ($objp = $db->fetch_object($resql)) {
10832 $liste_factures[] = ' - '.$outputlangs->trans('Invoice').' '. $objp->ref.' '.$outputlangs->trans('AmountPayed').' '.price($objp->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $objp->multicurrency_code);
10833 }
10834 }
10835 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_LIST__'] = implode("\n", $liste_factures);
10836 ;
10837 $substitutionarray['__SUPPLIER_PAYMENT_INVOICES_TOTAL__'] = price($object->multicurrency_amount, 0, $outputlangs, 0, -1, -1, $object->multicurrency_code ? $object->multicurrency_code : $conf->currency);
10838 }
10839 if (is_object($object) && $object->element == 'shipping') {
10840 '@phan-var-force Expedition $object';
10842 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT . "/expedition/card.php?id=" . $object->id;
10843 if (getDolGlobalInt('EXPEDITION_ALLOW_ONLINESIGN')) {
10844 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
10845 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'expedition', (string) $object->ref, 1, $object);
10846 }
10847 }
10848 }
10849
10850 if (is_object($object) && $object->element == 'action') {
10851 '@phan-var-force ActionComm $object';
10853 $substitutionarray['__EVENT_LABEL__'] = $object->label;
10854 $substitutionarray['__EVENT_DESCRIPTION__'] = $object->note;
10855 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action" . $object->type_code);
10856 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
10857 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
10858 $substitutionarray['__EVENT_DATE_TZUSER__'] = dol_print_date($object->datep, 'day', 'tzuserrel', $outputlangs);
10859 $substitutionarray['__EVENT_TIME_TZUSER__'] = dol_print_date($object->datep, 'hour', 'tzuserrel', $outputlangs);
10860 }
10861 }
10862 }
10863
10864 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
10865 '@phan-var-force Facture|FactureRec $object';
10867 include_once DOL_DOCUMENT_ROOT . '/core/lib/functionsnumtoword.lib.php';
10868
10869 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
10870 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
10871 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
10872 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
10873
10874 $already_payed_all = 0;
10875 if (is_object($object) && ($object instanceof Facture)) {
10876 $already_payed_all = $object->totalpaid + $object->totaldeposits + $object->totalcreditnotes;
10877 }
10878
10879 $substitutionarray['__SIMPLE_HTML_TABLE__'] = is_object($object) && !empty($object->lines) ? showSimpleHTMLTable($outputlangs, $object) : "";
10880 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
10881 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
10882 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
10883
10884 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
10885 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
10886 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
10887
10888 $substitutionarray['__DEPOSIT_PERCENT__'] = is_object($object) ? $object->deposit_percent : '';
10889 $substitutionarray['__DEPOSIT_AMOUNT__'] = is_object($object) ? price2num($object->total_ttc * ($object->deposit_percent / 100), 'MT') : '';
10890
10891 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
10892
10893 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10894 $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)) : '';
10895 $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)) : '';
10896
10897 $mysocuselocaltax1 = false;
10898 $mysocuselocaltax2 = false;
10899 if ($mysoc instanceof Societe && !empty($mysoc->country_code)) {
10900 $tmparray = $mysoc->useLocalTax(-1);
10901 $mysocuselocaltax1 = $tmparray[1];
10902 $mysocuselocaltax2 = $tmparray[2];
10903 }
10904
10905 // Local taxes
10906 if ($onlykey != 2 || $mysocuselocaltax1) {
10907 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
10908 }
10909 if ($onlykey != 2 || $mysocuselocaltax2) {
10910 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
10911 }
10912
10913 // Amount keys formatted in a currency
10914 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10915 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10916 $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) : '';
10917 $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)) : '';
10918 if ($onlykey != 2 || $mysocuselocaltax1) {
10919 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10920 }
10921 if ($onlykey != 2 || $mysocuselocaltax2) {
10922 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
10923 }
10924 // Amount keys formatted in a currency (with the typo error for backward compatibility)
10925 if ($onlykey != 2) {
10926 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
10927 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
10928 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
10929 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
10930 if ($mysocuselocaltax1) {
10931 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
10932 }
10933 if ($mysoc->useLocalTax2) {
10934 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
10935 }
10936 }
10937
10938 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
10939 $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) : '';
10940 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
10941 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
10942 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
10943 // TODO Add other keys for foreign multicurrency
10944
10945 // For backward compatibility
10946 if ($onlykey != 2) {
10947 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
10948 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
10949 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10950 }
10951 }
10952
10953
10954 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10955 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
10956
10957 $now = dol_now();
10958
10959 $tmp = dol_getdate($now, true);
10960 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10961 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10962 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10963 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10964
10965 $daytext = $outputlangs->trans('Day' . $tmp['wday']);
10966
10967 $substitutionarray = array_merge($substitutionarray, array(
10968 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10969 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10970 '__DAY__' => (string) $tmp['mday'],
10971 '__DAY_TEXT__' => $daytext, // Monday
10972 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10973 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10974 '__MONTH__' => (string) $tmp['mon'],
10975 '__MONTH_TEXT__' => $outputlangs->transnoentitiesnoconv('Month' . sprintf("%02d", $tmp['mon'])),
10976 '__MONTH_TEXT_SHORT__' => $outputlangs->transnoentitiesnoconv('MonthShort' . sprintf("%02d", $tmp['mon'])),
10977 '__MONTH_TEXT_MIN__' => $outputlangs->transnoentitiesnoconv('MonthVeryShort' . sprintf("%02d", $tmp['mon'])),
10978 '__YEAR__' => (string) $tmp['year'],
10979 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10980 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10981 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10982 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10983 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->transnoentitiesnoconv('Month' . sprintf("%02d", $tmp3['month'])),
10984 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->transnoentitiesnoconv('MonthShort' . sprintf("%02d", $tmp3['month'])),
10985 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->transnoentitiesnoconv('MonthVeryShort' . sprintf("%02d", $tmp3['month'])),
10986 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10987 '__NEXT_DAY__' => (string) $tmp4['day'],
10988 '__NEXT_MONTH__' => (string) $tmp5['month'],
10989 '__NEXT_MONTH_TEXT__' => $outputlangs->transnoentitiesnoconv('Month' . sprintf("%02d", $tmp5['month'])),
10990 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->transnoentitiesnoconv('MonthShort' . sprintf("%02d", $tmp5['month'])),
10991 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->transnoentitiesnoconv('MonthVeryShort' . sprintf("%02d", $tmp5['month'])),
10992 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10993 ));
10994 }
10995
10996 if (isModEnabled('multicompany')) {
10997 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10998 }
10999 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
11000 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
11001 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->transnoentitiesnoconv('TranslationOfKey');
11002 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->transnoentitiesnoconv('TranslationOfKey') . ' (load also language file before)';
11003 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->transnoentitiesnoconv('ValueOfConstantKey');
11004 }
11005
11006 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
11007
11008 return $substitutionarray;
11009}
11010
11027function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
11028{
11029 global $db, $langs;
11030
11031 if (!is_array($substitutionarray)) {
11032 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
11033 }
11034
11035 if (empty($outputlangs)) {
11036 $outputlangs = $langs;
11037 }
11038
11039 // Is initial text HTML or simple text ?
11040 $msgishtml = 0;
11041 if (dol_textishtml($text, 1)) {
11042 $msgishtml = 1;
11043 }
11044
11045 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
11046 if (is_object($outputlangs)) {
11047 $reg = array();
11048 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
11049 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
11050 $tmp = explode('|', $reg[1]);
11051 if (!empty($tmp[1])) {
11052 $outputlangs->load($tmp[1]);
11053 }
11054
11055 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
11056
11057 if (empty($converttextinhtmlifnecessary)) {
11058 // convert $newval into HTML is necessary
11059 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
11060 } else {
11061 if (! $msgishtml) {
11062 $valueishtml = dol_textishtml($value, 1);
11063 //var_dump("valueishtml=".$valueishtml);
11064
11065 if ($valueishtml) {
11066 $text = dol_htmlentitiesbr($text);
11067 $msgishtml = 1;
11068 }
11069 } else {
11070 $value = dol_nl2br((string) $value);
11071 }
11072
11073 $text = preg_replace('/__\‍(' . preg_quote($reg[1], '/') . '\‍)__/', $value, $text);
11074 }
11075 }
11076 }
11077
11078 // Make substitution for constant keys.
11079 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
11080 $reg = array();
11081 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
11082 $originalkeyfound = $reg[1];
11083 $keyfound = preg_replace('/\|urlencode$/', '', $originalkeyfound);
11084
11085 if (isASecretKey($keyfound)) {
11086 $value = '*****forbidden*****';
11087 } else {
11088 $value = getDolGlobalString($keyfound);
11089 // Execute some functions on value of substitution key
11090 if (preg_match('/\|urlencode$/', $originalkeyfound)) {
11091 $value = urlencode($value);
11092 }
11093 }
11094
11095 if (empty($converttextinhtmlifnecessary)) {
11096 // convert $newval into HTML is necessary
11097 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
11098 } else {
11099 if (! $msgishtml) {
11100 $valueishtml = dol_textishtml($value, 1);
11101
11102 if ($valueishtml) {
11103 $text = dol_htmlentitiesbr($text);
11104 $msgishtml = 1;
11105 }
11106 } else {
11107 $value = dol_nl2br((string) $value);
11108 }
11109
11110 $text = preg_replace('/__\[' . preg_quote($originalkeyfound, '/') . '\]__/', $value, $text);
11111 }
11112 }
11113
11114 // Make substitution for array $substitutionarray
11115 foreach ($substitutionarray as $key => $value) {
11116 if (!isset($value)) {
11117 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
11118 }
11119
11120 if (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN') && ($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__')) {
11121 $value = ''; // Protection
11122 }
11123
11124 if (empty($converttextinhtmlifnecessary)) {
11125 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
11126 } else {
11127 if (! $msgishtml) {
11128 $valueishtml = dol_textishtml($value, 1);
11129
11130 if ($valueishtml) {
11131 $text = dol_htmlentitiesbr($text);
11132 $msgishtml = 1;
11133 }
11134 } else {
11135 $value = dol_nl2br((string) $value);
11136 }
11137 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
11138 }
11139 }
11140
11141 /*
11142 Loop to scan $substitutionarray for couples: key=__XXX__@lazyload and value='path:class:method:id' or 'path:class:method:id:keyinarrayresult' if method return array
11143 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.
11144 If no, we don't need to make replacement, so we do nothing.
11145 If yes, we can make the substitution:
11146
11147 include_once $path;
11148 $tmpobj = new $class($db);
11149 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
11150 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
11151 */
11152 $memory_object_list = array();
11153 foreach ($substitutionarray as $key => $value) {
11154 $lazy_load_arr = array();
11155 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
11156 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
11157 $key_to_substitute = $lazy_load_arr[1];
11158 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
11159 $param_arr = explode(':', (string) $value);
11160 // path:class:method:id
11161 if (count($param_arr) >= 4) {
11162 $path = $param_arr[0];
11163 $class = $param_arr[1];
11164 $method = $param_arr[2];
11165 $id = (int) $param_arr[3];
11166 $keyinarrayresult = empty($param_arr[4]) ? '' : $param_arr[4];
11167
11168 // load class file and init object list in memory
11169 if (!isset($memory_object_list[$class])) {
11170 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
11171 require_once DOL_DOCUMENT_ROOT . $path;
11172 if (class_exists($class)) {
11173 $memory_object_list[$class] = array(
11174 'list' => array(),
11175 );
11176 }
11177 }
11178 }
11179
11180 // fetch object and set substitution
11181 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
11182 if (method_exists($class, $method)) {
11183 if (!isset($memory_object_list[$class]['list'][$id])) {
11184 $tmpobj = new $class($db);
11185 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11186 $tmpvaluetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
11187 $memory_object_list[$class]['list'][$id] = $tmpobj;
11188 } else {
11189 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
11190 $tmpobj = $memory_object_list[$class]['list'][$id];
11191 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11192 $tmpvaluetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
11193 }
11194
11195 if ($keyinarrayresult) {
11196 $valuetouseforsubstitution = (string) $tmpvaluetouseforsubstitution[$keyinarrayresult]; // Cast to string in case value is 123.5 for example
11197 } else {
11198 $valuetouseforsubstitution = (string) $tmpvaluetouseforsubstitution; // Cast to string in case value is 123.5 for example
11199 }
11200 $text = str_replace((string) $key_to_substitute, $valuetouseforsubstitution, $text);
11201 }
11202 }
11203 }
11204 }
11205 }
11206 }
11207 }
11208
11209 return $text;
11210}
11211
11224function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
11225{
11226 global $conf, $user;
11227
11228 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
11229
11230 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
11231
11232 // Check if there is external substitution to do, requested by plugins
11233 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
11234
11235 foreach ($dirsubstitutions as $reldir) {
11236 $dir = dol_buildpath($reldir, 0);
11237
11238 // Check if directory exists
11239 if (!dol_is_dir($dir)) {
11240 continue;
11241 }
11242
11243 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
11244 foreach ($substitfiles as $substitfile) {
11245 $reg = array();
11246 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
11247 $module = $reg[1];
11248
11249 dol_syslog("Library " . $substitfile['name'] . " found into " . $dir);
11250 // Include the user's functions file
11251 require_once $dir . $substitfile['name'];
11252 // Call the user's function, and only if it is defined
11253 $function_name = $module . "_" . $callfunc;
11254 if (function_exists($function_name)) {
11255 $function_name($substitutionarray, $outputlangs, $object, $parameters);
11256 }
11257 }
11258 }
11259 }
11260 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
11261 // to list all tags in odt template
11262 $tags = '';
11263 foreach ($substitutionarray as $key => $value) {
11264 $tags .= '{' . $key . '} => ' . $value . "\n";
11265 }
11266 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
11267 }
11268}
11269
11279function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
11280{
11281 print get_date_range($date_start, $date_end, $format, $outputlangs);
11282}
11283
11294function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
11295{
11296 global $langs;
11297
11298 $out = '';
11299
11300 if (!is_object($outputlangs)) {
11301 $outputlangs = $langs;
11302 }
11303
11304 if ($date_start && $date_end) {
11305 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11306 }
11307 if ($date_start && !$date_end) {
11308 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11309 }
11310 if (!$date_start && $date_end) {
11311 $out .= ($withparenthesis ? ($withparenthesis == 1 ? ' ' : '').'(' : '') . $outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
11312 }
11313
11314 return $out;
11315}
11316
11325function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
11326{
11327 $ret = '';
11328 // If order not defined, we use the setup
11329 if ($nameorder < 0) {
11330 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
11331 }
11332 if ($nameorder == 1) {
11333 $ret .= $firstname;
11334 if ($firstname && $lastname) {
11335 $ret .= ' ';
11336 }
11337 $ret .= $lastname;
11338 } elseif ($nameorder == 2 || $nameorder == 3) {
11339 $ret .= $firstname;
11340 if (empty($ret) && $nameorder == 3) {
11341 $ret .= $lastname;
11342 }
11343 } else { // 0, 4 or 5
11344 $ret .= $lastname;
11345 if (empty($ret) && $nameorder == 5) {
11346 $ret .= $firstname;
11347 }
11348 if ($nameorder == 0) {
11349 if ($firstname && $lastname) {
11350 $ret .= ' ';
11351 }
11352 $ret .= $firstname;
11353 }
11354 }
11355 return $ret;
11356}
11357
11358
11371function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
11372{
11373 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
11374 if (!is_array($mesgs)) {
11375 $mesgs = trim((string) $mesgs);
11376 // If mesgs is a not an empty string
11377 if ($mesgs) {
11378 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
11379 return;
11380 }
11381 if ($attop) {
11382 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11383 } else {
11384 $_SESSION['dol_events'][$style][] = $mesgs;
11385 }
11386 }
11387 } else {
11388 // If mesgs is an array
11389 foreach ($mesgs as $mesg) {
11390 $mesg = trim((string) $mesg);
11391 if ($mesg) {
11392 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
11393 return;
11394 }
11395 if ($attop) {
11396 array_unshift($_SESSION['dol_events'][$style], $mesgs);
11397 } else {
11398 $_SESSION['dol_events'][$style][] = $mesg;
11399 }
11400 }
11401 }
11402 }
11403}
11404
11418function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
11419{
11420 if (empty($mesg) && empty($mesgs)) {
11421 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
11422 } else {
11423 if ($messagekey) {
11424 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
11425 // TODO
11426 $mesg .= '';
11427 }
11428 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE" . $messagekey])) {
11429 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
11430 dol_print_error(null, 'Bad parameter style=' . $style . ' for setEventMessages');
11431 }
11432 if (empty($mesgs)) {
11433 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
11434 } else {
11435 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
11436 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
11437 }
11438 setEventMessage($mesgs, $style, $noduplicate, $attop);
11439 }
11440 }
11441 }
11442}
11443
11453function dol_htmloutput_events($disabledoutputofmessages = 0)
11454{
11455 // Show mesgs
11456 if (isset($_SESSION['dol_events']['mesgs'])) {
11457 if (empty($disabledoutputofmessages)) {
11458 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
11459 }
11460 unset($_SESSION['dol_events']['mesgs']);
11461 }
11462 // Show errors
11463 if (isset($_SESSION['dol_events']['errors'])) {
11464 if (empty($disabledoutputofmessages)) {
11465 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
11466 }
11467 unset($_SESSION['dol_events']['errors']);
11468 }
11469
11470 // Show warnings
11471 if (isset($_SESSION['dol_events']['warnings'])) {
11472 if (empty($disabledoutputofmessages)) {
11473 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
11474 }
11475 unset($_SESSION['dol_events']['warnings']);
11476 }
11477}
11478
11493function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
11494{
11495 global $conf, $langs;
11496
11497 $ret = 0;
11498 $return = '';
11499 $out = '';
11500 $divstart = $divend = '';
11501
11502 // If inline message with no format, we add it.
11503 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
11504 $divstart = '<div class="' . $style . ' clearboth">';
11505 $divend = '</div>';
11506 }
11507
11508 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
11509 $langs->load("errors");
11510 $out .= $divstart;
11511 if (is_array($mesgarray) && count($mesgarray)) {
11512 foreach ($mesgarray as $message) {
11513 $ret++;
11514 $out .= $langs->trans($message);
11515 if ($ret < count($mesgarray)) {
11516 $out .= "<br>\n";
11517 }
11518 }
11519 }
11520 if ($mesgstring) {
11521 $ret++;
11522 $out .= $langs->trans($mesgstring);
11523 }
11524 $out .= $divend;
11525 }
11526
11527 if ($out) {
11528 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
11529 $return = '<script nonce="' . getNonce() . '">
11530 $(document).ready(function() {
11531 /* jnotify(message, preset of message type, keepmessage) */
11532 $.jnotify("' . dol_escape_js($out) . '", "' . ($style == "ok" ? 3000 : $style) . '", ' . ($style == "ok" ? "false" : "true") . ',{ remove: function (){} } );
11533 });
11534 </script>';
11535 } else {
11536 $return = $out;
11537 }
11538 }
11539
11540 return $return;
11541}
11542
11554function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11555{
11556 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11557}
11558
11572function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
11573{
11574 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
11575 return;
11576 }
11577
11578 $iserror = 0;
11579 $iswarning = 0;
11580 if (is_array($mesgarray)) {
11581 foreach ($mesgarray as $val) {
11582 if ($val && preg_match('/class="error"/i', $val)) {
11583 $iserror++;
11584 break;
11585 }
11586 if ($val && preg_match('/class="warning"/i', $val)) {
11587 $iswarning++;
11588 break;
11589 }
11590 }
11591 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
11592 $iserror++;
11593 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
11594 $iswarning++;
11595 }
11596 if ($style == 'error' || $style == 'errors') {
11597 $iserror++;
11598 }
11599 if ($style == 'warning' || $style == 'warnings') {
11600 $iswarning++;
11601 }
11602
11603 if ($iserror || $iswarning) {
11604 // Remove div from texts
11605 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
11606 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
11607 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
11608 // Remove div from texts array
11609 if (is_array($mesgarray)) {
11610 $newmesgarray = array();
11611 foreach ($mesgarray as $val) {
11612 if (is_string($val)) {
11613 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
11614 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
11615 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
11616 $newmesgarray[] = $tmpmesgstring;
11617 } else {
11618 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
11619 }
11620 }
11621 $mesgarray = $newmesgarray;
11622 }
11623 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
11624 } else {
11625 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
11626 }
11627}
11628
11640function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
11641{
11642 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
11643}
11644
11666function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
11667{
11668 // Clean parameters
11669 $order = strtolower($order);
11670
11671 if (is_array($array)) {
11672 $sizearray = count($array);
11673 if ($sizearray > 0) {
11674 // Build a temp array with sorting key as value
11675 $temp = array();
11676 foreach (array_keys($array) as $key) {
11677 $tmpmultikey = explode(',', $index);
11678 $newindex = $tmpmultikey[0];
11679 if (is_object($array[$key])) {
11680 $temp[$key] = empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex;
11681 // Add other keys
11682 if (!empty($tmpmultikey[1])) {
11683 $newindex = $tmpmultikey[1];
11684 $temp[$key] .= '__' . (empty($array[$key]->$newindex) ? 0 : $array[$key]->$newindex);
11685 }
11686 } else {
11687 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11688 $temp[$key] = empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex];
11689 // Add other keys
11690 if (!empty($tmpmultikey[1])) {
11691 $newindex = $tmpmultikey[1];
11692 // @phan-suppress-next-line PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
11693 $temp[$key] .= '__' . (empty($array[$key][$newindex]) ? 0 : $array[$key][$newindex]);
11694 }
11695 }
11696 if ($natsort == -1) {
11697 $temp[$key] = '___' . $temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
11698 }
11699 }
11700 if (empty($natsort) || $natsort == -1) {
11701 if ($order == 'asc') {
11702 asort($temp);
11703 } else {
11704 arsort($temp);
11705 }
11706 } else {
11707 if ($case_sensitive) {
11708 natsort($temp);
11709 } else {
11710 natcasesort($temp); // natecasesort is not sensible to case
11711 }
11712 if ($order != 'asc') {
11713 $temp = array_reverse($temp, true);
11714 }
11715 }
11716
11717 $sorted = array();
11718
11719 foreach (array_keys($temp) as $key) {
11720 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
11721 }
11722
11723 return $sorted;
11724 }
11725 }
11726 return $array;
11727}
11728
11729
11737function utf8_check($str)
11738{
11739 $str = (string) $str; // Sometimes string is an int.
11740
11741 // We must use here a binary strlen function (so not dol_strlen)
11742 $strLength = strlen($str);
11743 for ($i = 0; $i < $strLength; $i++) {
11744 if (ord($str[$i]) < 0x80) {
11745 continue; // 0bbbbbbb
11746 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
11747 $n = 1; // 110bbbbb
11748 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
11749 $n = 2; // 1110bbbb
11750 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
11751 $n = 3; // 11110bbb
11752 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
11753 $n = 4; // 111110bb
11754 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
11755 $n = 5; // 1111110b
11756 } else {
11757 return false; // Does not match any model
11758 }
11759 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
11760 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
11761 return false;
11762 }
11763 }
11764 }
11765 return true;
11766}
11767
11775function utf8_valid($str)
11776{
11777 /* 2 other methods to test if string is utf8
11778 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
11779 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
11780 */
11781 return preg_match('//u', $str) ? true : false;
11782}
11783
11784
11791function ascii_check($str)
11792{
11793 if (function_exists('mb_check_encoding')) {
11794 //if (mb_detect_encoding($str, 'ASCII', true) return false;
11795 if (!mb_check_encoding($str, 'ASCII')) {
11796 return false;
11797 }
11798 } else {
11799 if (preg_match('/[^\x00-\x7f]/', $str)) {
11800 return false; // Contains a byte > 7f
11801 }
11802 }
11803
11804 return true;
11805}
11806
11807
11815function dol_osencode($str)
11816{
11817 $tmp = ini_get("unicode.filesystem_encoding");
11818 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
11819 $tmp = 'iso-8859-1'; // By default for windows
11820 }
11821 if (empty($tmp)) {
11822 $tmp = 'utf-8'; // By default for other
11823 }
11824 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
11825 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
11826 }
11827
11828 if ($tmp == 'iso-8859-1') {
11829 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
11830 }
11831 return $str;
11832}
11833
11834
11850function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
11851{
11852 global $conf;
11853
11854 // If key empty
11855 if ($key == '') {
11856 return 0;
11857 }
11858
11859 // Check in cache
11860 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
11861 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
11862 }
11863
11864 dol_syslog('dol_getIdFromCode (value for field ' . $fieldid . ' from key ' . $key . ' not found into cache)', LOG_DEBUG);
11865
11866 $sql = "SELECT " . $db->sanitize($fieldid) . " as valuetoget";
11867 $sql .= " FROM " . MAIN_DB_PREFIX . $db->sanitize($tablename);
11868 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
11869 $sql .= " WHERE " . $db->sanitize($fieldkey) . " = " . ((int) $key);
11870 } else {
11871 $sql .= " WHERE " . $db->sanitize($fieldkey) . " = '" . $db->escape($key) . "'";
11872 }
11873 if (!empty($entityfilter)) {
11874 $sql .= " AND entity IN (" . getEntity($tablename) . ")";
11875 }
11876 if ($filters) {
11877 $sql .= $filters; // @phan-suppress-current-line SqlInjection
11878 }
11879
11880 $resql = $db->query($sql);
11881 if ($resql) {
11882 $obj = $db->fetch_object($resql);
11883 $valuetoget = '';
11884 if ($obj) {
11885 $valuetoget = $obj->valuetoget;
11886 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
11887 } else {
11888 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
11889 }
11890 $db->free($resql);
11891
11892 return $valuetoget;
11893 } else {
11894 return -1;
11895 }
11896}
11897
11907function isStringVarMatching($var, $regextext, $matchrule = 1)
11908{
11909 // Tolerate callers (custom modules, older code) that already pass a full regex with delimiters
11910 // like '/^(aaa|bbb)/' instead of the bare body. Without this, the function would build
11911 // '/^/^(aaa|bbb)//' which trips preg_match() with 'Unknown modifier ^'.
11912 $regextext = preg_replace('#^/\^?#', '', (string) $regextext);
11913 $regextext = preg_replace('#\$?/[imsxuADSUXJ]*$#', '', $regextext);
11914
11915 if ($matchrule == 1) {
11916 if ($var == 'mainmenu') {
11917 global $mainmenu;
11918 return (preg_match('/^' . $regextext . '/', $mainmenu));
11919 } elseif ($var == 'leftmenu') {
11920 global $leftmenu;
11921 return (preg_match('/^' . $regextext . '/', $leftmenu));
11922 } else {
11923 return 'This variable is not accessible with dol_eval';
11924 }
11925 } else {
11926 return 'This value '.$matchrule.' for param $matchrule is not yet implemented';
11927 }
11928}
11929
11930
11940function verifCond($strToEvaluate, $onlysimplestring = '1')
11941{
11942 //print $strToEvaluate."<br>\n";
11943 $rights = true;
11944 if (isset($strToEvaluate) && $strToEvaluate !== '') {
11945 //var_dump($strToEvaluate);
11946 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
11947 $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
11948
11949 // On string syntax error, dol_eval may return a string that start with 'Bad call of ...' or 'Bad string syntax to evaluate...' !!!
11950 //var_dump($strToEvaluate, $rep);
11951 $rights = (bool) $rep && (!is_string($rep) || (strpos($rep, 'Exception during') === false && strpos($rep, 'Bad call of') === false && strpos($rep, 'Bad string syntax to evaluate') === false));
11952 //var_dump($rights);
11953 }
11954 return $rights;
11955}
11956
11971function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11972{
11973 if ($returnvalue != 1) {
11974 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
11975 }
11976
11977 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
11978 return dol_eval_new($s);
11979 } else {
11980 return dol_eval_standard($s, $hideerrors, $onlysimplestring);
11981 }
11982}
11983
11994function dol_eval_new($s)
11995{
11996 // Only this global variables can be read by eval function and returned to caller
11997 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11998 $db, $langs, $user, $website, $websitepage,
11999 $action, $mainmenu, $leftmenu,
12000 $mysoc,
12001 $objectoffield, // To allow the use of $objectoffield in computed fields
12002
12003 // Old variables used
12004 $object;
12005
12006 if (getDolGlobalString('MAIN_ALLOW_OLD_VAR_OBJ_IN_DOL_EVAL')) {
12007 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
12008 }
12009
12010 // PHP < 7.4.0
12011 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
12012 defined('T_FN') || define('T_FN', PHP_INT_MAX);
12013
12014 // PHP < 8.0.0
12015 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
12016 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
12017 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
12018 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
12019 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
12020
12021 // PHP < 8.1.0
12022 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
12023 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
12024 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
12025 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
12026
12027 // PHP < 8.4.0
12028 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
12029 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
12030 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
12031
12032 $prohibited_token_ids = [
12033 /*
12034 * Prohibited int tokens
12035 */
12036
12037 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
12038 'T_ABSTRACT',
12039 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
12040 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
12041 'T_ATTRIBUTE',
12042 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
12043 'T_BAD_CHARACTER',
12044 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
12045 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
12046 'T_CALLABLE',
12047 'T_CATCH',
12048 'T_CLASS',
12049 'T_CLOSE_TAG',
12050 'T_CONST',
12051 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
12052 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
12053 'T_DECLARE',
12054 'T_DIR',
12055 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
12056 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
12057 'T_ECHO',
12058 'T_ENDDECLARE',
12059 'T_ENUM',
12060 'T_EVAL',
12061 'T_EXIT',
12062 'T_EXTENDS',
12063 // 'T_FOR', 'T_FOREACH',
12064 'T_FILE',
12065 'T_FINAL',
12066 'T_FINALLY',
12067 'T_FN',
12068 'T_FUNCTION',
12069 'T_FUNC_C',
12070 'T_GLOBAL',
12071 'T_GOTO',
12072 'T_HALT_COMPILER',
12073 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
12074 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
12075 'T_IMPLEMENTS',
12076 'T_INCLUDE',
12077 'T_INCLUDE_ONCE',
12078 'T_INSTEADOF',
12079 'T_INTERFACE',
12080 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
12081 'T_LINE',
12082 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
12083 'T_METHOD_C',
12084 // 'T_NEW',
12085 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
12086 'T_NAMESPACE',
12087 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
12088 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
12089 'T_OPEN_TAG',
12090 'T_OPEN_TAG_WITH_ECHO',
12091 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
12092 'T_PRINT',
12093 'T_PRIVATE',
12094 'T_PROTECTED',
12095 'T_PUBLIC',
12096 // 'T_PROPERTY_C',
12097 'T_READONLY',
12098 'T_REQUIRE',
12099 'T_REQUIRE_ONCE',
12100 'T_RETURN',
12101 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
12102 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
12103 'T_STATIC',
12104 'T_THROW',
12105 'T_TRAIT',
12106 'T_TRAIT_C',
12107 'T_TRY',
12108 'T_UNSET',
12109 'T_UNSET_CAST',
12110 'T_USE',
12111 // 'T_VARIABLE',
12112 'T_VAR',
12113 // 'T_WHILE', 'T_WHITESPACE',
12114 // 'T_XOR_EQUAL',
12115 // 'T_YIELD', 'T_YIELD_FROM',
12116
12117 /*
12118 * Prohibited string tokens
12119 */
12120 ';',
12121 '`',
12122 ];
12123
12124 $prohibited_variables = [
12125 '$_COOKIE',
12126 '$_ENV',
12127 '$_FILES',
12128 '$GLOBALS',
12129 '$_GET',
12130 '$_POST',
12131 '$_REQUEST',
12132 '$_SERVER',
12133 '$_SESSION',
12134 ];
12135
12136 $prohibited_functions = [
12137 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
12138 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
12139 'override_function',
12140 'session_id',
12141 'session_create_id',
12142 'session_regenerate_id',
12143 'call_user_func',
12144 'call_user_func_array', // PREVENT calling forbidden functions
12145 'exec',
12146 'passthru',
12147 'shell_exec',
12148 'system',
12149 'proc_open',
12150 'popen',
12151 'dol_eval',
12152 'dol_eval_new',
12153 'dol_eval_standard',
12154 'dol_contctdesc',
12155 'executeCLI',
12156 'verifCond',
12157 'GETPOST', // Native Dolibarr functions
12158 'create_function',
12159 'assert',
12160 'mb_ereg_replace',
12161 'mb_eregi_replace', // function with eval capabilities
12162 'dol_compress_dir',
12163 'dol_decode',
12164 'dol_delete_file',
12165 'dol_delete_dir',
12166 'dol_delete_dir_recursive',
12167 'dol_copy',
12168 'archiveOrBackupFile', // more dolibarr functions
12169 'fopen',
12170 'file_put_contents',
12171 'fputs',
12172 'fputscsv',
12173 'fwrite',
12174 'fpassthru',
12175 'mkdir',
12176 'rmdir',
12177 'symlink',
12178 'touch',
12179 'unlink',
12180 'umask', // PHP functions related to file operations
12181 'invoke',
12182 'invokeArgs', // Method of ReflectionFunction to execute a function
12183 'filter_input',
12184 'filter_input_array',
12185 'GETPOST', // PREVENT CODE INJECTION
12186 ];
12187
12188 $prohibited_token_arrangements = [
12189 // Variable functions "$a(", '"$a"(', "'FN_NAME'(", ('FN_NAME')()
12190 ' T_VARIABLE ( ',
12191 ' " ( ',
12192 ' \' ( ',
12193 ' T_CONSTANT_ENCAPSED_STRING ( ',
12194 ' ) ( ',
12195 ];
12196
12197 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
12198
12199 $tokens_arrangement = ' ';
12200
12201 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
12202 if (is_array($tokens[$i])) {
12203 $token_id = $tokens[$i][0];
12204 $token_value = $tokens[$i][1];
12205 $token_name = token_name($tokens[$i][0]);
12206 } else {
12207 $token_id = $tokens[$i];
12208 $token_value = $tokens[$i];
12209 $token_name = $tokens[$i];
12210 }
12211
12212 // Ignore whitespaces
12213 if (T_WHITESPACE === $token_id) {
12214 continue;
12215 }
12216
12217 // Keep history to check arrangements
12218 $tokens_arrangement .= "{$token_name} ";
12219
12220 // Prohibited Variables
12221 if (
12222 T_VARIABLE === $token_id
12223 && in_array($token_value, $prohibited_variables, true)
12224 ) {
12225 return "« {$token_value} » is prohibited in « {$s} »";
12226 }
12227
12228 // Prohibited Functions
12229 if (
12230 T_STRING === $token_id
12231 && in_array($token_value, $prohibited_functions, true)
12232 ) {
12233 return "« {$token_value} » is prohibited in « {$s} »";
12234 }
12235 }
12236
12237 // Prohibited Token IDs
12238 $maxi = count($prohibited_token_ids);
12239 for ($i = 0; $i < $maxi; ++$i) {
12240 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
12241 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
12242 }
12243 }
12244
12245 // Prohibited token arrangements
12246 $maxi = count($prohibited_token_arrangements);
12247 for ($i = 0; $i < $maxi; ++$i) {
12248 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
12249 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
12250 }
12251 }
12252
12253 // Return result
12254 try {
12255 return @eval("return {$s};") ?? '';
12256 } catch (Throwable $ex) {
12257 return "Exception during evaluation: " . $s . " - " . $ex->getMessage();
12258 }
12259}
12260
12275function dol_eval_standard($s, $hideerrors = 1, $onlysimplestring = '1')
12276{
12277 // Only this global variables can be read by eval function and returned to caller
12278 // The less we have, the better it is.
12279
12280 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()
12281 global $db, $langs, $user, $website, $websitepage;
12282 global $action, $mainmenu, $leftmenu;
12283 global $mysoc;
12284 global $objectoffield; // To allow the use of $objectoffield in computed fields
12285 global $object;
12286
12287 // Old variables (deprecated since v23)
12288 if (getDolGlobalString('MAIN_ALLOW_OLD_VAR_OBJ_IN_DOL_EVAL')) {
12289 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $objectoffield
12290 }
12291
12292 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
12293 if ($onlysimplestring == '0') { // '0' is deprecated, we process it as the more secured '1'
12294 $onlysimplestring = '1';
12295 }
12296 if (!in_array($onlysimplestring, array('1', '2'))) {
12297 return "Bad call of dol_eval. Parameter onlysimplestring must be '1' or '2'.";
12298 }
12299 if (!is_scalar($s)) {
12300 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
12301 }
12302
12303 try {
12304 global $dolibarr_main_restrict_eval_methods;
12305
12306 // Set $dolibarr_main_restrict_eval_methods_array
12307 if (!isset($dolibarr_main_restrict_eval_methods)) {
12308 $dolibarr_main_restrict_eval_methods = 'getDolGlobalString, getDolGlobalInt, getDolCurrency, getDolEntity, getDolDBType, fetchNoCompute, hasRight, isAdmin, isExternalUser, isModEnabled, isStringVarMatching, abs, min, max, round, dol_now, preg_match';
12309 }
12310 //print '$dolibarr_main_restrict_eval_methods = '.$dolibarr_main_restrict_eval_methods."\n";
12311 $dolibarr_main_restrict_eval_methods_array = explode(',', str_replace(" ", "", $dolibarr_main_restrict_eval_methods));
12312
12313 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
12314 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
12315 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$objectoffield->canvas == "patient@cabinetmed"'
12316 // 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"
12317
12318 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
12319 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
12320 if ($onlysimplestring == '2') {
12321 $specialcharsallowed .= '<[]'; // Later we check that < has space before and after
12322 }
12323 global $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12324 if (!empty($dolibarr_main_allow_unsecured_special_chars_in_dol_eval)) {
12325 $specialcharsallowed .= (string) $dolibarr_main_allow_unsecured_special_chars_in_dol_eval;
12326 }
12327 if (preg_match('/[^a-z0-9\s' . preg_quote($specialcharsallowed, '/') . ']/i', $s)) {
12328 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): ' . $s;
12329 }
12330
12331 // Check if we found a | without a space before and after
12332 /* Disabled to allow preg_match('/(AAA|BBB)/')
12333 $tmps = str_replace(' || ', '__XXX__', $s);
12334 if (strpos($tmps, '|') !== false) {
12335 return 'Bad string syntax to evaluate (The char | can be used only when duplicated || with a space before and after): ' . $s;
12336 }
12337 */
12338
12339 // Check if there is PHP comments (can be used to obfuscate code)
12340 if (strpos($s, '/*') !== false || strpos($s, '//') !== false) {
12341 return 'Bad string syntax to evaluate (The comment string /* and // are not allowed): ' . $s;
12342 }
12343
12344 // Check if we found a ? without a space before and after
12345 $tmps = str_replace(' ? ', '__XXX__', $s);
12346 if (strpos($tmps, '?') !== false) {
12347 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): ' . $s;
12348 }
12349
12350 // Check if there is a < or <= without spaces after
12351 if (preg_match('/<=?[^\s]/', $s)) {
12352 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found a < or <= without space after): ' . $s;
12353 }
12354
12355 // Check if there is an include or a require
12356 if (preg_match('/(include|include_once|require|require_once)/', $s)) {
12357 return 'Bad string syntax to evaluate (found not allowed key include|include_once|require|require_once): ' . $s;
12358 }
12359
12360 // Check if there is dynamic call (first we use black list patterns)
12361 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
12362 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;
12363 }
12364
12365 if (empty($dolibarr_main_restrict_eval_methods)) {
12366 // If $dolibarr_main_restrict_eval_methods was set to '', we must check if we try dynamic call
12367
12368 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
12369 $savescheck = '';
12370 $scheck = $s;
12371 while ($scheck && $savescheck != $scheck) {
12372 $savescheck = $scheck;
12373 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
12374 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
12375 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
12376 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12377 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
12378 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
12379 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
12380 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
12381 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
12382 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
12383 }
12384 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
12385
12386 // Now test if it remains 1 open parenthesis.
12387 if (strpos($scheck, '(') !== false) {
12388 return 'Bad string syntax to evaluate (mode ' . $onlysimplestring . ', found call of a function or method without using the direct name of the function): ' . $s;
12389 }
12390 }
12391
12392 if (strpos($s, '`') !== false) {
12393 return 'Bad string syntax to evaluate (backtick char is forbidden): ' . $s;
12394 }
12395
12396 // Disallow also concat operator
12397 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) {
12398 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
12399 return 'Bad string syntax to evaluate (dot char is forbidden if not strictly between 2 numbers): ' . $s;
12400 }
12401 }
12402
12403 // We exclude string using a $ character that are not an expected global or temporary vars, so that are not:
12404 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $var....
12405 $savescheck = '';
12406 $scheck = $s;
12407 while ($scheck && $savescheck != $scheck) {
12408 $savescheck = $scheck;
12409 $scheck = preg_replace('/\$conf->[a-z\_]+->enabled/', '__VARCONFENABLED__', $scheck); // Remove this once $user->module->enabled has been replaced everywhere with isModEnabled.
12410 $scheck = preg_replace('/\$user->id/', '__VARUSERID__', $scheck);
12411 $scheck = preg_replace('/\$user->hasRight/', '__VARUSERHASRIGHT__', $scheck);
12412 $scheck = preg_replace('/\$user->rights/', '__VARUSERHASRIGHT__', $scheck); // Remove this once $user->rights->xxx is replaced everywhere with $user->hasRight()
12413 $scheck = preg_replace('/\$user->isAdmin/', '__VARUSERHASRIGHT__', $scheck);
12414 $scheck = preg_replace('/\$user->admin/', '__VARUSERISADMIN__', $scheck); // Remove this once $user->admin is replaced everywhere with $user->isAdmin()
12415 $scheck = preg_replace('/\$user->isExternalUser/', '__VARUSERSOCID__', $scheck);
12416 $scheck = preg_replace('/\$user->socid/', '__VARUSERSOCID__', $scheck); // Remove this once $user->admin is replaced everywhere with $user->isExternalUser()
12417 $scheck = preg_replace('/\‍(\$db\‍)/', '__VARDB__', $scheck);
12418 $scheck = preg_replace('/\$langs/', '__VARLANGSTRANS__', $scheck);
12419 $scheck = preg_replace('/\$mysoc/', '__VARMYSOC__', $scheck);
12420 $scheck = preg_replace('/\$action/', '__VARACTION__', $scheck);
12421 $scheck = preg_replace('/\$mainmenu/', '__VARMAINMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12422 $scheck = preg_replace('/\$leftmenu/', '__VARLEFTMENU__', $scheck); // Remove this once all tests on $mainmenu has been replaced with isStringVarMatching
12423 $scheck = preg_replace('/\$websitepage/', '__VARWEBSITEPAGE__', $scheck);
12424 $scheck = preg_replace('/\$website/', '__VARWEBSITE__', $scheck);
12425 $scheck = preg_replace('/\$objectoffield/', '__VAROBJECTOFFIELD__', $scheck);
12426 $scheck = preg_replace('/\$object/', '__VAROBJECT__', $scheck);
12427 $scheck = preg_replace('/\$var/', '__VARVAR__', $scheck);
12428
12429 // deprecated (now we use $objecf->canvas or $objectoffield->canvas)
12430 $scheck = preg_replace('/\$soc->canvas/', '__VARSOCCANVAS__', $scheck);
12431 $scheck = preg_replace('/\$obj->canvas/', '__VAROBJCANVAS__', $scheck);
12432
12433 // Now test if it remains one '$'
12434 if (strpos($scheck, '$') !== false) {
12435 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);
12436 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;
12437 }
12438 }
12439
12440 // We block use of php exec or php file functions
12441 $forbiddenphpstrings = array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction', 'SplFileObject', 'SplTempFileObject');
12442
12443 if (empty($dolibarr_main_restrict_eval_methods)) { // If forced to ''
12444 // 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)
12445 // 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
12446 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
12447 $forbiddenphpfunctions = array();
12448 $forbiddenphpmethods = array();
12449
12450 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
12451 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
12452 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
12453
12454 $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"));
12455 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
12456 $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"));
12457 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
12458 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
12459
12460 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
12461
12462 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
12463 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
12464 $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"));
12465 $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", ));
12466 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("putenv", "dl", "apache_child_terminate", "apache_setenv"));
12467 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("posix_kill", "posix_setuid", "posix_setgid"));
12468 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
12469 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
12470 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
12471 $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
12472 $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"));
12473 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
12474 if (!getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL')) { // We disallow all function that allow to obfuscate the real name of a function
12475 // @phpcs:ignore
12476 $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
12477 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concat", "dol_concatdesc")); // native dolibarr functions
12478 }
12479 // Remove from blacklist the function that are into the whitelist
12480 /*foreach ($forbiddenphpfunctions as $key => $forbiddenphpfunction) {
12481 if (in_array($forbiddenphpfunction, $dolibarr_main_restrict_eval_methods_array)) {
12482 unset($forbiddenphpfunctions[$key]);
12483 }
12484 }*/
12485
12486 $forbiddenphpmethods = array_merge($forbiddenphpmethods, array('invoke', 'invokeArgs')); // Methods of ReflectionFunction to execute a function
12487 // Remove from blacklist the function that are into the whitelist
12488 /*foreach ($forbiddenphpmethods as $key => $forbiddenphpmethod) {
12489 if (in_array($forbiddenphpmethod, $dolibarr_main_restrict_eval_methods_array)) {
12490 unset($forbiddenphpmethods[$key]);
12491 }
12492 }*/
12493
12494 $forbiddenphpregex = 'global\s*\$';
12495 $forbiddenphpregex .= '|';
12496 $forbiddenphpregex .= '\b(' . implode('|', $forbiddenphpfunctions) . ')\b';
12497
12498 $forbiddenphpmethodsregex = '->(' . implode('|', $forbiddenphpmethods) . ')';
12499
12500 // Now scan all forbidden patterns
12501 do {
12502 $oldstringtoclean = $s;
12503 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12504 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12505 $s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12506 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12507 } while ($oldstringtoclean != $s);
12508
12509 if (strpos($s, '__forbiddenstring__') !== false) {
12510 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12511 return 'Bad string syntax to evaluate: ' . $s;
12512 }
12513 }
12514
12515 if (!empty($dolibarr_main_restrict_eval_methods)) {
12516 // Accept only white-listed allowed function and classes
12517 // TODO Get all pattern '/([\s\w]+)\‍(/', then check that $reg[1] is a defined class or a function into a given list
12518 $pattern = '/([\s\w\'\]\"]+)\‍(/';
12519
12520 $matches = array();
12521 preg_match_all($pattern, $s, $matches);
12522
12523 if (count($matches)) {
12524 foreach ($matches[1] as $m) {
12525 $m = trim($m);
12526 if (empty($m)) {
12527 continue;
12528 }
12529 $reg = array();
12530 if (!preg_match('/new ([A-Z][\w]+)/i', $m, $reg)) {
12531 if (!in_array($m, $dolibarr_main_restrict_eval_methods_array)) {
12532 if ($m != "'" && $m != '"') {
12533 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12534 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;
12535 }
12536 }
12537 } else {
12538 if (!class_exists($reg[1])) {
12539 dol_syslog('Bad string syntax to evaluate: Class "'.$reg[1].'" does not exist. ' . $s, LOG_WARNING);
12540 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" does not exist. ' . $s;
12541 }
12542 $parents = class_parents($reg[1]); // Get list of parent classes of class we want to check
12543 if (!in_array('CommonObject', $parents)) { // Only classes that inherit CommonObject are ok. This forbid dangerous classes like ReflectionFunction, SplFileObject, ...
12544 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);
12545 return 'Bad string syntax to evaluate. Class "'.$reg[1].'" is not allowed because only classes extended CommonObject can be used in dynamic evaluation. ' . $s;
12546 }
12547 }
12548 }
12549 }
12550
12551 $forbiddenphpregex = 'global\s*\$';
12552 $forbiddenphpregex .= '|'; // or
12553 $forbiddenphpregex .= '}\s*\[';
12554 $forbiddenphpregex .= '|'; // or
12555 $forbiddenphpregex .= '\‍)\s*\‍(';
12556
12557 // Now scan all forbidden patterns
12558 do {
12559 $oldstringtoclean = $s;
12560 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
12561 $s = preg_replace('/' . $forbiddenphpregex . '/i', '__forbiddenstring__', $s);
12562 //$s = preg_replace('/' . $forbiddenphpmethodsregex . '/i', '__forbiddenstring__', $s);
12563 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
12564 } while ($oldstringtoclean != $s);
12565
12566 if (strpos($s, '__forbiddenstring__') !== false) {
12567 dol_syslog('Bad string syntax to evaluate: ' . $s, LOG_WARNING);
12568 return 'Bad string syntax to evaluate: ' . $s;
12569 }
12570 }
12571
12572 //print $s."<br>\n";
12573 ob_start(); // An evaluation has no reason to output data
12574 $isObBufferActive = true;
12575 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
12576 $tmpo = ob_get_clean(); // This close the buffer
12577 $isObBufferActive = false;
12578 if ($tmpo) {
12579 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
12580 }
12581 return $tmps;
12582 } catch (Exception $e) {
12583 if ($isObBufferActive) {
12584 // Clean up buffer which was left behind due to exception.
12585 $tmpo = ob_get_clean(); // This close the buffer
12586 $isObBufferActive = false;
12587 }
12588 $error = 'dol_eval try/catch error for string: ' . $s . ' - Error: ';
12589 $error .= $e->getMessage();
12590 dol_syslog($error, LOG_WARNING);
12591 return 'Exception during evaluation: ' . $s;
12592 } catch (Error $e) {
12593 if ($isObBufferActive) {
12594 // Clean up buffer which was left behind due to exception.
12595 $tmpo = ob_get_clean(); // This close the buffer
12596 $isObBufferActive = false;
12597 }
12598 $error = 'dol_eval try/catch error for string: ' . $s . ' - Error: ';
12599 $error .= $e->getMessage();
12600 dol_syslog($error, LOG_WARNING);
12601 return 'Exception during evaluation: ' . $s;
12602 }
12603}
12604
12612function dol_validElement($element)
12613{
12614 return (trim($element) != '');
12615}
12616
12625function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
12626{
12627 if (empty($codelang)) {
12628 return '';
12629 }
12630
12631 if ($codelang == 'auto') {
12632 return '<span class="fa fa-language"></span>';
12633 }
12634
12635 $langtocountryflag = array(
12636 'ar_AR' => '',
12637 'ca_ES' => 'catalonia',
12638 'da_DA' => 'dk',
12639 'fr_CA' => 'mq',
12640 'sv_SV' => 'se',
12641 'sw_SW' => 'unknown',
12642 'AQ' => 'unknown',
12643 'CW' => 'unknown',
12644 'IM' => 'unknown',
12645 'JE' => 'unknown',
12646 'MF' => 'unknown',
12647 'BL' => 'unknown',
12648 'SX' => 'unknown'
12649 );
12650
12651 if (isset($langtocountryflag[$codelang])) {
12652 $flagImage = $langtocountryflag[$codelang];
12653 } else {
12654 $tmparray = explode('_', $codelang);
12655 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
12656 }
12657
12658 $morecss = '';
12659 $reg = array();
12660 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
12661 $morecss = $reg[1];
12662 $moreatt = "";
12663 }
12664
12665 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
12666 return '<span class="flag-sprite ' . strtolower($flagImage) . ($morecss ? ' ' . $morecss : '') . '"' . ($moreatt ? ' ' . $moreatt : '') . (!$notitlealt ? ' title="' . $codelang . '"' : '') . '></span>';
12667}
12668
12676function getLanguageCodeFromCountryCode($countrycode)
12677{
12678 global $mysoc;
12679
12680 if (empty($countrycode)) {
12681 return null;
12682 }
12683
12684 if (strtoupper($countrycode) == 'MQ') {
12685 return 'fr_CA';
12686 }
12687 if (strtoupper($countrycode) == 'SE') {
12688 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
12689 }
12690 if (strtoupper($countrycode) == 'CH') {
12691 if ($mysoc->country_code == 'FR') {
12692 return 'fr_CH';
12693 }
12694 if ($mysoc->country_code == 'DE') {
12695 return 'de_CH';
12696 }
12697 if ($mysoc->country_code == 'IT') {
12698 return 'it_CH';
12699 }
12700 }
12701
12702 // Locale list taken from:
12703 // http://stackoverflow.com/questions/3191664/
12704 // list-of-all-locales-and-their-short-codes
12705 $locales = array(
12706 'af-ZA',
12707 'am-ET',
12708 'ar-AE',
12709 'ar-BH',
12710 'ar-DZ',
12711 'ar-EG',
12712 'ar-IQ',
12713 'ar-JO',
12714 'ar-KW',
12715 'ar-LB',
12716 'ar-LY',
12717 'ar-MA',
12718 'ar-OM',
12719 'ar-QA',
12720 'ar-SA',
12721 'ar-SY',
12722 'ar-TN',
12723 'ar-YE',
12724 //'as-IN', // Moved after en-IN
12725 'ba-RU',
12726 'be-BY',
12727 'bg-BG',
12728 'bn-BD',
12729 //'bn-IN', // Moved after en-IN
12730 'bo-CN',
12731 'br-FR',
12732 'ca-ES',
12733 'co-FR',
12734 'cs-CZ',
12735 'cy-GB',
12736 'da-DK',
12737 'de-AT',
12738 'de-CH',
12739 'de-DE',
12740 'de-LI',
12741 'de-LU',
12742 'dv-MV',
12743 'el-GR',
12744 'en-AU',
12745 'en-BZ',
12746 'en-CA',
12747 'en-GB',
12748 'en-IE',
12749 'en-IN',
12750 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
12751 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
12752 'en-JM',
12753 'en-MY',
12754 'en-NZ',
12755 'en-PH',
12756 'en-SG',
12757 'en-TT',
12758 'en-US',
12759 'en-ZA',
12760 'en-ZW',
12761 'es-AR',
12762 'es-BO',
12763 'es-CL',
12764 'es-CO',
12765 'es-CR',
12766 'es-DO',
12767 'es-EC',
12768 'es-ES',
12769 'es-GT',
12770 'es-HN',
12771 'es-MX',
12772 'es-NI',
12773 'es-PA',
12774 'es-PE',
12775 'es-PR',
12776 'es-PY',
12777 'es-SV',
12778 'es-US',
12779 'es-UY',
12780 'es-VE',
12781 'et-EE',
12782 'eu-ES',
12783 'fa-IR',
12784 'fi-FI',
12785 'fo-FO',
12786 'fr-BE',
12787 'fr-CA',
12788 'fr-CH',
12789 'fr-FR',
12790 'fr-LU',
12791 'fr-MC',
12792 'fy-NL',
12793 'ga-IE',
12794 'gd-GB',
12795 'gl-ES',
12796 'gu-IN',
12797 'he-IL',
12798 'hi-IN',
12799 'hr-BA',
12800 'hr-HR',
12801 'hu-HU',
12802 'hy-AM',
12803 'id-ID',
12804 'ig-NG',
12805 'ii-CN',
12806 'is-IS',
12807 'it-CH',
12808 'it-IT',
12809 'ja-JP',
12810 'ka-GE',
12811 'kk-KZ',
12812 'kl-GL',
12813 'km-KH',
12814 'kn-IN',
12815 'ko-KR',
12816 'ky-KG',
12817 'lb-LU',
12818 'lo-LA',
12819 'lt-LT',
12820 'lv-LV',
12821 'mi-NZ',
12822 'mk-MK',
12823 'ml-IN',
12824 'mn-MN',
12825 'mr-IN',
12826 'ms-BN',
12827 'ms-MY',
12828 'mt-MT',
12829 'nb-NO',
12830 'ne-NP',
12831 'nl-BE',
12832 'nl-NL',
12833 'nn-NO',
12834 'oc-FR',
12835 'or-IN',
12836 'pa-IN',
12837 'pl-PL',
12838 'ps-AF',
12839 'pt-BR',
12840 'pt-PT',
12841 'rm-CH',
12842 'ro-MD',
12843 'ro-RO',
12844 'ru-RU',
12845 'rw-RW',
12846 'sa-IN',
12847 'se-FI',
12848 'se-NO',
12849 'se-SE',
12850 'si-LK',
12851 'sk-SK',
12852 'sl-SI',
12853 'sq-AL',
12854 'sv-FI',
12855 'sv-SE',
12856 'sw-KE',
12857 'ta-IN',
12858 'te-IN',
12859 'th-TH',
12860 'tk-TM',
12861 'tn-ZA',
12862 'tr-TR',
12863 'tt-RU',
12864 'ug-CN',
12865 'uk-UA',
12866 'ur-PK',
12867 'vi-VN',
12868 'wo-SN',
12869 'xh-ZA',
12870 'yo-NG',
12871 'zh-CN',
12872 'zh-HK',
12873 'zh-MO',
12874 'zh-SG',
12875 'zh-TW',
12876 'zu-ZA',
12877 );
12878
12879 $buildprimarykeytotest = strtolower($countrycode) . '-' . strtoupper($countrycode);
12880 if (in_array($buildprimarykeytotest, $locales)) {
12881 return strtolower($countrycode) . '_' . strtoupper($countrycode);
12882 }
12883
12884 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
12885 foreach ($locales as $locale) {
12886 $locale_language = locale_get_primary_language($locale);
12887 $locale_region = locale_get_region($locale);
12888 if (strtoupper($countrycode) == $locale_region) {
12889 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
12890 return strtolower($locale_language) . '_' . strtoupper($locale_region);
12891 }
12892 }
12893 } else {
12894 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
12895 }
12896
12897 return null;
12898}
12899
12930function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
12931{
12932 global $hookmanager, $db;
12933
12934 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
12935 foreach ($conf->modules_parts['tabs'][$type] as $value) {
12936 $values = explode(':', $value);
12937
12938 $reg = array();
12939 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
12940 if (count($values) !== 6) {
12941 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);
12942 continue;
12943 }
12944
12945 // new declaration with permissions:
12946 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12947 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
12948 if ($values[0] != $type) {
12949 continue;
12950 }
12951
12952 $newtab = array();
12953 $postab = $h;
12954 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
12955 $str = $values[1];
12956 $posstart = strpos($str, '(');
12957 if ($posstart > 0) {
12958 $posend = strpos($str, ')');
12959 if ($posstart > 0) {
12960 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
12961 if (is_numeric($res1)) {
12962 $postab = (int) $res1;
12963 $values[1] = '+' . substr($str, $posend + 1);
12964 }
12965 }
12966 }
12967
12968 global $objectoffield; // So we can use $objectoffield int verifCond
12969 $objectoffield = $object;
12970
12971 if (!verifCond($values[4], '2')) {
12972 continue;
12973 }
12974
12975 if ($values[3]) {
12976 if ($filterorigmodule) { // If a filter of module origin has been requested
12977 if (strpos($values[3], '@')) { // This is an external module
12978 if ($filterorigmodule != 'external') {
12979 continue;
12980 }
12981 } else { // This looks a core module
12982 if ($filterorigmodule != 'core') {
12983 continue;
12984 }
12985 }
12986 }
12987 $langs->load($values[3]);
12988 }
12989
12990 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
12991 // If label is "SUBSTITUION_..."
12992 $substitutionarray = array();
12993 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
12994 $label = make_substitutions($reg[1], $substitutionarray);
12995 } else {
12996 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
12997 $labeltemp = explode(',', $values[2]);
12998 $label = $langs->trans($labeltemp[0]);
12999
13000 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
13001 dol_include_once($labeltemp[2]);
13002 $classtoload = $labeltemp[1];
13003 if (class_exists($classtoload)) {
13004 $obj = new $classtoload($db);
13005 $function = $labeltemp[3];
13006 if ($obj && $function && method_exists($obj, $function)) {
13007 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
13008 $nbrec = $obj->$function($object->id, $obj);
13009 if (!empty($nbrec)) {
13010 $label .= '<span class="badge marginleftonlyshort">' . $nbrec . '</span>';
13011 }
13012 }
13013 }
13014 }
13015 }
13016 $url = preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]);
13017 $link = parse_url($url);
13018 $query = [];
13019 if (isset($link['query'])) {
13020 parse_str($link['query'], $query);
13021 }
13022 $newtab[0] = dolBuildUrl(dol_buildpath($link['path'], 1), $query);
13023 $newtab[1] = $label;
13024 $newtab[2] = str_replace('+', '', $values[1]);
13025 $h++;
13026
13027 // set tab at its position
13028 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
13029 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
13030 if ($values[0] != $type) {
13031 continue;
13032 }
13033 $tabname = str_replace('-', '', $values[1]);
13034 foreach ($head as $key => $val) {
13035 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
13036 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
13037 if ($head[$key][2] == $tabname && $condition) {
13038 unset($head[$key]);
13039 break;
13040 }
13041 }
13042 }
13043 }
13044 }
13045
13046 // No need to make a return $head. Var is modified as a reference
13047 if (!empty($hookmanager)) {
13048 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule, 'type' => $type);
13049 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
13050 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
13051 if ($reshook > 0) { // Hook ask to replace completely the array
13052 $head = $hookmanager->resArray;
13053 } else { // Hook
13054 $head = array_merge($head, $hookmanager->resArray);
13055 }
13056 $h = count($head);
13057 }
13058}
13059
13071function printCommonFooter($zone = 'private')
13072{
13073 global $conf, $hookmanager, $user, $langs;
13074 global $action;
13075 global $micro_start_time;
13076
13077 if ($zone == 'private') {
13078 print "\n" . '<!-- Common footer for private page -->' . "\n";
13079 } else {
13080 print "\n" . '<!-- Common footer for public page -->' . "\n";
13081 }
13082
13083 // A div to store page_y POST parameter so we can read it using javascript
13084 print "\n<!-- A div to store page_y POST parameter -->\n";
13085 print '<div id="page_y" style="display: none;">' . (GETPOST('page_y') ? GETPOST('page_y') : '') . '</div>' . "\n";
13086
13087 $parameters = array('zone' => $zone);
13088 $tmpobject = null;
13089 // @phan-suppress-next-line PhanPluginConstantVariableNull
13090 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters, $tmpobject, $action); // Note that $action and $object may have been modified by some hooks
13091 if (empty($reshook)) {
13092 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
13093 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
13094 }
13095
13096 print "\n";
13097 if (!empty($conf->use_javascript_ajax)) {
13098 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
13099 print '<script>' . "\n";
13100 print 'jQuery(document).ready(function() {' . "\n";
13101
13102 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
13103 print "\n";
13104 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */' . "\n";
13105 print 'jQuery("li.menuhider").click(function(event) {';
13106 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }' . "\n";
13107 print ' console.log("We click on .menuhider");' . "\n";
13108 print ' $("body").toggleClass("sidebar-collapse")' . "\n";
13109 print '});' . "\n";
13110 }
13111
13112 // Management of focus and mandatory for fields
13113 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"])))) {
13114 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */' . "\n";
13115 $relativepathstring = $_SERVER["PHP_SELF"];
13116 // Clean $relativepathstring
13117 if (constant('DOL_URL_ROOT')) {
13118 $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
13119 }
13120 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
13121 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
13122 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
13123
13124 if (!empty($user->default_values[$relativepathstring]['focus'])) {
13125 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
13126 $qualified = 0;
13127 if ($defkey != '_noquery_') {
13128 $tmpqueryarraytohave = explode('&', $defkey);
13129 $foundintru = 0;
13130 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
13131 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
13132 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
13133 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
13134 $foundintru = 1;
13135 }
13136 }
13137 if (!$foundintru) {
13138 $qualified = 1;
13139 }
13140 //var_dump($defkey.'-'.$qualified);
13141 } else {
13142 $qualified = 1;
13143 }
13144
13145 if ($qualified) {
13146 print 'console.log("set the focus by executing jQuery(...).focus();")' . "\n";
13147 foreach ($defval as $paramkey => $paramval) {
13148 // Set focus on field
13149 print 'jQuery("input[name=\'' . $paramkey . '\']").focus();' . "\n";
13150 print 'jQuery("textarea[name=\'' . $paramkey . '\']").focus();' . "\n"; // TODO KO with ckeditor
13151 print 'jQuery("select[name=\'' . $paramkey . '\']").focus();' . "\n"; // Not really useful, but we keep it in case of.
13152 }
13153 }
13154 }
13155 }
13156 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
13157 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
13158 $qualified = 0;
13159 if ($defkey != '_noquery_') {
13160 $tmpqueryarraytohave = explode('&', $defkey);
13161 $foundintru = 0;
13162 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
13163 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
13164 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
13165 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
13166 $foundintru = 1;
13167 }
13168 }
13169 if (!$foundintru) {
13170 $qualified = 1;
13171 }
13172 //var_dump($defkey.'-'.$qualified);
13173 } else {
13174 $qualified = 1;
13175 }
13176
13177 if ($qualified) {
13178 print 'console.log("set the js code to manage fields that are set as mandatory");' . "\n";
13179
13180 foreach ($defval as $paramkey => $paramval) {
13181 // Solution 1: Add handler on submit to check if mandatory fields are empty
13182 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
13183 print "form.on('submit', function(event) {
13184 var submitter = \$(this).find(':submit:focus').get(0);
13185 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
13186
13187 if (buttonName == 'cancel') {
13188 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
13189 return true;
13190 }
13191
13192 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');
13193
13194 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
13195 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
13196
13197 if (tmptypefield == 'textarea') {
13198 // We must instead check the content of ckeditor
13199 var tmpeditor = CKEDITOR.instances['" . dol_escape_js($paramkey) . "'];
13200 if (tmpeditor) {
13201 tmpvalue = tmpeditor.getData();
13202 console.log('For textarea tmpvalue is '+tmpvalue);
13203 }
13204 }
13205
13206 let tmpvalueisempty = false;
13207 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
13208 tmpvalueisempty = true;
13209 }
13210 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
13211 tmpvalueisempty = true;
13212 }
13213 if (tmpvalueisempty && buttonName !== 'cancel') {
13214 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
13215 event.preventDefault(); // Stop submission of form to allow custom code to decide.
13216 event.stopPropagation(); // Stop other handlers.
13217
13218 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
13219
13220 return false;
13221 }
13222 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
13223 return true;
13224 });
13225 \n";
13226
13227 // Solution 2: Add property 'required' on input
13228 // so browser will check value and try to focus on it when submitting the form.
13229 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
13230 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
13231 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
13232 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
13233 //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";
13234 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
13235 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
13236 // Add 'field required' class on closest td for all input elements : input, textarea and select
13237 //print '}, 500);'; // 500 milliseconds delay
13238
13239 // Now set the class "fieldrequired"
13240 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
13241 }
13242
13243 // If we submit using the cancel button, we remove the required attributes
13244 print 'jQuery("input[name=\'cancel\']").click(function() {
13245 console.log("We click on cancel button so removed all required attribute");
13246 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
13247 });' . "\n";
13248 }
13249 }
13250 }
13251 }
13252
13253 print '});' . "\n";
13254
13255 // End of tuning
13256 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
13257 print "\n";
13258 print "/* JS CODE TO ENABLE to add memory info */\n";
13259 print 'window.console && console.log("';
13260 if (getDolGlobalString('MEMCACHED_SERVER')) {
13261 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER') . ' - ';
13262 }
13263 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
13264 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
13265 $micro_end_time = microtime(true);
13266 print ' - Build time: ' . ceil(1000 * ($micro_end_time - $micro_start_time)) . ' ms';
13267 }
13268
13269 if (function_exists("memory_get_usage")) {
13270 print ' - Mem: ' . memory_get_usage(); // Do not use true here, it seems it takes the peak amount
13271 }
13272 if (function_exists("memory_get_peak_usage")) {
13273 print ' - Real mem peak: ' . memory_get_peak_usage(true);
13274 }
13275 if (function_exists("zend_loader_file_encoded")) {
13276 print ' - Zend encoded file: ' . (zend_loader_file_encoded() ? 'yes' : 'no');
13277 }
13278 print '");' . "\n";
13279 }
13280
13281 print "\n" . '</script>' . "\n";
13282
13283 // Google Analytics
13284 // TODO Remove this, can be replaced with the hook printCommonFooter
13285 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
13286 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
13287 foreach ($tmptagarray as $tmptag) {
13288 print "\n";
13289 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
13290 print '
13291 <!-- Global site tag (gtag.js) - Google Analytics -->
13292 <script nonce="' . getNonce() . '" async src="https://www.googletagmanager.com/gtag/js?id=' . trim($tmptag) . '"></script>
13293 <script>
13294 window.dataLayer = window.dataLayer || [];
13295 function gtag(){dataLayer.push(arguments);}
13296 gtag(\'js\', new Date());
13297
13298 gtag(\'config\', \'' . trim($tmptag) . '\');
13299 </script>';
13300 print "\n";
13301 }
13302 }
13303 }
13304
13305 // Add Xdebug coverage of code
13306 if (defined('XDEBUGCOVERAGE')) {
13307 print_r(xdebug_get_code_coverage());
13308 }
13309
13310 // Output string from hooks
13311 if (!empty($hookmanager->resPrint)) {
13312 print $hookmanager->resPrint;
13313 }
13314
13315 // Add DebugBar data
13316 if ($user->hasRight('debugbar', 'read')) {
13317 global $debugbar;
13318 if ($debugbar instanceof DebugBar\DebugBar) {
13319 if (isset($debugbar['time'])) {
13320 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
13321 $debugbar['time']->stopMeasure('pageaftermaster');
13322 }
13323 print '<!-- Output debugbar data -->' . "\n";
13324 $renderer = $debugbar->getJavascriptRenderer();
13325 print $renderer->render();
13326 }
13327 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
13328 print "\n";
13329 print "<!-- Start of log output\n";
13330 //print '<div class="hidden">'."\n";
13331 foreach ($conf->logbuffer as $logline) {
13332 print $logline . "<br>\n";
13333 }
13334 //print '</div>'."\n";
13335 print "End of log output -->\n";
13336 }
13337 }
13338}
13339
13349function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
13350{
13351 if (is_null($string)) {
13352 return array();
13353 }
13354
13355 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
13356 // This is a regex string
13357 $newdelimiter = $delimiter;
13358 } else {
13359 // This is a simple string
13360 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
13361 $newdelimiter = preg_quote($delimiter, '/');
13362 }
13363
13364 if ($a = preg_split('/' . $newdelimiter . '/', $string)) {
13365 $ka = array();
13366 foreach ($a as $s) { // each part
13367 if ($s) {
13368 if ($pos = strpos($s, $kv)) { // key/value delimiter
13369 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
13370 } else { // key delimiter not found
13371 $ka[] = trim($s);
13372 }
13373 }
13374 }
13375 return $ka;
13376 }
13377
13378 return array();
13379}
13380
13388function dolExplodeKeepIfQuotes($input)
13389{
13390 // Use regexp to capture words and section in quotes
13391 $matches = array();
13392 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
13393
13394 // Merge result and delete empty values
13395
13396 $result = array_map(
13403 static function ($a, $b, $c) {
13404 if ($a !== '') {
13405 return $a;
13406 }
13407 if ($b !== '') {
13408 return $b;
13409 }
13410 if ($c !== '') {
13411 return $c;
13412 }
13413 return '';
13414 },
13415 $matches[1],
13416 $matches[2],
13417 $matches[3]
13418 );
13419 return array_values(array_filter(
13420 $result,
13427 static function ($val) {
13428 return $val !== '';
13429 }
13430 ));
13431}
13432
13433
13440function dol_set_focus($selector)
13441{
13442 print "\n" . '<!-- Set focus onto a specific field -->' . "\n";
13443 print '<script nonce="' . getNonce() . '">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("' . dol_escape_js($selector) . '").focus(); });</script>' . "\n";
13444}
13445
13446
13454function dol_getmypid()
13455{
13456 if (!function_exists('getmypid')) {
13457 return mt_rand(99900000, 99965535);
13458 } else {
13459 return getmypid(); // May be a number on 64 bits (depending on OS)
13460 }
13461}
13462
13485function natural_search($fields, $value, $mode = 0, $nofirstand = 0, $sqltoadd = '')
13486{
13487 global $db, $langs;
13488
13489 $value = trim($value);
13490
13491 if ($mode == 0) {
13492 $value = preg_replace('/\*/', '%', $value); // Replace * with %
13493 }
13494 if ($mode == 1) {
13495 $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
13496 }
13497
13498 $value = preg_replace('/\s*\|\s*/', '|', $value);
13499
13500 // Split criteria on ' ' but not if we are inside quotes.
13501 // For mode 3, the split is done later on the , only and not on the ' '.
13502 if ($mode != -3 && $mode != 3) {
13503 $crits = dolExplodeKeepIfQuotes($value);
13504 } else {
13505 $crits = array($value);
13506 }
13507
13508 $res = '';
13509 if (!is_array($fields)) {
13510 $fields = array($fields);
13511 }
13512 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
13513 foreach ($crits as $crit) { // Loop on each AND criteria
13514 $crit = trim($crit);
13515 $i2 = 0; // count the nb of valid criteria added for this this first criteria
13516 $newres = '';
13517
13518 foreach ($fields as $field) {
13519 if ($mode == 1) {
13520 $tmpcrits = explode('|', $crit);
13521 $i3 = 0; // count the nb of valid criteria added for this current field
13522 foreach ($tmpcrits as $tmpcrit) {
13523 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13524 continue;
13525 }
13526 $tmpcrit = trim($tmpcrit);
13527
13528 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13529
13530 $operator = '=';
13531 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
13532
13533 $reg = array();
13534 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
13535 if (!empty($reg[1])) {
13536 $operator = $reg[1];
13537 }
13538 if ($newcrit != '') {
13539 $numnewcrit = price2num($newcrit);
13540 if (is_numeric($numnewcrit)) {
13541 $newres .= $db->sanitize($field) . ' ' . $operator . ' ' . ((float) $numnewcrit); // should be a numeric
13542 } else {
13543 $newres .= '1 = 2'; // force false, we received a corrupted data
13544 }
13545 $i3++; // a criteria was added to string
13546 }
13547 }
13548 $i2++; // a criteria for 1 more field was added to string
13549 } elseif ($mode == 2 || $mode == -2) {
13550 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
13551 $newres .= ($i2 > 0 ? ' OR ' : '') . $db->sanitize($field) . " " . ($mode == -2 ? 'NOT ' : '');
13552 $newres .= $crit ? "IN (" . $db->sanitize($db->escape($crit)) . ")" : "IN (0)";
13553 if ($mode == -2) {
13554 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13555 }
13556 $i2++; // a criteria for 1 more field was added to string
13557 } elseif ($mode == 3 || $mode == -3) {
13558 $tmparray = explode(',', $crit);
13559 if (count($tmparray)) {
13560 $listofcodes = '';
13561 $listofcodesnot = '';
13562 foreach ($tmparray as $val) {
13563 $val = trim($val);
13564 if ($val !== '') {
13565 if (preg_match('/^!/', $val)) {
13566 $listofcodesnot .= ($listofcodesnot ? ',' : '');
13567 $listofcodesnot .= "'" . $db->escape(preg_replace('/^!=?/', '', $val)) . "'";
13568 } else {
13569 $listofcodes .= ($listofcodes ? ',' : '');
13570 $listofcodes .= "'" . $db->escape($val) . "'";
13571 }
13572 }
13573 }
13574 $newres .= ($i2 > 0 ? ' OR ' : '');
13575 if ($listofcodes && $listofcodesnot) {
13576 $newres .= '(';
13577 }
13578 if ($listofcodes) {
13579 $newres .= $db->sanitize($field) . " " . ($mode == -3 ? 'NOT IN' : 'IN') . " (" . $db->sanitize($listofcodes, 1, 0, 1) . ")";
13580 }
13581 if ($listofcodes && $listofcodesnot) {
13582 $newres .= ' AND ';
13583 }
13584 if ($listofcodesnot) {
13585 $newres .= $db->sanitize($field) . " " . ($mode == -3 ? 'IN ' : 'NOT IN') . " (" . $db->sanitize($listofcodesnot, 1, 0, 1) . ")";
13586 }
13587 if ($listofcodes && $listofcodesnot) {
13588 $newres .= ')';
13589 }
13590 $i2++; // a criteria for 1 more field was added to string
13591 }
13592 if ($mode == -3) {
13593 $newres .= ' OR ' . $db->sanitize($field) . ' IS NULL';
13594 }
13595 } elseif ($mode == 4) {
13596 $tmparray = explode(',', $crit);
13597 if (count($tmparray)) {
13598 $listofcodes = '';
13599 foreach ($tmparray as $val) {
13600 $val = trim($val);
13601 if ($val) {
13602 $newres .= ($i2 > 0 ? " OR (" : "(") . $db->sanitize($field) . " LIKE '" . $db->escape($val) . ",%'";
13603 $newres .= ' OR ' . $db->sanitize($field) . " = '" . $db->escape($val) . "'";
13604 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . "'";
13605 $newres .= ' OR ' . $db->sanitize($field) . " LIKE '%," . $db->escape($val) . ",%'";
13606 $newres .= ')';
13607 $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)
13608 }
13609 }
13610 }
13611 } else { // $mode=0
13612 $tmpcrits = explode('|', $crit);
13613 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
13614 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
13615 if ($tmpcrit !== '0' && empty($tmpcrit)) {
13616 continue;
13617 }
13618 $tmpcrit = trim($tmpcrit);
13619
13620 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
13621 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
13622 } else {
13623 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
13624 }
13625
13626 $isSellist = false;
13627 $table = $label = $key = null;
13628
13629 if (strpos($field, 'ef.') === 0) {
13630 $extrafieldName = substr($field, 3);
13631 $extrafields = new ExtraFields($db);
13632 $extrafields->fetch_name_optionals_label('product');
13633
13634 if (isset($extrafields->attributes['product']['type'][$extrafieldName]) && $extrafields->attributes['product']['type'][$extrafieldName] === 'sellist') {
13635 $isSellist = true;
13636 $paramArray = $extrafields->attributes['product']['param'][$extrafieldName]['options'] ?? [];
13637 $param = array_key_first($paramArray);
13638 list($table, $label, $key) = explode(':', $param);
13639 }
13640 }
13641
13642 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
13643 $newres .= $db->sanitize($field) . " = " . (is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
13644 } else {
13645 $tmpcrit2 = $tmpcrit;
13646 $tmpbefore = '%';
13647 $tmpafter = '%';
13648 $tmps = '';
13649
13650 if ($isSellist) {
13651 $newres .= $field . " IN (SELECT t." . $key . " FROM " . $db->prefix() . $table . " AS t WHERE t." . $label . " LIKE '%" . $db->escape($tmpcrit2) . "%')";
13652 } else {
13653 if (preg_match('/^!/', $tmpcrit)) {
13654 $tmps .= $db->sanitize($field) . " NOT LIKE "; // ! as exclude character
13655 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
13656 } else {
13657 $tmps .= $db->sanitize($field) . " LIKE ";
13658 }
13659 $tmps .= "'";
13660
13661 if (preg_match('/^[\^\$]/', $tmpcrit)) {
13662 $tmpbefore = '';
13663 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
13664 }
13665 if (preg_match('/[\^\$]$/', $tmpcrit)) {
13666 $tmpafter = '';
13667 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
13668 }
13669
13670 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13671 $tmps = "(" . $tmps;
13672 }
13673 $newres .= $tmps;
13674 $newres .= $tmpbefore;
13675 $newres .= $db->escape($tmpcrit2);
13676 $newres .= $tmpafter;
13677 $newres .= "'";
13678 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
13679 $newres .= " OR " . $field . " IS NULL)";
13680 }
13681 }
13682 }
13683
13684 $i3++;
13685 }
13686
13687 $i2++; // a criteria for 1 more field was added to string
13688 }
13689 }
13690
13691 if ($sqltoadd) {
13692 $newres .= ($newres ? '' : ' OR ').str_replace('__KEYTOSEARCH__', $crit, $sqltoadd);
13693 }
13694
13695 if ($newres) {
13696 $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') . $newres . ($i2 > 1 ? ')' : '');
13697 }
13698 $i1++;
13699 }
13700 $res = ($nofirstand ? "" : " AND ") . "(" . $res . ")";
13701
13702 return $res;
13703}
13704
13712function showSimpleHTMLTable($outputlangs, $object)
13713{
13714 global $conf;
13715
13716 $discountIsAvailable = false;
13717 $orderPositionHasNoPrice = false;
13718
13719 if (!property_exists($object->lines[0], "remise_percent") ||
13720 !property_exists($object->lines[0], "fk_unit") ||
13721 !property_exists($object->lines[0], "multicurrency_total_ttc") ||
13722 !property_exists($object->lines[0], "description") ||
13723 !property_exists($object->lines[0], "qty")) {
13724 return"";
13725 }
13726
13727 foreach ($object->lines as $order_position) {
13728 if (!property_exists($order_position, "price")) {
13729 $orderPositionHasNoPrice = true;
13730 break;
13731 }
13732
13733 if (!empty($order_position->remise_percent)) {
13734 $discountIsAvailable = true;
13735 break;
13736 }
13737 };
13738
13739 if ($orderPositionHasNoPrice) {
13740 return "";
13741 }
13742
13743 $discountHeader = $discountIsAvailable ? '<th style="width:120px">'.$outputlangs->trans("Discount").'</th>' : '';
13744
13745 $table = '<table border="0" cellpadding="1" cellspacing="1">';
13746 $table .= '
13747 <thead>
13748 <tr>
13749 <th style="width:50px; text-align:left">#</th>
13750 <th style="text-align:left">'.$outputlangs->trans("Description").'</th>
13751 <th style="width:120px; text-align:right;">'.$outputlangs->trans("Price").'</th>
13752 <th style="width:100px; text-align:right;">'.$outputlangs->trans("Quantity").'</th>
13753 <th style="width:120px; text-align:right;">'.$outputlangs->trans("Unit").'</th>'.
13754 $discountHeader.'
13755 <th style="width:120px; text-align:right;">'.$outputlangs->trans("Sum").'</th>
13756 </tr>
13757 </thead>
13758 <tbody>';
13759
13760 foreach ($object->lines as $index => $order_position) {
13761 $position = $index + 1;
13762 $price = price($order_position->price, 0, $outputlangs, 0, -1, -1, $conf->currency);
13763 $unit = measuringUnitString($order_position->fk_unit, '', null, 1);
13764 $total = price($order_position->multicurrency_total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency);
13765 $discount = $discountIsAvailable ? '<td style="text-align:center">'.$order_position->remise_percent.'%</td>' : "";
13766
13767 $table .= '
13768 <tr>
13769 <td>'.$position.'</td>
13770 <td>'.$order_position->description.'</td>
13771 <td style="text-align:right">'.$price.'</td>
13772 <td style="text-align:right">'.$order_position->qty.'</td>
13773 <td style="text-align:right">'.$unit.'</td>'.
13774 $discount.'
13775 <td style="text-align:right">'.$total.'</td>
13776 </tr>';
13777 }
13778 $table .= '</tbody></table>';
13779
13780 return $table;
13781}
13782
13789function showDirectDownloadLink($object)
13790{
13791 global $langs;
13792
13793 $out = '';
13794 $url = $object->getLastMainDocLink($object->element);
13795
13796 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe') . ' <span class="opacitymedium">' . $langs->trans("DirectDownloadLink") . '</span><br>';
13797 if ($url) {
13798 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="' . $url . '"></div>';
13799 $out .= ajax_autoselect("directdownloadlink", '');
13800 } else {
13801 $out .= '<div class="urllink">' . $langs->trans("FileNotShared") . '</div>';
13802 }
13803
13804 return $out;
13805}
13806
13815function getImageFileNameForSize($file, $extName, $extImgTarget = '')
13816{
13817 $dirName = dirname($file);
13818 if ($dirName == '.') {
13819 $dirName = '';
13820 }
13821
13822 if (!in_array($extName, array('', '_small', '_mini'))) {
13823 return 'Bad parameter extName';
13824 }
13825
13826 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp|\.avif)$/i', '', $file); // We remove image extension, whatever is its case
13827 $fileName = basename($fileName);
13828
13829 if (empty($extImgTarget)) {
13830 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
13831 }
13832 if (empty($extImgTarget)) {
13833 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
13834 }
13835 if (empty($extImgTarget)) {
13836 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
13837 }
13838 if (empty($extImgTarget)) {
13839 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
13840 }
13841 if (empty($extImgTarget)) {
13842 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
13843 }
13844 if (empty($extImgTarget)) {
13845 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
13846 }
13847 if (empty($extImgTarget)) {
13848 $extImgTarget = (preg_match('/\.avif$/i', $file) ? '.avif' : '');
13849 }
13850
13851 if (!$extImgTarget) {
13852 return $file;
13853 }
13854
13855 $subdir = '';
13856 if ($extName) {
13857 $subdir = 'thumbs/';
13858 }
13859
13860 return ($dirName ? $dirName . '/' : '') . $subdir . $fileName . $extName . $extImgTarget; // New filename for thumb
13861}
13862
13863
13873function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
13874{
13875 global $conf, $langs;
13876
13877 if (empty($conf->use_javascript_ajax)) {
13878 return '';
13879 }
13880
13881 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
13882
13883 if ($alldata == 1) {
13884 if ($isAllowedForPreview) {
13885 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));
13886 } else {
13887 return array();
13888 }
13889 }
13890
13891 // old behavior, return a string
13892 if ($isAllowedForPreview) {
13893 $tmpurl = DOL_URL_ROOT . '/document.php?modulepart=' . urlencode($modulepart) . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : '');
13894 $title = $langs->transnoentities("Preview");
13895 //$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().
13896 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
13897
13898 // 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,
13899 // 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.
13900 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
13901 return 'javascript:' . dol_escape_uri('document_preview(\'' . dol_escape_js($tmpurl) . '\', \'' . dol_escape_js(dol_mimetype($relativepath)) . '\', \'' . dol_escape_js($title) . '\')');
13902 } else {
13903 return '';
13904 }
13905}
13906
13913function getLabelSpecialCode($idcode)
13914{
13915 global $langs;
13916
13917 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
13918 if ($idcode > 10) {
13919 return 'Module ID ' . $idcode;
13920 }
13921 if (!empty($arrayspecialines[$idcode])) {
13922 return $langs->trans($arrayspecialines[$idcode]);
13923 }
13924 return '';
13925}
13926
13936function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
13937{
13938 global $langs;
13939 $out = '<script nonce="' . getNonce() . '">
13940 jQuery(document).ready(function () {
13941 jQuery("' . ((strpos($htmlname, '.') === 0 ? '' : '#') . $htmlname) . '").click(function() { jQuery(this).select(); } );
13942 });
13943 </script>';
13944 if ($addlink) {
13945 if ($textonlink === 'image') {
13946 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . img_picto('', 'globe') . '</a>';
13947 } else {
13948 $out .= ' <a href="' . $addlink . '" target="_blank" rel="noopener noreferrer">' . $langs->trans("Link") . '</a>';
13949 }
13950 }
13951 return $out;
13952}
13953
13961function dolIsAllowedForPreview($file)
13962{
13963 // Check .noexe extension in filename
13964 if (preg_match('/\.noexe$/i', $file)) {
13965 return 0;
13966 }
13967
13968 // Check mime types
13969 $mime_preview = array('avif', 'bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp', 'webm', 'mp4');
13970 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
13971 $mime_preview[] = 'svg+xml';
13972 }
13973 //$mime_preview[]='vnd.oasis.opendocument.presentation';
13974 //$mime_preview[]='archive';
13975 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
13976 if ($num_mime !== false) {
13977 return 1;
13978 }
13979
13980 // By default, not allowed for preview
13981 return 0;
13982}
13983
13984
13994function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
13995{
13996 $mime = $default;
13997 $imgmime = 'other.png';
13998 $famime = 'file-o';
13999 $srclang = '';
14000
14001 $tmpfile = preg_replace('/\.noexe$/', '', $file);
14002
14003 // Plain text files
14004 if (preg_match('/\.txt$/i', $tmpfile)) {
14005 $mime = 'text/plain';
14006 $imgmime = 'text.png';
14007 $famime = 'file-alt';
14008 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
14009 $mime = 'text/richtext';
14010 $imgmime = 'text.png';
14011 $famime = 'file-alt';
14012 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
14013 $mime = 'text/csv';
14014 $imgmime = 'text.png';
14015 $famime = 'file-csv';
14016 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
14017 $mime = 'text/tab-separated-values';
14018 $imgmime = 'text.png';
14019 $famime = 'file-alt';
14020 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
14021 $mime = 'text/plain';
14022 $imgmime = 'text.png';
14023 $famime = 'file-alt';
14024 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
14025 $mime = 'text/plain';
14026 $imgmime = 'text.png';
14027 $srclang = 'ini';
14028 $famime = 'file-alt';
14029 } elseif (preg_match('/\.md$/i', $tmpfile)) {
14030 $mime = 'text/plain';
14031 $imgmime = 'text.png';
14032 $srclang = 'md';
14033 $famime = 'file-alt';
14034 } elseif (preg_match('/\.css$/i', $tmpfile)) {
14035 $mime = 'text/css';
14036 $imgmime = 'css.png';
14037 $srclang = 'css';
14038 $famime = 'file-alt';
14039 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
14040 $mime = 'text/plain';
14041 $imgmime = 'text.png';
14042 $srclang = 'lang';
14043 $famime = 'file-alt';
14044 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
14045 $mime = 'text/plain';
14046 $imgmime = 'text.png';
14047 $famime = 'file-alt';
14048 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
14049 $mime = 'text/html';
14050 $imgmime = 'html.png';
14051 $srclang = 'html';
14052 $famime = 'file-alt';
14053 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
14054 $mime = 'text/xml';
14055 $imgmime = 'other.png';
14056 $srclang = 'xml';
14057 $famime = 'file-alt';
14058 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
14059 $mime = 'text/xml';
14060 $imgmime = 'other.png';
14061 $srclang = 'xaml';
14062 $famime = 'file-alt';
14063 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
14064 $mime = 'text/plain';
14065 $imgmime = 'text.png';
14066 $srclang = 'bas';
14067 $famime = 'file-code';
14068 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
14069 $mime = 'text/plain';
14070 $imgmime = 'text.png';
14071 $srclang = 'c';
14072 $famime = 'file-code';
14073 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
14074 $mime = 'text/plain';
14075 $imgmime = 'text.png';
14076 $srclang = 'cpp';
14077 $famime = 'file-code';
14078 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
14079 $mime = 'text/plain';
14080 $imgmime = 'text.png';
14081 $srclang = 'cs';
14082 $famime = 'file-code';
14083 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
14084 $mime = 'text/plain';
14085 $imgmime = 'text.png';
14086 $srclang = 'h';
14087 $famime = 'file-code';
14088 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
14089 $mime = 'text/plain';
14090 $imgmime = 'text.png';
14091 $srclang = 'java';
14092 $famime = 'file-code';
14093 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
14094 $mime = 'text/plain';
14095 $imgmime = 'php.png';
14096 $srclang = 'php';
14097 $famime = 'file-code';
14098 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
14099 $mime = 'text/plain';
14100 $imgmime = 'php.png';
14101 $srclang = 'php';
14102 $famime = 'file-code';
14103 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
14104 $mime = 'text/plain';
14105 $imgmime = 'pl.png';
14106 $srclang = 'perl';
14107 $famime = 'file-code';
14108 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
14109 $mime = 'text/plain';
14110 $imgmime = 'text.png';
14111 $srclang = 'sql';
14112 $famime = 'file-code';
14113 } elseif (preg_match('/\.js$/i', $tmpfile)) {
14114 $mime = 'text/x-javascript';
14115 $imgmime = 'jscript.png';
14116 $srclang = 'js';
14117 $famime = 'file-code';
14118 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
14119 $mime = 'application/vnd.oasis.opendocument.presentation';
14120 $imgmime = 'ooffice.png';
14121 $famime = 'file-powerpoint';
14122 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
14123 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
14124 $imgmime = 'ooffice.png';
14125 $famime = 'file-excel';
14126 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
14127 $mime = 'application/vnd.oasis.opendocument.text';
14128 $imgmime = 'ooffice.png';
14129 $famime = 'file-word';
14130 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
14131 $mime = 'application/msaccess';
14132 $imgmime = 'mdb.png';
14133 $famime = 'file';
14134 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
14135 $mime = 'application/msword';
14136 $imgmime = 'doc.png';
14137 $famime = 'file-word';
14138 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
14139 $mime = 'application/msword';
14140 $imgmime = 'doc.png';
14141 $famime = 'file-word';
14142 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
14143 $mime = 'application/vnd.ms-excel';
14144 $imgmime = 'xls.png';
14145 $famime = 'file-excel';
14146 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
14147 $mime = 'application/vnd.ms-excel';
14148 $imgmime = 'xls.png';
14149 $famime = 'file-excel';
14150 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
14151 $mime = 'application/vnd.ms-excel';
14152 $imgmime = 'xls.png';
14153 $famime = 'file-excel';
14154 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
14155 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
14156 $imgmime = 'xls.png';
14157 $famime = 'file-excel';
14158 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
14159 $mime = 'application/vnd.ms-powerpoint';
14160 $imgmime = 'ppt.png';
14161 $famime = 'file-powerpoint';
14162 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
14163 $mime = 'application/x-mspowerpoint';
14164 $imgmime = 'ppt.png';
14165 $famime = 'file-powerpoint';
14166 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
14167 $mime = 'application/pdf';
14168 $imgmime = 'pdf.png';
14169 $famime = 'file-pdf';
14170 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
14171 $mime = 'text/x-bat';
14172 $imgmime = 'script.png';
14173 $srclang = 'dos';
14174 $famime = 'file-code';
14175 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
14176 $mime = 'text/x-sh';
14177 $imgmime = 'script.png';
14178 $srclang = 'bash';
14179 $famime = 'file-code';
14180 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
14181 $mime = 'text/x-ksh';
14182 $imgmime = 'script.png';
14183 $srclang = 'bash';
14184 $famime = 'file-code';
14185 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
14186 $mime = 'text/x-bash';
14187 $imgmime = 'script.png';
14188 $srclang = 'bash';
14189 $famime = 'file-code';
14190 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
14191 $mime = 'image/x-icon';
14192 $imgmime = 'image.png';
14193 $famime = 'file-image';
14194 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
14195 $mime = 'image/jpeg';
14196 $imgmime = 'image.png';
14197 $famime = 'file-image';
14198 } elseif (preg_match('/\.png$/i', $tmpfile)) {
14199 $mime = 'image/png';
14200 $imgmime = 'image.png';
14201 $famime = 'file-image';
14202 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
14203 $mime = 'image/gif';
14204 $imgmime = 'image.png';
14205 $famime = 'file-image';
14206 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
14207 $mime = 'image/bmp';
14208 $imgmime = 'image.png';
14209 $famime = 'file-image';
14210 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
14211 $mime = 'image/tiff';
14212 $imgmime = 'image.png';
14213 $famime = 'file-image';
14214 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
14215 $mime = 'image/svg+xml';
14216 $imgmime = 'image.png';
14217 $famime = 'file-image';
14218 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
14219 $mime = 'image/webp';
14220 $imgmime = 'image.png';
14221 $famime = 'file-image';
14222 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
14223 $mime = 'text/calendar';
14224 $imgmime = 'other.png';
14225 $famime = 'file-alt';
14226 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
14227 $mime = 'text/calendar';
14228 $imgmime = 'other.png';
14229 $famime = 'file-alt';
14230 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
14231 $mime = 'application/x-bittorrent';
14232 $imgmime = 'other.png';
14233 $famime = 'file-o';
14234 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
14235 $mime = 'audio';
14236 $imgmime = 'audio.png';
14237 $famime = 'file-audio';
14238 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
14239 $mime = 'video/mp4';
14240 $imgmime = 'video.png';
14241 $famime = 'file-video';
14242 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
14243 $mime = 'video/ogg';
14244 $imgmime = 'video.png';
14245 $famime = 'file-video';
14246 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
14247 $mime = 'video/webm';
14248 $imgmime = 'video.png';
14249 $famime = 'file-video';
14250 } elseif (preg_match('/\.avif$/i', $tmpfile)) {
14251 $mime = 'image/avif';
14252 $imgmime = 'image.png';
14253 $famime = 'file-image';
14254 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
14255 $mime = 'video/x-msvideo';
14256 $imgmime = 'video.png';
14257 $famime = 'file-video';
14258 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
14259 $mime = 'video/divx';
14260 $imgmime = 'video.png';
14261 $famime = 'file-video';
14262 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
14263 $mime = 'video/xvid';
14264 $imgmime = 'video.png';
14265 $famime = 'file-video';
14266 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
14267 $mime = 'video';
14268 $imgmime = 'video.png';
14269 $famime = 'file-video';
14270 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
14271 // application/xxx where zzz is zip, ...
14272 $mime = 'archive';
14273 $imgmime = 'archive.png';
14274 $famime = 'file-archive';
14275 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
14276 $mime = 'application/octet-stream';
14277 $imgmime = 'other.png';
14278 $famime = 'file-o';
14279 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
14280 $mime = 'library';
14281 $imgmime = 'library.png';
14282 $famime = 'file-o';
14283 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
14284 $mime = 'error';
14285 $imgmime = 'error.png';
14286 $famime = 'file-alt';
14287 }
14288
14289 if ($famime == 'file-o') {
14290 // file-o seems to not work in fontawesome 5
14291 $famime = 'file';
14292 }
14293
14294 // Return mimetype string
14295 switch ((int) $mode) {
14296 case 1:
14297 $tmp = explode('/', $mime);
14298 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
14299 case 2:
14300 return $imgmime;
14301 case 3:
14302 return $srclang;
14303 case 4:
14304 return $famime;
14305 }
14306 return $mime;
14307}
14308
14320function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
14321{
14322 global $conf, $db;
14323
14324 $tablename = preg_replace('/^' . preg_quote(MAIN_DB_PREFIX, '/') . '/', '', $tablename); // Clean name of table for backward compatibility.
14325
14326 $dictvalues = (isset($conf->cache['dictvalues_' . $tablename]) ? $conf->cache['dictvalues_' . $tablename] : null);
14327
14328 if (is_null($dictvalues)) {
14329 $dictvalues = array();
14330
14331 $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
14332 if ($checkentity) {
14333 $sql .= ' AND entity IN (0,' . getEntity($tablename) . ')';
14334 }
14335
14336 $resql = $db->query($sql);
14337 if ($resql) {
14338 while ($obj = $db->fetch_object($resql)) {
14339 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
14340 }
14341 } else {
14342 dol_print_error($db);
14343 }
14344
14345 $conf->cache['dictvalues_' . $tablename] = $dictvalues;
14346 }
14347
14348 if (!empty($dictvalues[$id])) {
14349 // Found
14350 $tmp = $dictvalues[$id];
14351 return (property_exists($tmp, $field) ? $tmp->$field : '');
14352 } else {
14353 // Not found
14354 return '';
14355 }
14356}
14357
14364function colorIsLight($stringcolor)
14365{
14366 $stringcolor = str_replace('#', '', $stringcolor);
14367 $res = -1;
14368 if (!empty($stringcolor)) {
14369 $res = 0;
14370 $tmp = explode(',', $stringcolor);
14371 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
14372 $r = $tmp[0];
14373 $g = $tmp[1];
14374 $b = $tmp[2];
14375 } else {
14376 $hexr = $stringcolor[0] . $stringcolor[1];
14377 $hexg = $stringcolor[2] . $stringcolor[3];
14378 $hexb = $stringcolor[4] . $stringcolor[5];
14379 $r = hexdec($hexr);
14380 $g = hexdec($hexg);
14381 $b = hexdec($hexb);
14382 }
14383 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
14384 if ($bright > 0.6) {
14385 $res = 1;
14386 }
14387 }
14388 return $res;
14389}
14390
14399function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
14400{
14401 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
14402 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
14403 if (empty($menuentry['enabled'])) {
14404 return 0; // Entry disabled by condition
14405 }
14406 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
14407 $tmploops = explode('|', $menuentry['module']);
14408 $found = 0;
14409 foreach ($tmploops as $tmploop) {
14410 if (in_array($tmploop, $listofmodulesforexternal)) {
14411 $found++;
14412 break;
14413 }
14414 }
14415 if (!$found) {
14416 return 0; // Entry is for menus all excluded to external users
14417 }
14418 }
14419 if (!$menuentry['perms'] && $type_user) {
14420 return 0; // No permissions and user is external
14421 }
14422 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
14423 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
14424 }
14425 if (!$menuentry['perms']) {
14426 return 2; // No permissions and user is external
14427 }
14428 return 1;
14429}
14430
14438function roundUpToNextMultiple($n, $x = 5)
14439{
14440 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
14441 return (int) $result;
14442}
14443
14455function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
14456{
14457 $csstouse = 'badge';
14458 $csstouse .= (!empty($mode) ? ' badge-' . $mode : '');
14459 $csstouse .= (!empty($type) ? ' badge-' . $type : '');
14460 $csstouse .= (empty($params['css']) ? '' : ' ' . $params['css']);
14461
14462 $attr = array(
14463 'class' => $csstouse
14464 );
14465
14466 if (empty($html)) {
14467 $html = $label;
14468 }
14469
14470 if (!empty($url)) {
14471 $attr['href'] = $url;
14472 }
14473
14474 if ($mode === 'dot') {
14475 $attr['class'] .= ' classfortooltip';
14476 $attr['title'] = $html;
14477 $attr['aria-label'] = $label;
14478 $html = '';
14479 }
14480
14481 // Override attr
14482 if (!empty($params['attr']) && is_array($params['attr'])) {
14483 foreach ($params['attr'] as $key => $value) {
14484 if ($key == 'class') {
14485 $attr['class'] .= ' ' . $value;
14486 } elseif ($key == 'classOverride') {
14487 $attr['class'] = $value;
14488 } else {
14489 $attr[$key] = $value;
14490 }
14491 }
14492 }
14493
14494 // TODO: add hook
14495
14496 // escape all attribute
14497 $attr = array_map('dolPrintHTMLForAttribute', $attr);
14498
14499 $TCompiledAttr = array();
14500 foreach ($attr as $key => $value) {
14501 $TCompiledAttr[] = $key . '="' . $value . '"';
14502 }
14503
14504 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
14505
14506 $tag = !empty($url) ? 'a' : 'span';
14507
14508 return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
14509}
14510
14511
14524function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
14525{
14526 global $conf;
14527
14528 $return = '';
14529 $dolGetBadgeParams = array();
14530
14531 if (!empty($params['badgeParams'])) {
14532 $dolGetBadgeParams = $params['badgeParams'];
14533 }
14534
14535 // TODO : add a hook
14536 if ($displayMode == 0) {
14537 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
14538 } elseif ($displayMode == 1) {
14539 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14540 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
14541 // Use status with images (for backward compatibility)
14542 $return = '';
14543 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '') . (!empty($html) ? $html : $statusLabel) . (in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
14544 $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>' : '');
14545
14546 // For small screen, we always use the short label instead of long label.
14547 if (!empty($conf->dol_optimize_smallscreen)) {
14548 if ($displayMode == 0) {
14549 $displayMode = 1;
14550 } elseif ($displayMode == 4) {
14551 $displayMode = 2;
14552 } elseif ($displayMode == 6) {
14553 $displayMode = 5;
14554 }
14555 }
14556
14557 // For backward compatibility. Image's filename are still in French, so we use this array to convert
14558 $statusImg = array(
14559 'status0' => 'statut0',
14560 'status1' => 'statut1',
14561 'status2' => 'statut2',
14562 'status3' => 'statut3',
14563 'status4' => 'statut4',
14564 'status5' => 'statut5',
14565 'status6' => 'statut6',
14566 'status7' => 'statut7',
14567 'status8' => 'statut8',
14568 'status9' => 'statut9'
14569 );
14570
14571 if (!empty($statusImg[$statusType])) {
14572 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
14573 } else {
14574 $htmlImg = img_picto($statusLabel, $statusType);
14575 }
14576
14577 if ($displayMode === 2) {
14578 $return = $htmlImg . ' ' . $htmlLabelShort;
14579 } elseif ($displayMode === 3) {
14580 $return = $htmlImg;
14581 } elseif ($displayMode === 4) {
14582 $return = $htmlImg . ' ' . $htmlLabel;
14583 } elseif ($displayMode === 5) {
14584 $return = $htmlLabelShort . ' ' . $htmlImg;
14585 } else { // $displayMode >= 6
14586 $return = $htmlLabel . ' ' . $htmlImg;
14587 }
14588 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
14589 // Use new badge
14590 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
14591
14592 $dolGetBadgeParams['attr']['class'] = 'badge-status';
14593 if (empty($dolGetBadgeParams['attr']['title'])) {
14594 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
14595 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
14596 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
14597 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
14598 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
14599 }
14600
14601 if ($displayMode == 3) {
14602 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
14603 } elseif ($displayMode === 5) {
14604 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
14605 } else {
14606 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
14607 }
14608 }
14609
14610 return $return;
14611}
14612
14613
14649function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
14650{
14651 global $hookmanager, $action, $object, $langs;
14652
14653 // If $url is an array, we must build a dropdown button or recursively iterate over each value
14654 if (is_array($url)) {
14655 // Loop on $url array to remove entries of disabled modules
14656 foreach ($url as $key => $subbutton) {
14657 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
14658 unset($url[$key]);
14659 }
14660 }
14661
14662 $out = '';
14663
14664 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
14665 foreach ($url as $button) {
14666 if (!empty($button['lang'])) {
14667 $langs->load($button['lang']);
14668 }
14669 $label = $langs->trans($button['label']);
14670 $text = $button['text'] ?? '';
14671 $actionType = $button['actionType'] ?? '';
14672 $tmpUrl = DOL_URL_ROOT . $button['url'] . (empty($params['backtopage']) ? '' : '&amp;backtopage=' . urlencode($params['backtopage']));
14673 $id = $button['id'] ?? '';
14674 $userRight = $button['perm'] ?? 1;
14675 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
14676
14677 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
14678 }
14679 return $out;
14680 }
14681
14682 if (count($url) > 1) {
14683 $out .= '<div class="dropdown inline-block dropdown-holder">';
14684 $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>';
14685 $out .= '<div class="dropdown-content">';
14686 foreach ($url as $subbutton) {
14687 if (!empty($subbutton['lang'])) {
14688 $langs->load($subbutton['lang']);
14689 }
14690
14691 if (!empty($subbutton['urlraw'])) {
14692 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14693 } else {
14694 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14695 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14696 }
14697
14698 $subbuttonparam = array();
14699 if (!empty($subbutton['attr'])) {
14700 $subbuttonparam['attr'] = $subbutton['attr'];
14701 }
14702 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
14703
14704 $out .= dolGetButtonAction($subbutton['text'] ?? '', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
14705 }
14706 $out .= "</div>";
14707 $out .= "</div>";
14708 } else {
14709 foreach ($url as $subbutton) { // Should loop on 1 record only
14710 if (!empty($subbutton['lang'])) {
14711 $langs->load($subbutton['lang']);
14712 }
14713
14714 if (!empty($subbutton['urlraw'])) {
14715 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
14716 } else {
14717 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
14718 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
14719 }
14720
14721 $label = $langs->trans($subbutton['label']);
14722 $text = $subbutton['text'] ?? '';
14723 if (empty($text)) {
14724 $text = $label;
14725 $label = '';
14726 }
14727
14728 $out .= dolGetButtonAction($label, $text, 'default', $tmpurl, '', $subbutton['perm'], $params);
14729 }
14730 }
14731
14732 return $out;
14733 }
14734
14735 // Here, $url is a simple link
14736 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
14737 $class = "dropdown-item";
14738 } else {
14739 $class = 'butAction';
14740 if ($actionType == 'edit') {
14741 $class = 'butAction butActionEdit';
14742 } elseif ($actionType == 'email') {
14743 $class = 'butAction butActionEmail';
14744 } elseif ($actionType == 'clone') {
14745 $class = 'butAction butActionClone';
14746 } elseif ($actionType == 'cancel') {
14747 $class = 'butAction butActionDelete';
14748 } elseif ($actionType == 'danger' || $actionType == 'delete') {
14749 $class = 'butAction butActionDelete';
14750 if (!empty($url) && strpos($url, 'token=') === false) {
14751 $url .= '&token=' . newToken();
14752 }
14753 }
14754 }
14755 $attr = array(
14756 'class' => $class,
14757 'href' => empty($url) ? '' : $url,
14758 'title' => $label
14759 );
14760
14761 if (empty($text)) {
14762 $text = $label;
14763 $attr['title'] = ''; // if html not set, using label on title is redundant
14764 } else {
14765 $attr['title'] = $label;
14766 $attr['aria-label'] = $label;
14767 }
14768
14769 if (empty($userRight) || $userRight < 0) {
14770 $attr['class'] = 'butActionRefused';
14771 $attr['href'] = '';
14772 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
14773 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '');
14774 $attr['title'] .= ((empty($userRight) && empty($label)) ? $langs->trans('NotEnoughPermissions') : '');
14775 }
14776
14777 if (!empty($id)) {
14778 $attr['id'] = $id;
14779 }
14780
14781 // Override attr
14782 if (!empty($params['attr']) && is_array($params['attr'])) {
14783 foreach ($params['attr'] as $key => $value) {
14784 if ($key == 'class') {
14785 $attr['class'] .= ' ' . $value;
14786 } elseif ($key == 'classOverride') {
14787 $attr['class'] = $value;
14788 } else {
14789 $attr[$key] = $value;
14790 }
14791 }
14792 }
14793
14794 // automatic add tooltip when title is detected
14795 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
14796 $attr['class'] .= ' classfortooltip';
14797 }
14798
14799 // Js Confirm button
14800 if ($userRight && !empty($params['confirm'])) {
14801 if (!is_array($params['confirm'])) {
14802 $params['confirm'] = array();
14803 }
14804
14805 if (empty($params['confirm']['url'])) {
14806 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
14807 }
14808
14809 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
14810 $attr['data-confirm-url'] = $params['confirm']['url'];
14811 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
14812 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
14813 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
14814 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
14815 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
14816 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
14817
14818 $attr['class'] .= ' butActionConfirm';
14819 }
14820
14821 if (isset($attr['href']) && empty($attr['href'])) {
14822 unset($attr['href']);
14823 }
14824
14825 // TODO replace this $TCompiledAttr generation block by commonHtmlAttributeBuilder like line below
14826 // $TCompiledAttr = commonHtmlAttributeBuilder($attr, $params['use_unsecured_unescapedattr'] ?? []);
14827 $TCompiledAttr = array();
14828 foreach ($attr as $key => $value) {
14829 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
14830 // Deprecated, forbidden.
14831 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
14832 } elseif ($key == 'href') {
14833 $value = dolPrintHTMLForAttributeUrl($value);
14834 } else {
14835 $value = dolPrintHTMLForAttribute($value);
14836 }
14837
14838 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14839 }
14840 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
14841
14842 $tag = !empty($attr['href']) ? 'a' : 'span';
14843
14844 $parameters = array(
14845 'TCompiledAttr' => $TCompiledAttr, // array
14846 'compiledAttributes' => $compiledAttributes, // string
14847 'attr' => $attr,
14848 'tag' => $tag,
14849 'label' => $label,
14850 'html' => $text,
14851 'actionType' => $actionType,
14852 'url' => $url,
14853 'id' => $id,
14854 'userRight' => $userRight,
14855 'params' => $params
14856 );
14857
14858 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14859 if ($reshook < 0) {
14860 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
14861 }
14862
14863 if (empty($reshook)) {
14864 if (dol_textishtml($text)) { // If content already HTML encoded
14865 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
14866 } else {
14867 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
14868 }
14869 } else {
14870 return $hookmanager->resPrint;
14871 }
14872}
14873
14904function commonHtmlAttributeBuilder($attr, array $unescapedAttr = [])
14905{
14906 $TCompiledAttr = array();
14907 if (empty($attr)) {
14908 return [];
14909 }
14910
14911 foreach ($attr as $key => $value) {
14912 // special boolean attributes case
14913 if (in_array($key, getListOfHtmlBooleanAttributes())) {
14914 if ($value) {
14915 $TCompiledAttr[$key] = $key;
14916 }
14917 continue;
14918 }
14919
14920 if (!empty($unescapedAttr) && in_array($key, $unescapedAttr)) {
14921 // Not recommended
14922 $value = dol_htmlentities((string) $value, ENT_QUOTES | ENT_SUBSTITUTE);
14923 } elseif ($key == 'href') {
14924 $value = dolPrintHTMLForAttributeUrl((string) $value);
14925 } else {
14926 $value = dolPrintHTMLForAttribute((string) $value);
14927 }
14928
14929 $TCompiledAttr[$key] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
14930 }
14931
14932 return $TCompiledAttr;
14933}
14934
14949function getListOfHtmlBooleanAttributes(): array
14950{
14951 return [
14952 // Input / Form
14953 'checked',
14954 'disabled',
14955 'readonly',
14956 'required',
14957 'autofocus',
14958 'multiple',
14959
14960 // Option
14961 'selected',
14962
14963 // Form / General
14964 'novalidate',
14965 'formnovalidate',
14966
14967 // Media
14968 'autoplay',
14969 'controls',
14970 'loop',
14971 'muted',
14972 'playsinline',
14973
14974 // Other elements
14975 'hidden',
14976 'open',
14977 'ismap',
14978 'reversed',
14979 'allowfullscreen',
14980 'itemscope',
14981 'nomodule',
14982 'defer',
14983 'async',
14984 'default',
14985 'inert',
14986 ];
14987}
14988
14989
14998function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
14999{
15000 if (empty($url)) {
15001 return '';
15002 }
15003
15004 $parsedUrl = parse_url($url);
15005 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
15006 return $url;
15007 }
15008
15009 if (!empty($parsedUrl['query'])) {
15010 // Use parse_str() function to parse the string passed via URL
15011 $urlQuery = '';
15012 parse_str($parsedUrl['query'], $urlQuery);
15013 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
15014 $url .= '&amp;backtopage=' . urlencode($params['backtopage']);
15015 }
15016 }
15017
15018 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
15019 $url = DOL_URL_ROOT . $url;
15020 }
15021
15022 return $url;
15023}
15024
15025
15032function dolGetButtonTitleSeparator($moreClass = "")
15033{
15034 return '<span class="button-title-separator ' . $moreClass . '" ></span>';
15035}
15036
15043function getFieldErrorIcon($fieldValidationErrorMsg)
15044{
15045 $out = '';
15046 if (!empty($fieldValidationErrorMsg)) {
15047 $out .= '<span class="field-error-icon classfortooltip" title="' . dol_escape_htmltag($fieldValidationErrorMsg, 1) . '" role="alert" >'; // role alert is used for accessibility
15048 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
15049 $out .= '</span>';
15050 }
15051
15052 return $out;
15053}
15054
15067function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
15068{
15069 global $langs, $user;
15070
15071 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
15072 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
15073 return '';
15074 }
15075 // Fix old picto fa-th-list to use fa-grid-vertical instead
15076 if ($iconClass == 'fa fa-th-list imgforviewmode') {
15077 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
15078 }
15079
15080 $class = 'btnTitle';
15081 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
15082 $class .= ' btnTitlePlus';
15083 }
15084 $useclassfortooltip = 1;
15085
15086 if (!empty($params['morecss'])) {
15087 $class .= ' ' . $params['morecss'];
15088 }
15089
15090 $attr = array(
15091 'class' => $class,
15092 'href' => empty($url) ? '' : $url
15093 );
15094
15095 if (!empty($helpText)) {
15096 $attr['title'] = $helpText;
15097 } elseif ($label) { // empty($attr['title']) &&
15098 $attr['title'] = $label;
15099 $useclassfortooltip = 0;
15100 }
15101
15102 if ($status == 2) {
15103 $attr['class'] .= ' btnTitleSelected';
15104 } elseif ($status <= 0) {
15105 $attr['class'] .= ' refused';
15106
15107 $attr['href'] = '';
15108
15109 if ($status == -1) { // disable
15110 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
15111 } elseif ($status == 0) { // Not enough permissions
15112 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
15113 }
15114 }
15115
15116 if (!empty($attr['title']) && $useclassfortooltip) {
15117 $attr['class'] .= ' classfortooltip';
15118 }
15119
15120 if (!empty($id)) {
15121 $attr['id'] = $id;
15122 }
15123
15124 // Override attr
15125 if (!empty($params['attr']) && is_array($params['attr'])) {
15126 foreach ($params['attr'] as $key => $value) {
15127 if ($key == 'class') {
15128 $attr['class'] .= ' ' . $value;
15129 } elseif ($key == 'classOverride') {
15130 $attr['class'] = $value;
15131 } else {
15132 $attr[$key] = $value;
15133 }
15134 }
15135 }
15136
15137 if (isset($attr['href']) && empty($attr['href'])) {
15138 unset($attr['href']);
15139 }
15140
15141 // TODO : add a hook
15142
15143 // Generate attributes with escapement
15144 $TCompiledAttr = array();
15145 foreach ($attr as $key => $value) {
15146 $TCompiledAttr[] = $key . '="' . dol_escape_htmltag($value) . '"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
15147 }
15148
15149 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
15150
15151 $tag = (empty($attr['href']) ? 'span' : 'a');
15152
15153 $button = '<' . $tag . ' ' . $compiledAttributes . '>';
15154 $button .= '<span class="' . $iconClass . ' valignmiddle btnTitle-icon"></span>';
15155 if (!empty($params['forcenohideoftext'])) {
15156 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label' . (empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '') . '">' . $label . '</span>';
15157 }
15158 $button .= '</' . $tag . '>';
15159
15160 return $button;
15161}
15162
15173function getElementProperties($elementType)
15174{
15175 global $conf, $db, $hookmanager;
15176
15177 $regs = array();
15178
15179 //$element_type='facture';
15180
15181 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
15182
15183 // Parse element/subelement
15184 $module = $elementType;
15185 $element = $elementType;
15186 $subelement = $elementType;
15187 $table_element = $elementType;
15188
15189 // If we ask a resource form external module (instead of default path)
15190 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
15191 $element = $subelement = $regs[1];
15192 $module = $regs[2];
15193 }
15194
15195 // If we ask a resource for a string with an element and a subelement
15196 // Example 'project_task'
15197 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
15198 $module = $element = $regs[1];
15199 $subelement = $regs[2];
15200 }
15201
15202 // Object lines will use parent classpath and module ref
15203 if (substr($elementType, -3) == 'det') {
15204 $module = preg_replace('/det$/', '', $element);
15205 $subelement = preg_replace('/det$/', '', $subelement);
15206 $classpath = $module . '/class';
15207 $classfile = $module;
15208 $classname = preg_replace('/det$/', 'Line', $element);
15209 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'supplier_order', 'commandefournisseur'))) {
15210 $classname = preg_replace('/det$/', 'Ligne', $element);
15211 }
15212 }
15213 // For compatibility and to work with non standard path
15214 if ($elementType == "action" || $elementType == "actioncomm") {
15215 $classpath = 'comm/action/class';
15216 $subelement = 'Actioncomm';
15217 $module = 'agenda';
15218 $table_element = 'actioncomm';
15219 } elseif ($elementType == 'cronjob') {
15220 $classpath = 'cron/class';
15221 $module = 'cron';
15222 $table_element = 'cron';
15223 } elseif ($elementType == 'adherent_type') {
15224 $classpath = 'adherents/class';
15225 $classfile = 'adherent_type';
15226 $module = 'adherent';
15227 $subelement = 'adherent_type';
15228 $classname = 'AdherentType';
15229 $table_element = 'adherent_type';
15230 } elseif ($elementType == 'bank_account') {
15231 $classpath = 'compta/bank/class';
15232 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
15233 $classfile = 'account';
15234 $classname = 'Account';
15235 } elseif ($elementType == 'bank_line') {
15236 $classpath = 'compta/bank/class';
15237 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
15238 $classfile = 'account';
15239 $classname = 'AccountLine';
15240 } elseif ($elementType == 'category') {
15241 $classpath = 'categories/class';
15242 $module = 'categorie';
15243 $subelement = 'categorie';
15244 $table_element = 'categorie';
15245 } elseif ($elementType == 'contact') {
15246 $classpath = 'contact/class';
15247 $classfile = 'contact';
15248 $module = 'societe';
15249 $subelement = 'contact';
15250 $table_element = 'socpeople';
15251 } elseif ($elementType == 'inventory') {
15252 $module = 'product';
15253 $classpath = 'product/inventory/class';
15254 } elseif ($elementType == 'inventoryline') {
15255 $module = 'product';
15256 $classpath = 'product/inventory/class';
15257 $table_element = 'inventorydet';
15258 $parent_element = 'inventory';
15259 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
15260 $module = 'stock';
15261 $classpath = 'product/stock/class';
15262 $classfile = 'entrepot';
15263 $classname = 'Entrepot';
15264 $table_element = 'entrepot';
15265 } elseif ($elementType == 'project') {
15266 $classpath = 'projet/class';
15267 $module = 'projet';
15268 $table_element = 'projet';
15269 } elseif ($elementType == 'project_task') {
15270 $classpath = 'projet/class';
15271 $module = 'projet';
15272 $subelement = 'task';
15273 $table_element = 'projet_task';
15274 } elseif ($elementType == 'mo') {
15275 $classpath = 'mrp/class';
15276 $module = 'mrp';
15277 $classfile = 'mo';
15278 $classname = 'Mo';
15279 $table_element = 'mrp_mo';
15280 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
15281 $classpath = 'compta/facture/class';
15282 $module = 'facture';
15283 $subelement = 'facture';
15284 $table_element = 'facture';
15285 } elseif ($elementType == 'facturedet') {
15286 $classpath = 'compta/facture/class';
15287 $classfile = 'facture';
15288 $classname = 'FactureLigne';
15289 $module = 'facture';
15290 $table_element = 'facturedet';
15291 $parent_element = 'facture';
15292 } elseif ($elementType == 'facturerec' || $elementType == 'facture_rec') {
15293 $classpath = 'compta/facture/class';
15294 $classfile = 'facture-rec';
15295 $module = 'facture';
15296 $classname = 'FactureRec';
15297 } elseif ($elementType == 'commande' || $elementType == 'order') {
15298 $classpath = 'commande/class';
15299 $module = 'commande';
15300 $subelement = 'commande';
15301 $table_element = 'commande';
15302 } elseif ($elementType == 'commandedet') {
15303 $classpath = 'commande/class';
15304 $classfile = 'commande';
15305 $classname = 'OrderLine';
15306 $module = 'commande';
15307 $table_element = 'commandedet';
15308 $parent_element = 'commande';
15309 } elseif ($elementType == 'propal') {
15310 $classpath = 'comm/propal/class';
15311 $table_element = 'propal';
15312 } elseif ($elementType == 'propaldet') {
15313 $classpath = 'comm/propal/class';
15314 $classfile = 'propal';
15315 $subelement = 'propaleligne';
15316 $module = 'propal';
15317 $table_element = 'propaldet';
15318 $parent_element = 'propal';
15319 } elseif ($elementType == 'shipping' || $elementType == 'shipment') {
15320 $classpath = 'expedition/class';
15321 $classfile = 'expedition';
15322 $classname = 'Expedition';
15323 $module = 'expedition';
15324 $table_element = 'expedition';
15325 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
15326 $classpath = 'expedition/class';
15327 $classfile = 'expedition';
15328 $classname = 'ExpeditionLigne';
15329 $module = 'expedition';
15330 $table_element = 'expeditiondet';
15331 $parent_element = 'expedition';
15332 } elseif ($elementType == 'delivery_note') {
15333 $classpath = 'delivery/class';
15334 $subelement = 'delivery';
15335 $module = 'expedition';
15336 } elseif ($elementType == 'delivery') {
15337 $classpath = 'delivery/class';
15338 $subelement = 'delivery';
15339 $module = 'expedition';
15340 } elseif ($elementType == 'deliverydet') {
15341 // @todo
15342 } elseif ($elementType == 'supplier_proposal') {
15343 $classpath = 'supplier_proposal/class';
15344 $module = 'supplier_proposal';
15345 $element = 'supplierproposal';
15346 $classfile = 'supplier_proposal';
15347 $subelement = 'supplierproposal';
15348 } elseif ($elementType == 'supplier_proposaldet') {
15349 $classpath = 'supplier_proposal/class';
15350 $module = 'supplier_proposal';
15351 $classfile = 'supplier_proposal';
15352 $classname = 'SupplierProposalLine';
15353 $table_element = 'supplier_proposaldet';
15354 $parent_element = 'supplier_proposal';
15355 } elseif ($elementType == 'contract') {
15356 $classpath = 'contrat/class';
15357 $module = 'contrat';
15358 $subelement = 'contrat';
15359 $table_element = 'contract';
15360 } elseif ($elementType == 'contratdet') {
15361 $classpath = 'contrat/class';
15362 $module = 'contrat';
15363 $table_element = 'contratdet';
15364 $parent_element = 'contrat';
15365 } elseif ($elementType == 'mailing') {
15366 $classpath = 'comm/mailing/class';
15367 $module = 'mailing';
15368 $classfile = 'mailing';
15369 $classname = 'Mailing';
15370 $subelement = '';
15371 } elseif ($elementType == 'member' || $elementType == 'adherent') {
15372 $classpath = 'adherents/class';
15373 $module = 'adherent';
15374 $subelement = 'adherent';
15375 $table_element = 'adherent';
15376 } elseif ($elementType == 'subscription') {
15377 $classpath = 'adherents/class';
15378 $classfile = 'subscription';
15379 $module = 'adherent';
15380 $subelement = 'subscription';
15381 $classname = 'Subscription';
15382 $table_element = 'subscription';
15383 } elseif ($elementType == 'usergroup') {
15384 $classpath = 'user/class';
15385 $module = 'user';
15386 } elseif ($elementType == 'mo' || $elementType == 'mrp') {
15387 $classpath = 'mrp/class';
15388 $classfile = 'mo';
15389 $classname = 'Mo';
15390 $module = 'mrp';
15391 $subelement = '';
15392 $table_element = 'mrp_mo';
15393 } elseif ($elementType == 'mrp_production') {
15394 $classpath = 'mrp/class';
15395 $classfile = 'mo';
15396 $classname = 'MoLine';
15397 $module = 'mrp';
15398 $subelement = '';
15399 $table_element = 'mrp_production';
15400 $parent_element = 'mo';
15401 } elseif ($elementType == 'cabinetmed_cons') {
15402 $classpath = 'cabinetmed/class';
15403 $module = 'cabinetmed';
15404 $subelement = 'cabinetmedcons';
15405 $table_element = 'cabinetmedcons';
15406 } elseif ($elementType == 'fichinter') {
15407 $classpath = 'fichinter/class';
15408 $module = 'ficheinter';
15409 $subelement = 'fichinter';
15410 $table_element = 'fichinter';
15411 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
15412 $classpath = 'resource/class';
15413 $module = 'resource';
15414 $subelement = 'dolresource';
15415 $table_element = 'resource';
15416 } elseif ($elementType == 'opensurvey_sondage') {
15417 $classpath = 'opensurvey/class';
15418 $module = 'opensurvey';
15419 $subelement = 'opensurveysondage';
15420 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' || $elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
15421 $classpath = 'fourn/class';
15422 $module = 'fournisseur';
15423 $classfile = 'fournisseur.commande';
15424 $element = 'order_supplier';
15425 $subelement = '';
15426 $classname = 'CommandeFournisseur';
15427 $table_element = 'commande_fournisseur';
15428 } elseif ($elementType == 'commande_fournisseurdet') {
15429 $classpath = 'fourn/class';
15430 $module = 'fournisseur';
15431 $classfile = 'fournisseur.commande';
15432 $element = 'commande_fournisseurdet';
15433 $subelement = '';
15434 $classname = 'CommandeFournisseurLigne';
15435 $table_element = 'commande_fournisseurdet';
15436 $parent_element = 'commande_fournisseur';
15437 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
15438 $classpath = 'fourn/class';
15439 $module = 'fournisseur';
15440 $classfile = 'fournisseur.facture';
15441 $element = 'invoice_supplier';
15442 $subelement = '';
15443 $classname = 'FactureFournisseur';
15444 $table_element = 'facture_fourn';
15445 } elseif ($elementType == 'facture_fourn_det') {
15446 $classpath = 'fourn/class';
15447 $module = 'fournisseur';
15448 $classfile = 'fournisseur.facture';
15449 $element = 'facture_fourn_det';
15450 $subelement = '';
15451 $classname = 'SupplierInvoiceLine';
15452 $table_element = 'facture_fourn_det';
15453 $parent_element = 'invoice_supplier';
15454 } elseif ($elementType == "service") {
15455 $classpath = 'product/class';
15456 $subelement = 'product';
15457 $table_element = 'product';
15458 } elseif ($elementType == 'salary') {
15459 $classpath = 'salaries/class';
15460 $module = 'salaries';
15461 } elseif ($elementType == 'payment_salary') {
15462 $classpath = 'salaries/class';
15463 $classfile = 'paymentsalary';
15464 $classname = 'PaymentSalary';
15465 $module = 'salaries';
15466 } elseif ($elementType == 'productlot') {
15467 $module = 'productbatch';
15468 $classpath = 'product/stock/class';
15469 $classfile = 'productlot';
15470 $classname = 'Productlot';
15471 $element = 'productlot';
15472 $subelement = '';
15473 $table_element = 'product_lot';
15474 } elseif ($elementType == 'societeaccount') {
15475 $classpath = 'societe/class';
15476 $classfile = 'societeaccount';
15477 $classname = 'SocieteAccount';
15478 $module = 'societe';
15479 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
15480 $classpath = 'website/class';
15481 $classfile = 'websitepage';
15482 $classname = 'Websitepage';
15483 $module = 'website';
15484 $subelement = 'websitepage';
15485 $table_element = 'website_page';
15486 } elseif ($elementType == 'fiscalyear') {
15487 $classpath = 'core/class';
15488 $module = 'accounting';
15489 $subelement = 'fiscalyear';
15490 } elseif ($elementType == 'chargesociales') {
15491 $classpath = 'compta/sociales/class';
15492 $module = 'tax';
15493 $table_element = 'chargesociales';
15494 } elseif ($elementType == 'tva') {
15495 $classpath = 'compta/tva/class';
15496 $module = 'tax';
15497 $subdir = '/vat';
15498 $table_element = 'tva';
15499 } elseif ($elementType == 'emailsenderprofile') {
15500 $module = '';
15501 $classpath = 'core/class';
15502 $classfile = 'emailsenderprofile';
15503 $classname = 'EmailSenderProfile';
15504 $table_element = 'c_email_senderprofile';
15505 $subelement = '';
15506 } elseif ($elementType == 'conferenceorboothattendee') {
15507 $classpath = 'eventorganization/class';
15508 $classfile = 'conferenceorboothattendee';
15509 $classname = 'ConferenceOrBoothAttendee';
15510 $module = 'eventorganization';
15511 } elseif ($elementType == 'conferenceorbooth') {
15512 $classpath = 'eventorganization/class';
15513 $classfile = 'conferenceorbooth';
15514 $classname = 'ConferenceOrBooth';
15515 $module = 'eventorganization';
15516 } elseif ($elementType == 'ccountry') {
15517 $module = '';
15518 $classpath = 'core/class';
15519 $classfile = 'ccountry';
15520 $classname = 'Ccountry';
15521 $table_element = 'c_country';
15522 $subelement = '';
15523 } elseif ($elementType == 'ecmfiles') {
15524 $module = 'ecm';
15525 $classpath = 'ecm/class';
15526 $classfile = 'ecmfiles';
15527 $classname = 'Ecmfiles';
15528 $table_element = 'ecmfiles';
15529 $subelement = '';
15530 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
15531 $module = 'knowledgemanagement';
15532 $classpath = 'knowledgemanagement/class';
15533 $classfile = 'knowledgerecord';
15534 $classname = 'KnowledgeRecord';
15535 $table_element = 'knowledgemanagement_knowledgerecord';
15536 $subelement = '';
15537 } elseif ($elementType == 'customer') {
15538 $module = 'societe';
15539 $classpath = 'societe/class';
15540 $classfile = 'client';
15541 $classname = 'Client';
15542 $table_element = 'societe';
15543 $subelement = '';
15544 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
15545 $module = 'societe';
15546 $classpath = 'fourn/class';
15547 $classfile = 'fournisseur';
15548 $classname = 'Fournisseur';
15549 $table_element = 'societe';
15550 $subelement = '';
15551 } elseif ($elementType == 'recruitmentcandidature') {
15552 $module = 'recruitment';
15553 $classfile = 'recruitmentcandidature';
15554 $classpath = 'recruitment/class';
15555 $classname = 'RecruitmentCandidature';
15556 $subelement = 'recruitmentcandidature';
15557 $subdir = '/recruitmentcandidature';
15558 } elseif ($elementType == 'recruitmentjobposition') {
15559 $module = 'recruitment';
15560 $classfile = 'recruitmentjobposition';
15561 $classpath = 'recruitment/class';
15562 $classname = 'RecruitmentJobPosition';
15563 $subelement = 'recruitmentjobposition';
15564 $subdir = '/recruitmentjobposition';
15565 }
15566
15567
15568 if (empty($classfile)) {
15569 $classfile = strtolower($subelement);
15570 }
15571 if (empty($classname)) {
15572 $classname = ucfirst($subelement);
15573 }
15574 if (empty($classpath)) {
15575 $classpath = $module . '/class';
15576 }
15577
15578 //print 'getElementProperties subdir='.$subdir;
15579
15580 // Set dir_output
15581 if ($module && isset($conf->$module)) { // The generic case
15582 if (!empty($conf->$module->multidir_output[$conf->entity])) {
15583 $dir_output = $conf->$module->multidir_output[$conf->entity];
15584 } elseif (!empty($conf->$module->output[$conf->entity])) {
15585 $dir_output = $conf->$module->output[$conf->entity];
15586 } elseif (!empty($conf->$module->dir_output)) {
15587 $dir_output = $conf->$module->dir_output;
15588 }
15589 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
15590 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
15591 } elseif (!empty($conf->$module->temp[$conf->entity])) {
15592 $dir_temp = $conf->$module->temp[$conf->entity];
15593 } elseif (!empty($conf->$module->dir_temp)) {
15594 $dir_temp = $conf->$module->dir_temp;
15595 }
15596 }
15597
15598 // Overwrite value for special cases
15599 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
15600 $dir_output = $conf->fournisseur->commande->dir_output;
15601 $dir_temp = $conf->fournisseur->commande->dir_temp;
15602 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
15603 $dir_output = $conf->fournisseur->facture->dir_output;
15604 $dir_temp = $conf->fournisseur->facture->dir_temp;
15605 }
15606 $dir_output .= $subdir;
15607 $dir_temp .= $subdir;
15608
15609 $elementProperties = array(
15610 'module' => $module,
15611 'element' => $element,
15612 'table_element' => $table_element,
15613 'subelement' => $subelement,
15614 'classpath' => $classpath,
15615 'classfile' => $classfile,
15616 'classname' => $classname,
15617 'dir_output' => $dir_output,
15618 'dir_temp' => $dir_temp,
15619 'parent_element' => $parent_element,
15620 );
15621
15622
15623 // Add hook
15624 if (!is_object($hookmanager)) {
15625 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
15626 $hookmanager = new HookManager($db);
15627 }
15628 $hookmanager->initHooks(array('elementproperties'));
15629
15630
15631 // Hook params
15632 $parameters = array(
15633 'elementType' => $elementType,
15634 'elementProperties' => $elementProperties
15635 );
15636
15637 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
15638
15639 if ($reshook) {
15640 $elementProperties = $hookmanager->resArray;
15641 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for security against misconfigured external modules
15642 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
15643 }
15644
15645 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
15646 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
15647 unset($hookmanager->contextarray[$key]);
15648 }
15649
15650 return $elementProperties;
15651}
15652
15666function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
15667{
15668 global $db, $conf;
15669
15670 $ret = 0;
15671
15672 $element_prop = getElementProperties($element_type);
15673 //var_dump($element_prop); exit;
15674
15675 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
15676 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
15677 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
15678 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
15679 // of service and we will return properties of a product.
15680 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
15681 } elseif ($element_prop['module'] == 'societeaccount') {
15682 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
15683 } else {
15684 $ismodenabled = isModEnabled($element_prop['module']);
15685 }
15686 //var_dump('element_type='.$element_type);
15687 //var_dump($element_prop);
15688 //var_dump($element_prop['module'].' '.$ismodenabled);
15689 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
15690 if ($useCache === 1 && $element_id > 0
15691 && !empty($conf->cache['fetchObjectByElement'][$element_type])
15692 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15693 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
15694 ) {
15695 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
15696 }
15697
15698 dol_include_once('/' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15699
15700 if (class_exists($element_prop['classname'])) {
15701 $className = $element_prop['classname'];
15702 $objecttmp = new $className($db);
15703 '@phan-var-force CommonObject $objecttmp';
15706 if ($element_id > 0 || !empty($element_ref)) {
15707 $ret = $objecttmp->fetch($element_id, $element_ref);
15708 if ($ret >= 0) {
15709 if (empty($objecttmp->module)) {
15710 $objecttmp->module = $element_prop['module'];
15711 }
15712
15713 if ($useCache > 0) {
15714 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
15715 $conf->cache['fetchObjectByElement'][$element_type] = [];
15716 }
15717
15718 // Manage cache limit
15719 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
15720 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
15721 }
15722
15723 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
15724 }
15725
15726 return $objecttmp;
15727 }
15728 } else {
15729 return $objecttmp; // returned an object without fetch
15730 }
15731 } else {
15732 dol_syslog($element_prop['classname'] . ' doesn\'t exists in /' . $element_prop['classpath'] . '/' . $element_prop['classfile'] . '.class.php');
15733 return -1;
15734 }
15735 }
15736
15737 return $ret;
15738}
15739
15745function getExecutableContent()
15746{
15747 $arrayofregexextension = array(
15748 'htm',
15749 'html',
15750 'shtml',
15751 'js',
15752 'phar',
15753 'php',
15754 'php3',
15755 'php4',
15756 'php5',
15757 'phtml',
15758 'pht',
15759 'pl',
15760 'py',
15761 'cgi',
15762 'ksh',
15763 'sh',
15764 'shtml',
15765 'bash',
15766 'bat',
15767 'cmd',
15768 'wpk',
15769 'exe',
15770 'dmg',
15771 'appimage'
15772 );
15773
15774 return $arrayofregexextension;
15775}
15776
15783function isAFileWithExecutableContent($filename)
15784{
15785 $arrayofregexextension = getExecutableContent();
15786
15787 foreach ($arrayofregexextension as $fileextension) {
15788 if (preg_match('/\.' . preg_quote($fileextension, '/') . '$/i', $filename)) {
15789 return true;
15790 }
15791 }
15792
15793 return false;
15794}
15795
15803function newToken()
15804{
15805 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
15806}
15807
15815function currentToken()
15816{
15817 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
15818}
15819
15825function getNonce()
15826{
15827 global $conf;
15828
15829 if (empty($conf->cache['nonce'])) {
15830 include_once DOL_DOCUMENT_ROOT . '/core/lib/security.lib.php';
15831 $conf->cache['nonce'] = dolGetRandomBytes(8);
15832 }
15833
15834 return $conf->cache['nonce'];
15835}
15836
15837
15851function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
15852{
15853 global $langs;
15854
15855 print '<div class="div-table-responsive-no-min">';
15856 print '<table class="noborder centpercent">';
15857 print '<tr class="liste_titre">';
15858
15859 print ($emptyColumns < 1) ? '<th>' : '<th colspan="' . ($emptyColumns + 1) . '">';
15860
15861 print '<span class="valignmiddle">' . $langs->trans($header) . '</span>';
15862
15863 if (!empty($link)) {
15864 if (!empty($arguments)) {
15865 print '<a href="' . DOL_URL_ROOT . '/' . $link . '?' . $arguments . '">';
15866 } else {
15867 print '<a href="' . DOL_URL_ROOT . '/' . $link . '">';
15868 }
15869 }
15870
15871 if ($number > -1) {
15872 print '<span class="badge marginleftonlyshort">' . $number . '</span>';
15873 } elseif (!empty($link)) {
15874 print '<span class="badge marginleftonlyshort">...</span>';
15875 }
15876
15877 if (!empty($link)) {
15878 print '</a>';
15879 }
15880
15881 print '</th>';
15882
15883 if ($number < 0 && !empty($link)) {
15884 print '<th class="right">';
15885 print '</th>';
15886 }
15887
15888 print '</tr>';
15889}
15890
15899function finishSimpleTable($addLineBreak = false)
15900{
15901 print '</table>';
15902 print '</div>';
15903
15904 if ($addLineBreak) {
15905 print '<br>';
15906 }
15907}
15908
15920function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
15921{
15922 global $langs;
15923
15924 if ($num === 0) {
15925 print '<tr class="oddeven">';
15926 print '<td colspan="' . $tableColumnCount . '"><span class="opacitymedium">' . $langs->trans($noneWord) . '</span></td>';
15927 print '</tr>';
15928 return;
15929 }
15930
15931 if ($nbofloop === 0) {
15932 // don't show a summary line
15933 return;
15934 }
15935
15936 /* Case already handled above, commented to satisfy phpstan.
15937 if ($num === 0) {
15938 $colspan = $tableColumnCount;
15939 } else
15940 */
15941 if ($num > $nbofloop) {
15942 $colspan = $tableColumnCount;
15943 } else {
15944 $colspan = $tableColumnCount - 1;
15945 }
15946
15947 if ($extraRightColumn) {
15948 $colspan--;
15949 }
15950
15951 print '<tr class="liste_total">';
15952
15953 if ($nbofloop > 0 && $num > $nbofloop) {
15954 print '<td colspan="' . $colspan . '" class="right">' . $langs->trans("XMoreLines", ($num - $nbofloop)) . '</td>';
15955 } else {
15956 print '<td colspan="' . $colspan . '" class="right"> ' . $langs->trans("Total") . '</td>';
15957 print '<td class="right centpercent">' . price($total) . '</td>';
15958 }
15959
15960 if ($extraRightColumn) {
15961 print '<td></td>';
15962 }
15963
15964 print '</tr>';
15965}
15966
15975function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
15976{
15977 if ($method == -1) {
15978 $method = 0;
15979 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
15980 $method = 1;
15981 }
15982 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
15983 $method = 2;
15984 }
15985 }
15986
15987 // Be sure we don't have output buffering enabled to have readfile working correctly
15988 $level = ob_get_level();
15989 ob_start();
15990 while (ob_get_level() > $level) {
15991 ob_end_clean();
15992 }
15993
15994 // Solution 0
15995 if ($method == 0) {
15996 readfile($fullpath_original_file_osencoded);
15997 } elseif ($method == 1) {
15998 // Solution 1
15999 $handle = fopen($fullpath_original_file_osencoded, "rb");
16000 while (!feof($handle)) {
16001 print fread($handle, 8192);
16002 }
16003 fclose($handle);
16004 } elseif ($method == 2) {
16005 // Solution 2
16006 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
16007 $handle2 = fopen("php://output", "wb");
16008 stream_copy_to_stream($handle1, $handle2);
16009 fclose($handle1);
16010 fclose($handle2);
16011 }
16012}
16013
16023function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
16024{
16025 global $langs;
16026
16027 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
16028
16029 $result = '<span class="clipboardCP' . ($showonlyonhover ? ' clipboardCPShowOnHover valignmiddle' : '') . '">';
16030 if ($texttoshow === 'none') {
16031 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
16032 $result .= '<span class="clipboardCPValueToPrint"></span>';
16033 } elseif ($texttoshow) {
16034 $result .= '<' . $tag . ' class="clipboardCPValue hidewithsize">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
16035 $result .= '<span class="clipboardCPValueToPrint">' . dol_escape_htmltag($texttoshow, 1, 1) . '</span>';
16036 } else {
16037 $result .= '<' . $tag . ' class="clipboardCPValue">' . dol_escape_htmltag($valuetocopy, 1, 1) . '</' . $tag . '>';
16038 }
16039 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="' . dolPrintHTML($langs->trans("ClickToCopyToClipboard")) . '"></span>';
16040 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
16041 $result .= '<span class="clipboardCPText"></span>';
16042 $result .= '</span>';
16043
16044 return $result;
16045}
16046
16047
16055function jsonOrUnserialize($stringtodecode, $assoc = true)
16056{
16057 $result = json_decode($stringtodecode, $assoc);
16058 if ($result === null) {
16059 $result = unserialize($stringtodecode, ['allowed_classes' => false]); // For backward compatibility. Is no more used in recent versions.
16060 }
16061
16062 return $result;
16063}
16064
16065
16082function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
16083{
16084 global $db, $user;
16085
16086 if (is_null($filter) || !is_string($filter) || $filter === '') {
16087 return '';
16088 }
16089 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
16090 $filter = '(' . $filter . ')';
16091 }
16092
16093 $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'
16094 $firstandlastparenthesis = 0;
16095
16096 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
16097 if ($noerror) {
16098 return '1 = 2';
16099 } else {
16100 return 'Filter syntax error - ' . $errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
16101 }
16102 }
16103
16104 // Test the filter syntax
16105 $t = preg_replace_callback('/' . $regexstring . '/i', 'dolForgeDummyCriteriaCallback', $filter);
16106 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
16107 // If the string result contains something else than '()', the syntax was wrong
16108
16109 if (preg_match('/[^\‍(\‍)]/', $t)) {
16110 $tmperrorstr = 'Bad syntax of the search string';
16111 $errorstr = 'Bad syntax of the search string: ' . $filter;
16112 if ($noerror) {
16113 return '1 = 2';
16114 } else {
16115 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - " . $errorstr, LOG_WARNING);
16116 return 'Filter error - ' . $tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
16117 }
16118 }
16119
16120 $ret = ($noand ? "" : " AND ") . ($nopar ? "" : '(') . preg_replace_callback('/' . $regexstring . '/i', 'dolForgeSQLCriteriaCallback', $filter) . ($nopar ? "" : ')');
16121
16122 if (is_object($db)) {
16123 $ret = str_replace('__NOW__', "'" . $db->idate(dol_now()) . "'", $ret);
16124 }
16125 if (is_object($user)) {
16126 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
16127 }
16128
16129 return $ret;
16130}
16131
16139function dolForgeExplodeAnd($sqlfilters)
16140{
16141 $arrayofandtags = array();
16142 $nbofchars = dol_strlen($sqlfilters);
16143
16144 $error = '';
16145 $parenthesislevel = 0;
16146 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
16147 if (!$result) {
16148 return array();
16149 }
16150 if ($parenthesislevel >= 1) {
16151 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
16152 }
16153
16154 $i = 0;
16155 $s = '';
16156 $countparenthesis = 0;
16157 while ($i < $nbofchars) {
16158 $char = dol_substr($sqlfilters, $i, 1);
16159
16160 if ($char == '(') {
16161 $countparenthesis++;
16162 } elseif ($char == ')') {
16163 $countparenthesis--;
16164 }
16165
16166 if ($countparenthesis == 0) {
16167 $char2 = dol_substr($sqlfilters, $i + 1, 1);
16168 $char3 = dol_substr($sqlfilters, $i + 2, 1);
16169 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
16170 // We found a AND
16171 $s = trim($s);
16172 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
16173 $s = '(' . $s . ')';
16174 }
16175 $arrayofandtags[] = $s;
16176 $s = '';
16177 $i += 2;
16178 } else {
16179 $s .= $char;
16180 }
16181 } else {
16182 $s .= $char;
16183 }
16184 $i++;
16185 }
16186 if ($s) {
16187 $s = trim($s);
16188 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
16189 $s = '(' . $s . ')';
16190 }
16191 $arrayofandtags[] = $s;
16192 }
16193
16194 return $arrayofandtags;
16195}
16196
16206function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
16207{
16208 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
16209 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
16210 $tmp = $sqlfilters;
16211
16212 $nb = dol_strlen($tmp);
16213 $counter = 0;
16214 $parenthesislevel = 0;
16215
16216 $error = '';
16217
16218 $i = 0;
16219 while ($i < $nb) {
16220 $char = dol_substr($tmp, $i, 1);
16221
16222 if ($char == '(') {
16223 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
16224 // We open a parenthesis and it is the first char
16225 $parenthesislevel++;
16226 }
16227 $counter++;
16228 } elseif ($char == ')') {
16229 $nbcharremaining = ($nb - $i - 1);
16230 if ($nbcharremaining >= $counter) {
16231 $parenthesislevel = min($parenthesislevel, $counter - 1);
16232 }
16233 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
16234 $parenthesislevel = $counter;
16235 }
16236 $counter--;
16237 }
16238
16239 if ($counter < 0) {
16240 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
16241 $parenthesislevel = 0;
16242 dol_syslog($error, LOG_WARNING);
16243 return false;
16244 }
16245
16246 $i++;
16247 }
16248
16249 if ($counter > 0) {
16250 $error = "Wrong balance of parenthesis in sqlfilters=" . $sqlfilters;
16251 $parenthesislevel = 0;
16252 dol_syslog($error, LOG_WARNING);
16253 return false;
16254 }
16255
16256 return true;
16257}
16258
16266function dolForgeDummyCriteriaCallback($matches)
16267{
16268 //dol_syslog("Convert matches ".$matches[1]);
16269 if (empty($matches[1])) {
16270 return '';
16271 }
16272 $tmp = explode(':', $matches[1]);
16273 if (count($tmp) < 3) {
16274 return '';
16275 }
16276
16277 return '()'; // An empty criteria
16278}
16279
16289function dolForgeSQLCriteriaCallback($matches)
16290{
16291 global $db;
16292
16293 //dol_syslog("Convert matches ".$matches[1]);
16294 if (empty($matches[1])) {
16295 return '';
16296 }
16297 $tmp = explode(':', $matches[1], 3);
16298 if (count($tmp) < 3) {
16299 return '';
16300 }
16301
16302 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
16303
16304 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
16305
16306 $realOperator = [
16307 'NOTLIKE' => 'NOT LIKE',
16308 'ISNOT' => 'IS NOT',
16309 'NOTIN' => 'NOT IN',
16310 '!=' => '<>',
16311 ];
16312
16313 if (array_key_exists($operator, $realOperator)) {
16314 $operator = $realOperator[$operator];
16315 }
16316
16317 $tmpescaped = $tmp[2];
16318
16319 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
16320
16321 $regbis = array();
16322
16323 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID/code/field only (or subrequest if $dolibarr_allow_unsecured_select_in_extrafields_filter not enabled)
16324 global $dolibarr_allow_unsecured_select_in_extrafields_filter;
16325
16326 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
16327 $tmpescaped2 = '(';
16328 // Explode and sanitize each element in list
16329 $tmpelemarray = explode(',', $tmpescaped);
16330 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
16331 $reg = array();
16332 $tmpelem = trim($tmpelem);
16333 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
16334 $tmpelemarray[$tmpkey] = "'" . $db->escape($db->sanitize($reg[1], 2, 1, 1, 1)) . "'";
16335 } elseif (ctype_digit((string) $tmpelem)) { // if only 0-9 chars, no .
16336 $tmpelemarray[$tmpkey] = (int) $tmpelem;
16337 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
16338 $tmpelemarray[$tmpkey] = (float) $tmpelem;
16339 } elseif (!empty($dolibarr_allow_unsecured_select_in_extrafields_filter)) {
16340 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest (should be removed in a future as it allows blind SQL injection)
16341 } else {
16342 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
16343 }
16344 }
16345 $tmpescaped2 .= implode(',', $tmpelemarray);
16346 $tmpescaped2 .= ')';
16347
16348 $tmpescaped = $tmpescaped2;
16349 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
16350 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
16351 $tmpescaped = $regbis[1];
16352 }
16353 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
16354 $tmpescaped = "'" . $db->escape($tmpescaped) . "'"; // We do not escape the _ and % so the LIKE will work as expected
16355 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
16356 // TODO Retrieve type of field for $operand field name.
16357 // So we can complete format. For example we could complete a year with month and day.
16358 $tmpescaped = "'" . $db->escape($regbis[1]) . "'";
16359 } else {
16360 if (strtoupper($tmpescaped) == 'NULL') {
16361 $tmpescaped = 'NULL';
16362 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
16363 $tmpescaped = (int) $tmpescaped;
16364 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
16365 $tmpescaped = (float) $tmpescaped;
16366 } else {
16367 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
16368 }
16369 }
16370
16371 return '(' . $db->escape($operand) . ' ' . strtoupper($operator) . ' ' . $tmpescaped . ')';
16372}
16373
16374
16384function getTimelineIcon($actionstatic, &$histo, $key)
16385{
16386 dol_syslog('getTimelineIcon::begin', LOG_DEBUG);
16387 global $langs;
16388
16389 $out = '<!-- timeline icon -->' . "\n";
16390 $iconClass = 'fa fa-comments';
16391 $img_picto = '';
16392 $colorClass = '';
16393 $pictoTitle = '';
16394
16395 if ($histo[$key]['percent'] == -1) {
16396 $colorClass = 'timeline-icon-not-applicble';
16397 $pictoTitle = $langs->trans('StatusNotApplicable');
16398 } elseif ($histo[$key]['percent'] == 0) {
16399 $colorClass = 'timeline-icon-todo';
16400 $pictoTitle = $langs->trans('StatusActionToDo') . ' (0%)';
16401 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
16402 $colorClass = 'timeline-icon-in-progress';
16403 $pictoTitle = $langs->trans('StatusActionInProcess') . ' (' . $histo[$key]['percent'] . '%)';
16404 } elseif ($histo[$key]['percent'] >= 100) {
16405 $colorClass = 'timeline-icon-done';
16406 $pictoTitle = $langs->trans('StatusActionDone') . ' (100%)';
16407 }
16408
16409 if ($actionstatic->code == 'AC_TICKET_CREATE') {
16410 $iconClass = 'fa fa-ticket';
16411 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
16412 $iconClass = 'fa fa-pencilxxx';
16413 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16414 $iconClass = 'fa fa-comments';
16415 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
16416 $iconClass = 'fa fa-mask';
16417 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16418 if ($actionstatic->type_picto) {
16419 $img_picto = img_picto('', $actionstatic->type_picto);
16420 } else {
16421 if ($actionstatic->type_code == 'AC_RDV') {
16422 $iconClass = 'fa fa-handshake';
16423 } elseif ($actionstatic->type_code == 'AC_TEL') {
16424 $iconClass = 'fa fa-phone';
16425 } elseif ($actionstatic->type_code == 'AC_FAX') {
16426 $iconClass = 'fa fa-fax';
16427 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
16428 $iconClass = 'fa fa-envelope';
16429 } elseif ($actionstatic->type_code == 'AC_INT') {
16430 $iconClass = 'fa fa-shipping-fast';
16431 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
16432 $iconClass = 'fa fa-robot';
16433 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
16434 $iconClass = 'fa fa-robot';
16435 }
16436 }
16437 }
16438
16439 $out .= '<i class="' . $iconClass . ' ' . $colorClass . '" title="' . $pictoTitle . '">' . $img_picto . '</i>' . "\n";
16440 return $out;
16441}
16442
16450{
16451 global $db;
16452
16453 $documents = array();
16454
16455 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
16456 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'ecm_files ecm';
16457 $sql .= " WHERE ecm.filepath = 'agenda/" . ((int) $object->id) . "'";
16458 //$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
16459 $sql .= ' OR ecm.agenda_id = ' . (int) $object->id;
16460 $sql .= ' ORDER BY ecm.position ASC';
16461
16462 $resql = $db->query($sql);
16463 if ($resql) {
16464 if ($db->num_rows($resql)) {
16465 while ($obj = $db->fetch_object($resql)) {
16466 $documents[$obj->id] = $obj;
16467 }
16468 }
16469 }
16470
16471 return $documents;
16472}
16473
16474
16492function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
16493{
16494 dol_syslog('show_actions_messaging::begin', LOG_DEBUG);
16495 global $user, $conf;
16496 global $form;
16497
16498 global $param, $massactionbutton;
16499
16500 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16501
16502 // Check parameters
16503 if (!is_object($filterobj) && !is_object($objcon)) {
16504 dol_print_error(null, 'BadParameter');
16505 }
16506
16507 $histo = array();
16508 '@phan-var-force array<int,array{type:string,tododone:string,id:string,datestart:int|string,dateend:int|string,fulldayevent:int,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';
16509
16510 $numaction = 0;
16511 $now = dol_now();
16512
16513 $sortfield_list = explode(',', $sortfield);
16514 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
16515 $sanitized_sortfield_new_list = array();
16516 foreach ($sortfield_list as $sortfield_value) {
16517 $sanitized_sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)]; //@phan-suppress-current-line SqlInjection
16518 }
16519 $sanitized_sortfield_new = implode(',', $sanitized_sortfield_new_list);
16520
16521 $sql = null;
16522 $sql2 = null;
16523
16524 if (isModEnabled('agenda')) {
16525 // Search histo on actioncomm
16526 if (is_object($objcon) && $objcon->id > 0) {
16527 $sql = "SELECT DISTINCT a.id, a.label as label,";
16528 } else {
16529 $sql = "SELECT a.id, a.label as label,";
16530 }
16531 $sql .= " a.datep as dp,";
16532 $sql .= " a.note as message,";
16533 $sql .= " a.datep2 as dp2,";
16534 $sql .= " a.percent as percent, 'action' as type,";
16535 $sql .= " a.fk_element, a.elementtype,";
16536 $sql .= " a.fk_contact, a.fulldayevent,";
16537 $sql .= " a.email_from as msg_from,";
16538 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
16539 $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";
16540 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16541 $sql .= ", sp.lastname, sp.firstname";
16542 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16543 $sql .= ", m.lastname, m.firstname";
16544 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur', 'Propal', 'Expedition'))) {
16545 $sql .= ", o.ref";
16546 } else {
16547 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16548 $sql .= ", o.ref";
16549 }
16550 }
16551 $sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as a";
16552 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "user as u on u.rowid = a.fk_user_action";
16553 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_actioncomm as c ON a.fk_action = c.id";
16554
16555 $force_filter_contact = $filterobj instanceof User;
16556
16557 if (is_object($objcon) && $objcon->id > 0) {
16558 $force_filter_contact = true;
16559 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "actioncomm_resources as r ON a.id = r.fk_actioncomm";
16560 $sql .= " AND r.element_type = '" . $db->escape($objcon->table_element) . "' AND r.fk_element = " . ((int) $objcon->id);
16561 }
16562
16563 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
16564 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "socpeople as sp ON a.fk_contact = sp.rowid";
16565 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
16566 $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "element_resources as er";
16567 $sql .= " ON er.resource_type = 'dolresource'";
16568 $sql .= " AND er.element_id = a.id";
16569 $sql .= " AND er.resource_id = " . ((int) $filterobj->id);
16570 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16571 $sql .= ", " . MAIN_DB_PREFIX . "adherent as m";
16572 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16573 $sql .= ", " . MAIN_DB_PREFIX . "commande_fournisseur as o";
16574 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16575 $sql .= ", " . MAIN_DB_PREFIX . "product as o";
16576 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16577 $sql .= ", " . MAIN_DB_PREFIX . "ticket as o";
16578 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16579 $sql .= ", " . MAIN_DB_PREFIX . "bom_bom as o";
16580 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16581 $sql .= ", " . MAIN_DB_PREFIX . "contrat as o";
16582 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16583 $sql .= ", " . MAIN_DB_PREFIX . "facture as o";
16584 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16585 $sql .= ", " . MAIN_DB_PREFIX . "facture_fourn as o";
16586 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16587 $sql .= ", " . MAIN_DB_PREFIX . "commande as o";
16588 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16589 $sql .= ", " . MAIN_DB_PREFIX . "expedition as o";
16590 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16591 $sql .= ", " . MAIN_DB_PREFIX . "propal as o";
16592 } else {
16593 if (is_object($filterobj) && !empty($filterobj->table_element) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16594 $sql .= ", " . MAIN_DB_PREFIX . $filterobj->table_element . " as o";
16595 }
16596 }
16597 $sql .= " WHERE a.entity IN (" . getEntity('agenda') . ")";
16598 if (!$force_filter_contact) {
16599 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
16600 $sql .= " AND a.fk_soc = " . ((int) $filterobj->id);
16601 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
16602 $sql .= " AND a.fk_project = o.rowid AND a.fk_project = " . ((int) $filterobj->id);
16603 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16604 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
16605 if ($filterobj->id) {
16606 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16607 }
16608 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
16609 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
16610 if ($filterobj->id) {
16611 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16612 }
16613 } elseif (is_object($filterobj) && get_class($filterobj) == 'Expedition') {
16614 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'shipping'";
16615 if ($filterobj->id) {
16616 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16617 }
16618 } elseif (is_object($filterobj) && get_class($filterobj) == 'Propal') {
16619 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'propal'";
16620 if ($filterobj->id) {
16621 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16622 }
16623 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16624 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
16625 if ($filterobj->id) {
16626 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16627 }
16628 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16629 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
16630 if ($filterobj->id) {
16631 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16632 }
16633 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16634 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
16635 if ($filterobj->id) {
16636 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16637 }
16638 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
16639 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
16640 if ($filterobj->id) {
16641 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16642 }
16643 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
16644 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
16645 if ($filterobj->id) {
16646 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16647 }
16648 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
16649 $sql .= " AND a.fk_contact = sp.rowid";
16650 if ($filterobj->id) {
16651 $sql .= " AND a.fk_contact = " . ((int) $filterobj->id);
16652 }
16653 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
16654 $sql .= " AND a.fk_element = o.rowid";
16655 if ($filterobj->id) {
16656 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice'";
16657 }
16658 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
16659 $sql .= " AND a.fk_element = o.rowid";
16660 if ($filterobj->id) {
16661 $sql .= " AND a.fk_element = " . ((int) $filterobj->id) . " AND a.elementtype = 'invoice_supplier'";
16662 }
16663 } else {
16664 if (is_object($filterobj) && !empty($filterobj->element) && !empty($filterobj->id) && array_key_exists('ref', $filterobj->fields)) {
16665 $sql .= " AND a.fk_element = o.rowid";
16666 $sql .= " AND a.elementtype = '" . $db->escape($filterobj->element) . "'";
16667 if ($filterobj->id) {
16668 $sql .= " AND a.fk_element = " . ((int) $filterobj->id);
16669 }
16670 }
16671 }
16672 } else {
16673 $sql .= " AND u.rowid = " . ((int) $filterobj->id);
16674 }
16675
16676 // Condition on actioncode
16677 if (!empty($actioncode) && $actioncode != '-1') {
16678 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
16679 if ($actioncode == 'AC_NON_AUTO') {
16680 $sql .= " AND c.type != 'systemauto'";
16681 } elseif ($actioncode == 'AC_ALL_AUTO') {
16682 $sql .= " AND c.type = 'systemauto'";
16683 } else {
16684 if ($actioncode == 'AC_OTH') {
16685 $sql .= " AND c.type != 'systemauto'";
16686 } elseif ($actioncode == 'AC_OTH_AUTO') {
16687 $sql .= " AND c.type = 'systemauto'";
16688 }
16689 }
16690 } else {
16691 if ($actioncode == 'AC_NON_AUTO') {
16692 $sql .= " AND c.type != 'systemauto'";
16693 } elseif ($actioncode == 'AC_ALL_AUTO') {
16694 $sql .= " AND c.type = 'systemauto'";
16695 } else {
16696 $sql .= " AND c.code = '" . $db->escape($actioncode) . "'";
16697 }
16698 }
16699 }
16700 if ($donetodo == 'todo') {
16701 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '" . $db->idate($now) . "'))";
16702 } elseif ($donetodo == 'done') {
16703 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '" . $db->idate($now) . "'))";
16704 }
16705 if (is_array($filters) && $filters['search_agenda_label']) {
16706 $sql .= natural_search('a.label', $filters['search_agenda_label']);
16707 }
16708 }
16709
16710 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
16711 if (
16712 isModEnabled('mailing') && !empty($objcon->email)
16713 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')
16714 ) {
16715 $langs->load("mails");
16716
16717 $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";
16718 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
16719 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
16720 $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
16721 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
16722 $sql2 .= ", '' as lastname, '' as firstname";
16723 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
16724 $sql2 .= ", '' as lastname, '' as firstname";
16725 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
16726 $sql2 .= ", '' as ref";
16727 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
16728 $sql2 .= ", '' as ref";
16729 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
16730 $sql2 .= ", '' as ref";
16731 }
16732 $sql2 .= " FROM " . MAIN_DB_PREFIX . "mailing as m, " . MAIN_DB_PREFIX . "mailing_cibles as mc, " . MAIN_DB_PREFIX . "user as u";
16733 $sql2 .= " WHERE mc.email = '" . $db->escape($objcon->email) . "'"; // Search is done on email.
16734 $sql2 .= " AND mc.statut = 1";
16735 $sql2 .= " AND u.rowid = m.fk_user_valid";
16736 $sql2 .= " AND mc.fk_mailing=m.rowid";
16737 }
16738
16739 $num = 0;
16740 $MAXWITHOUTPAGINATION = getDolGlobalInt('AGENDA_MAX_EVENTS_ON_PAGE_WITHOUT_PAGINATION', 100);
16741
16742 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
16743 if (!empty($sql) && !empty($sql2)) {
16744 $sql = $sql . " UNION " . $sql2;
16745 } elseif (empty($sql) && !empty($sql2)) {
16746 $sql = $sql2;
16747 }
16748
16749 //TODO Add navigation with this limits...
16750 $offset = 0;
16751 $limit = $MAXWITHOUTPAGINATION;
16752
16753 // Complete request and execute it with limit
16754 $sql .= $db->order($sanitized_sortfield_new, $sortorder);
16755 if ($limit) {
16756 $sql .= $db->plimit($limit + 1, $offset);
16757 }
16758
16759 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
16760
16761 $resql = $db->query($sql);
16762 if ($resql) {
16763 $i = 0;
16764 $num = $db->num_rows($resql);
16765
16766 $imaxinloop = ($limit ? min($num, $limit) : $num);
16767 while ($i < $imaxinloop) {
16768 $obj = $db->fetch_object($resql);
16769
16770 if ($obj->type == 'action') {
16771 $contactaction = new ActionComm($db);
16772 $contactaction->id = $obj->id;
16773 $result = $contactaction->fetchResources();
16774 if ($result < 0) {
16776 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
16777 }
16778
16779 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
16780 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
16781 $tododone = '';
16782 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
16783 $tododone = 'todo';
16784 }
16785
16786 $histo[$numaction] = array(
16787 'type' => $obj->type,
16788 'tododone' => $tododone,
16789 'id' => $obj->id,
16790 'datestart' => $db->jdate($obj->dp),
16791 'dateend' => $db->jdate($obj->dp2),
16792 'fulldayevent' => (int) $obj->fulldayevent,
16793 'note' => $obj->label,
16794 'message' => $obj->message,
16795 'percent' => $obj->percent,
16796
16797 'userid' => $obj->user_id,
16798 'login' => $obj->user_login,
16799 'userfirstname' => $obj->user_firstname,
16800 'userlastname' => $obj->user_lastname,
16801 'userphoto' => $obj->user_photo,
16802 'msg_from' => $obj->msg_from,
16803
16804 'contact_id' => $obj->fk_contact,
16805 'socpeopleassigned' => $contactaction->socpeopleassigned,
16806 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
16807 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
16808 'fk_element' => $obj->fk_element,
16809 'elementtype' => $obj->elementtype,
16810 // Type of event
16811 'acode' => $obj->acode,
16812 'alabel' => $obj->alabel,
16813 'libelle' => $obj->alabel, // deprecated
16814 'apicto' => $obj->apicto
16815 );
16816 } else {
16817 $histo[$numaction] = array(
16818 'type' => $obj->type,
16819 'tododone' => 'done',
16820 'id' => $obj->id,
16821 'datestart' => $db->jdate($obj->dp),
16822 'dateend' => $db->jdate($obj->dp2),
16823 'fulldayevent' => (int) $obj->fulldayevent,
16824 'note' => $obj->label,
16825 'message' => $obj->message,
16826 'percent' => $obj->percent,
16827 'acode' => $obj->acode,
16828
16829 'userid' => $obj->user_id,
16830 'login' => $obj->user_login,
16831 'userfirstname' => $obj->user_firstname,
16832 'userlastname' => $obj->user_lastname,
16833 'userphoto' => $obj->user_photo
16834 );
16835 }
16836
16837 $numaction++;
16838 $i++;
16839 }
16840 } else {
16842 }
16843 }
16844
16845 // Set $out to show events
16846 $out = '';
16847
16848 if (!isModEnabled('agenda')) {
16849 $langs->loadLangs(array("admin", "errors"));
16850 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
16851 }
16852
16853 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
16854 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
16855
16856 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
16857 include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
16858 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
16859 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
16860
16861 $formactions = new FormActions($db);
16862
16863 $actionstatic = new ActionComm($db);
16864 $userstatic = new User($db);
16865 $contactstatic = new Contact($db);
16866 $userGetNomUrlCache = array();
16867 $contactGetNomUrlCache = array();
16868
16869 $out .= '<div class="filters-container" >';
16870 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
16871 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
16872
16873 if (
16874 $objcon && get_class($objcon) == 'Contact' &&
16875 (is_null($filterobj) || get_class($filterobj) == 'Societe')
16876 ) {
16877 $out .= '<input type="hidden" name="id" value="' . $objcon->id . '" />';
16878 } else {
16879 $out .= '<input type="hidden" name="id" value="' . $filterobj->id . '" />';
16880 }
16881 if (($filterobj && get_class($filterobj) == 'Societe')) {
16882 $out .= '<input type="hidden" name="socid" value="' . $filterobj->id . '" />';
16883 } else {
16884 $out .= '<input type="hidden" name="userid" value="' . $filterobj->id . '" />';
16885 }
16886
16887 $out .= "\n";
16888
16889 $out .= '<div class="div-table-responsive-no-min">';
16890 $out .= '<table class="noborder borderbottom centpercent">';
16891
16892 $out .= '<tr class="liste_titre">';
16893
16894 // Action column
16895 if ($conf->main_checkbox_left_column) {
16896 $out .= '<th class="liste_titre width50 middle">';
16897 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16898 $out .= $searchpicto;
16899 $out .= '</th>';
16900 }
16901
16902 // Date
16903 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ') . "\n";
16904
16905 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">' . $langs->trans("Search") . ' : </strong></th>';
16906 if ($donetodo) {
16907 $out .= '<th class="liste_titre"></th>';
16908 }
16909 // Type of event
16910 $out .= '<th class="liste_titre">';
16911 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="' . $langs->trans("ActionType") . '"></span>';
16912 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
16913 $out .= '</th>';
16914 // Label
16915 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
16916 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="' . $filters['search_agenda_label'] . '" placeholder="' . $langs->trans("Label") . '">';
16917 $out .= '</th>';
16918
16919 // Action column
16920 if (!$conf->main_checkbox_left_column) {
16921 $out .= '<th class="liste_titre width50 middle">';
16922 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
16923 $out .= $searchpicto;
16924 $out .= '</th>';
16925 }
16926
16927 $out .= '</tr>';
16928
16929 $out .= '</table>';
16930
16931 $out .= '</form>';
16932 $out .= '</div>';
16933
16934 $out .= "\n";
16935
16936 $out .= '<ul class="timeline">';
16937
16938 if ($donetodo) {
16939 $tmp = '';
16940 if ($filterobj instanceof Societe) {
16941 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16942 }
16943 if ($filterobj instanceof User) {
16944 $tmp .= '<a href="' . DOL_URL_ROOT . '/comm/action/list.php?mode=show_list&socid=' . $filterobj->id . '&status=done">';
16945 }
16946 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
16947 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
16948 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
16949 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
16950 if ($filterobj instanceof Societe) {
16951 $tmp .= '</a>';
16952 }
16953 if ($filterobj instanceof User) {
16954 $tmp .= '</a>';
16955 }
16956 $out .= getTitleFieldOfList($tmp);
16957 }
16958
16959 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/cactioncomm.class.php';
16960 $caction = new CActionComm($db);
16961 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
16962
16963 $actualCycleDate = false;
16964
16965 // Loop on each event to show it
16966 foreach ($histo as $key => $value) {
16967 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
16968
16969 $actionstatic->type_picto = $histo[$key]['apicto'];
16970 $actionstatic->type_code = $histo[$key]['acode'];
16971
16972 $labeltype = $actionstatic->type_code;
16973 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
16974 $labeltype = 'AC_OTH';
16975 }
16976 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
16977 $labeltype = $langs->trans("Message");
16978 } else {
16979 if (!empty($arraylist[$labeltype])) {
16980 $labeltype = $arraylist[$labeltype];
16981 }
16982 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
16983 $labeltype .= ' - ' . $arraylist[$actionstatic->code]; // Use code in priority on type_code
16984 }
16985 }
16986
16987 $url = DOL_URL_ROOT . '/comm/action/card.php?id=' . $histo[$key]['id'];
16988
16989 $tmpa = dol_getdate($histo[$key]['datestart'], false);
16990
16991 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'] . '-' . $tmpa['yday']) {
16992 $actualCycleDate = $tmpa['year'] . '-' . $tmpa['yday'];
16993 $out .= '<!-- timeline time label -->';
16994 $out .= '<li class="time-label">';
16995 $out .= '<span class="timeline-badge-date">';
16996 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
16997 $out .= '</span>';
16998 $out .= '</li>';
16999 $out .= '<!-- /.timeline-label -->';
17000 }
17001
17002
17003 $out .= '<!-- timeline item -->' . "\n";
17004 $out .= '<li class="timeline-code-' . (!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none") . '">';
17005
17006 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
17007 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
17008 //$out .= $timelineicon;
17009 //var_dump($timelineicon);
17010 $out .= $typeicon;
17011
17012 $out .= '<div class="timeline-item">' . "\n";
17013
17014 $out .= '<span class="time timeline-header-action2">';
17015
17016 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
17017 $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") . ' ';
17018 $out .= $histo[$key]['id'];
17019 $out .= '</a> ';
17020 } else {
17021 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle') . ' ';
17022 }
17023
17024 if (
17025 $user->hasRight('agenda', 'allactions', 'create') ||
17026 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))
17027 ) {
17028 $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) . '">';
17029 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
17030 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
17031 $out .= '</a>';
17032 }
17033
17034 $out .= '</span>';
17035
17036 // Date
17037 $out .= '<span class="time"><i class="fa fa-clock valignmiddle"></i> ';
17038 $out .= '<span class="valignmiddle marginrightonly">';
17039 $out .= dol_print_date($histo[$key]['datestart'], 'day', 'tzuserrel');
17040 //$out .= '</span>';
17041 //$out .= '<span class="valignmiddle">'.
17042 $out .= ' '.dol_print_date($histo[$key]['datestart'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
17043 //$out .= '</span>';
17044 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
17045 $tmpa = dol_getdate($histo[$key]['datestart'], true);
17046 $tmpb = dol_getdate($histo[$key]['dateend'], true);
17047 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
17048 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 1);
17049 } else {
17050 $out .= ' - ' . dol_print_date($histo[$key]['dateend'], 'day', 'tzuserrel');
17051 //$out .= '<span class="valignmiddle marginrightonly">';
17052 $out .= ' '.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel', null, false, 'opacitymedium');
17053 //$out .= '</span>';
17054 }
17055 }
17056 $late = 0;
17057 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
17058 $late = 1;
17059 }
17060 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
17061 $late = 1;
17062 }
17063 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
17064 $late = 1;
17065 }
17066 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
17067 $late = 1;
17068 }
17069 if ($late) {
17070 $out .= img_warning($langs->trans("Late")) . ' ';
17071 }
17072 $out .= "</span></span>\n";
17073
17074 $out .= '<span class="time">';
17075 $out .= $actionstatic->getLibStatut(2);
17076 $out .= '</span>';
17077
17078 // Ref
17079 $out .= '<h3 class="timeline-header">';
17080
17081 // Author of event
17082 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
17083 if ($histo[$key]['userid'] > 0) {
17084 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
17085 $userstatic->fetch($histo[$key]['userid']);
17086 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
17087 }
17088 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
17089 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
17090 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
17091 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
17092 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
17093 } else {
17094 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
17095 }
17096 }
17097 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
17098 } else {
17099 $out .= '<img class="photomemberphoto userphoto" alt="" src="/public/theme/common/user_anonymous.png">'.$langs->trans("Anonymous");
17100 }
17101 $out .= '</div>';
17102
17103 // Title
17104 $out .= ' <div class="messaging-title inline-block">';
17105 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
17106 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
17107 $out .= $labeltype . ' - ';
17108 }
17109
17110 $libelle = '';
17111
17112 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
17113 $out .= $langs->trans('TicketNewMessage').' - <em>'.img_picto($langs->trans('Private'), 'lock', 'class="valignmiddle"').' '.$langs->trans('Private').'</em>';
17114 $summary = preg_replace('/\[[^\]]*\]\s*/', '', $actionstatic->label);
17115 //if ($summary != $object->title) {
17116 $out .= ' - '.dolPrintHTML($summary);
17117 //}
17118 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
17119 $out .= $langs->trans('TicketNewMessage');
17120 } elseif (isset($histo[$key]['type'])) {
17121 if ($histo[$key]['type'] == 'action') {
17122 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
17123 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
17124 $libelle = $histo[$key]['note'];
17125 $actionstatic->id = $histo[$key]['id'];
17126 if ($libelle != $labeltype) {
17127 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
17128 }
17129 } elseif ($histo[$key]['type'] == 'mailing') {
17130 $out .= '<a href="' . DOL_URL_ROOT . '/comm/mailing/card.php?id=' . $histo[$key]['id'] . '">' . img_object($langs->trans("ShowEMailing"), "email") . ' ';
17131 $transcode = $langs->transnoentitiesnoconv("Action" . $histo[$key]['acode']);
17132 $libelle = ($transcode != "Action" . $histo[$key]['acode'] ? $transcode : 'Send mass mailing');
17133 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
17134 } else {
17135 $libelle .= $histo[$key]['note'];
17136 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
17137 }
17138 }
17139 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
17140
17141 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
17142 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
17143 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
17144 } else {
17145 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
17146 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
17147 }
17148 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
17149 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
17150 }
17151
17152 // We do not show if link if on object we are filtering on (no need to show the link to ticket X when we are on page of events for the ticket X)
17153 $showlink = 1;
17154 if (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
17155 if ($histo[$key]['elementtype'] == 'ticket') {
17156 $showlink = 0;
17157 }
17158 }
17159
17160 if ($link && $showlink) {
17161 $out .= ' - ' . $link;
17162 }
17163 }
17164
17165 $out .= '</div>';
17166
17167 $out .= '</h3>';
17168
17169 // Message
17170 if ($actionstatic->code == 'AC_TICKET_CREATE') {
17171 $newmess = $filterobj->message;
17172 } else {
17173 $newmess = $histo[$key]['message'];
17174 }
17175 if (
17176 !empty($newmess && $newmess != $libelle)
17177 && $actionstatic->code != 'AC_TICKET_MODIFY'
17178 ) {
17179 $out .= '<div class="timeline-body wordbreak small">';
17180 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
17181 $truncatedText = dolGetFirstLineOfText($newmess, $truncateLines);
17182 if ($truncateLines > 0 && strlen($newmess) > strlen($truncatedText)) {
17183 $out .= '<div class="readmore-block --closed" >';
17184 $out .= ' <div class="readmore-block__excerpt">';
17185 $out .= dolPrintHTML($truncatedText, 0, array('pre', 'code'));
17186 $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>';
17187 $out .= ' </div>';
17188 $out .= ' <div class="readmore-block__full-text" >';
17189
17190 $out .= dolPrintHTML($newmess, 0, array('pre', 'code'));
17191
17192 $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>';
17193 $out .= ' </div>';
17194 $out .= '</div>';
17195 } else {
17196 $out .= dolPrintHTML($newmess, 0, array('pre', 'code'));
17197 }
17198 $out .= '</div>';
17199 }
17200
17201 // Timeline footer
17202 $footer = '';
17203
17204 // Contact for this action
17205 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
17206 $contactList = '';
17207 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
17208 if (empty($conf->cache['contact'][$cid])) {
17209 $contact = new Contact($db);
17210 $contact->fetch($cid);
17211 $conf->cache['contact'][$cid] = $contact;
17212 } else {
17213 $contact = $conf->cache['contact'][$cid];
17214 }
17215
17216 if ($contact) {
17217 $contactList .= !empty($contactList) ? ', ' : '';
17218 $contactList .= $contact->getNomUrl(1);
17219 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
17220 if (!empty($contact->phone_pro)) {
17221 $contactList .= '(' . dol_print_phone($contact->phone_pro) . ')';
17222 }
17223 }
17224 }
17225 }
17226
17227 $footer .= $langs->trans('ActionOnContact') . ' : ' . $contactList;
17228 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
17229 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
17230 $contact = new Contact($db);
17231 $result = $contact->fetch($histo[$key]['contact_id']);
17232 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
17233 } else {
17234 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
17235 $result = ($contact instanceof Contact) ? $contact->id : 0;
17236 }
17237
17238 if ($result > 0) {
17239 $footer .= $contact->getNomUrl(1);
17240 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
17241 if (!empty($contact->phone_pro)) {
17242 $footer .= '(' . dol_print_phone($contact->phone_pro) . ')';
17243 }
17244 }
17245 }
17246 }
17247
17248 $documents = getActionCommEcmList($actionstatic);
17249 if (!empty($documents)) {
17250 $footer .= '<div class="timeline-documents-container">';
17251 foreach ($documents as $doc) {
17252 $footer .= '<span id="document_' . $doc->id . '" class="timeline-documents" ';
17253 $footer .= ' data-id="' . $doc->id . '" ';
17254 $footer .= ' data-path="' . $doc->filepath . '"';
17255 $footer .= ' data-filename="' . dol_escape_htmltag($doc->filename) . '" ';
17256 $footer .= '>';
17257
17258 $filePath = DOL_DATA_ROOT . '/' . $doc->filepath . '/' . $doc->filename;
17259 $mime = dol_mimetype($filePath);
17260 if (empty($doc->agenda_id)) {
17261 $dir_ref = $actionstatic->id;
17262 $modulepart = 'actions';
17263 } else {
17264 $split_dir = explode('/', $doc->filepath);
17265 $modulepart = array_shift($split_dir);
17266 $dir_ref = implode('/', $split_dir);
17267 }
17268
17269 $file = $dir_ref . '/' . $doc->filename;
17270 $thumb = $dir_ref . '/thumbs/' . substr($doc->filename, 0, strrpos($doc->filename, '.')) . '_mini' . substr($doc->filename, strrpos($doc->filename, '.'));
17271 $doclink = dol_buildpath('document.php', 1) . '?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($file) . '&entity=' . $conf->entity;
17272 $viewlink = dol_buildpath('viewimage.php', 1) . '?modulepart=' . $modulepart . '&file=' . urlencode($thumb) . '&entity=' . $conf->entity;
17273
17274
17275
17276 $mimeAttr = ' mime="' . $mime . '" ';
17277 $class = '';
17278 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
17279 $class .= ' documentpreview';
17280 }
17281
17282 $footer .= '<a href="' . $doclink . '" class="btn-link ' . $class . '" target="_blank" rel="noopener noreferrer" ' . $mimeAttr . ' >';
17283 $footer .= img_mime($filePath) . ' ' . $doc->filename;
17284 $footer .= '</a>';
17285
17286 $footer .= '</span>';
17287 }
17288 $footer .= '</div>';
17289 }
17290
17291 if (!empty($footer)) {
17292 $out .= '<div class="timeline-footer">' . $footer . '</div>';
17293 }
17294
17295 $out .= '</div>' . "\n"; // end timeline-item
17296
17297 $out .= '</li>';
17298 $out .= '<!-- END timeline item -->';
17299 }
17300
17301 $out .= "</ul>\n";
17302
17303 // Code to manage the click on button data-read-more-action to show full description of an event
17304 $out .= '<script>
17305 jQuery(document).ready(function () {
17306 $(document).on("click", "[data-read-more-action]", function(e){
17307 console.log("We click on data-read-more-action");
17308 let readMoreBloc = $(this).closest(".readmore-block");
17309 if(readMoreBloc.length > 0){
17310 e.preventDefault();
17311 if($(this).attr("data-read-more-action") == "close"){
17312 readMoreBloc.addClass("--closed").removeClass("--open");
17313 $("html, body").animate({
17314 scrollTop: readMoreBloc.offset().top - 200
17315 }, 100);
17316 }else{
17317 readMoreBloc.addClass("--open").removeClass("--closed");
17318 }
17319 }
17320 });
17321 });
17322 </script>';
17323
17324
17325 if (empty($histo)) {
17326 $out .= '<span class="opacitymedium">' . $langs->trans("NoRecordFound") . '</span>';
17327 }
17328
17329 if ($num > $MAXWITHOUTPAGINATION) {
17330 $langs->load("errors");
17331 $out .= '<center><span class="opacitymedium">...' . $langs->trans("WarningTooManyDataPleaseUseMoreFilters", $MAXWITHOUTPAGINATION) . '...</span></center>';
17332 }
17333 }
17334
17335 if ($noprint) {
17336 return $out;
17337 } else {
17338 print $out;
17339 return null;
17340 }
17341}
17342
17354function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
17355{
17356 if ($timestamp === null) {
17357 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
17358 }
17359 $TParam = array(
17360 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
17361 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
17362 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
17363 );
17364 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
17365 $TParam = array_merge($TParam, array(
17366 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
17367 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
17368 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
17369 ));
17370 }
17371
17372 return '&' . http_build_query($TParam);
17373}
17374
17393function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
17394{
17395 global $conf, $db, $langs, $hookmanager;
17396 global $action, $object;
17397
17398 if (!is_object($langs)) {
17399 include_once DOL_DOCUMENT_ROOT . '/core/class/translate.class.php';
17400 $langs = new Translate('', $conf);
17401 $langs->setDefaultLang();
17402 }
17403
17404 $langs->load("errors");
17405
17406 if ($printheader) {
17407 if (function_exists("llxHeader")) {
17408 llxHeader('');
17409 } elseif (function_exists("llxHeaderVierge")) {
17410 llxHeaderVierge('');
17411 }
17412 }
17413
17414 print '<div class="error">';
17415 if (empty($message)) {
17416 print $langs->trans("ErrorRecordNotFound");
17417 } else {
17418 print $langs->trans($message);
17419 }
17420 print '</div>';
17421 print '<br>';
17422
17423 if (empty($showonlymessage)) {
17424 if (empty($hookmanager)) {
17425 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
17426 $hookmanager = new HookManager($db);
17427 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
17428 $hookmanager->initHooks(array('main'));
17429 }
17430
17431 $parameters = array('message' => $message, 'params' => $params);
17432 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
17433 print $hookmanager->resPrint;
17434 }
17435
17436 if ($printfooter && function_exists("llxFooter")) {
17437 llxFooter();
17438 if (is_object($db)) {
17439 $db->close();
17440 }
17441 }
17442 exit(0);
17443}
17444
17475function array_merge_recursive_distinct(array $array1, array $array2): array
17476{
17477 $merged = $array1;
17478
17479 foreach ($array2 as $key => $value) {
17480 if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
17481 $merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
17482 } else {
17483 $merged[$key] = $value;
17484 }
17485 }
17486
17487 return $merged;
17488}
17489
17496function getObjectSocId($obj)
17497{
17498 if (!empty($obj->socid)) {
17499 return (int) $obj->socid;
17500 } elseif (!empty($obj->soc_id)) {
17501 return (int) $obj->soc_id;
17502 } elseif (!empty($obj->societe_id)) {
17503 return (int) $obj->societe_id;
17504 }
17505 return null;
17506}
17507
17514{
17515 $default = 10;
17516 if (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 700) {
17517 $default = 8;
17518 } elseif (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 950) {
17519 $default = 10;
17520 } elseif (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] > 1130) {
17521 $default = 15;
17522 }
17523
17524 return $default;
17525}
$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=[], $ws='')
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:476
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:801
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:334
$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:524
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:509
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:87
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition date.lib.php:493
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:543
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert a PDF file 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.
$date_start
Variables from include:
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.
getDolGlobalLoginBadCharUnauthorized()
Return the list of unauthorized characters in user logins.
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.
dolPrintHTML($s, $allowiframe=0, $moreallowedtags=array())
Return a string (that can be on several lines) ready to be output on a HTML page.
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.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
getObjectSocId($obj)
Get the socid of an object, supporting legacy attribute names.
getListLimitFromScreenHeight()
Get the limit of list to show according to the screen height.
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.
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 ...
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dolOutputDates($datep, $datef=null, $fullday=0, $addseconds=0, $pictotoadd='', $tzoutput='tzuserrel', $reduceformat=0)
Print decorated date-hour.
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 value of 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)
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.
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.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
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.
safeArrayMap($callback, array $array)
Add a function to replace array_map with allowed callback.
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.
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)
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolForgeSQLCriteriaCallback($matches)
Function to forge a SQL criteria from a USF (Universal Filter Syntax) string.
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.
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')) formatLogObject($data)
Return a string serialized to be output on log with dol_syslog() An option allow to output log in one...
getDolDBType()
Return the current entity.
print_titre($title)
Show a title.
showSimpleHTMLTable($outputlangs, $object)
Returns simple order table template as string.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
img_error($titlealt='default')
Show error logo.
getTimelineIcon($actionstatic, &$histo, $key)
Get timeline icon.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formatted messages to output (Used to show messages on html output).
get_product_localtax_for_country($idprod, $local, $thirdpartytouseforcountry)
Return localtax vat rate of a product in a particular country or default country vat if product is un...
getUserRemoteIP($trusted=0)
Return the real IP of remote user.
buildParamDate($prefix, $timestamp=null, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_next($titlealt='default', $moreatt='')
Show next logo.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
dol_string_is_good_iso($s, $clean=0)
Check if a string is a correct iso string If not, it will not be considered as HTML encoded even if i...
getNonce()
Return a random string to be used as a nonce value for js.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
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.
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).
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
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
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
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:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition waf.inc.php:66