dolibarr 22.0.5
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2025 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2019-2023 Thibault Foucart <support@ptibogxiv.net>
18 * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20 * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21 * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22 * Copyright (C) 2022-2025 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
24 * Copyright (C) 2023-2024 Joachim Kueter <git-jk@bloxera.com>
25 * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26 * Copyright (C) 2024 Josep Lluís Amador Teruel <joseplluis@lliuretic.cat>
27 * Copyright (C) 2024 Benoît PASCAL <contact@p-ben.com>
28 * Copyright (C) 2026 Benjamin Falière <benjamin@faliere.com>
29 *
30 * This program is free software; you can redistribute it and/or modify
31 * it under the terms of the GNU General Public License as published by
32 * the Free Software Foundation; either version 3 of the License, or
33 * (at your option) any later version.
34 *
35 * This program is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 * GNU General Public License for more details.
39 *
40 * You should have received a copy of the GNU General Public License
41 * along with this program. If not, see <https://www.gnu.org/licenses/>.
42 * or see https://www.gnu.org/
43 */
44
51//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
52
53// Function for better PHP x compatibility
54if (!function_exists('utf8_encode')) {
62 function utf8_encode($elements)
63 {
64 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
65 }
66}
67
68if (!function_exists('utf8_decode')) {
76 function utf8_decode($elements)
77 {
78 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
79 }
80}
81if (!function_exists('str_starts_with')) {
90 function str_starts_with($haystack, $needle)
91 {
92 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
93 }
94}
95if (!function_exists('str_ends_with')) {
104 function str_ends_with($haystack, $needle)
105 {
106 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
107 }
108}
109if (!function_exists('str_contains')) {
118 function str_contains($haystack, $needle)
119 {
120 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
121 }
122}
123
124
136function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
137{
138 global $conf;
139
140 if (!is_object($object) && empty($module)) {
141 return null;
142 }
143 if (empty($module) && !empty($object->element)) {
144 $module = $object->element;
145 }
146
147 // Special case for backward compatibility
148 if ($module == 'fichinter') {
149 $module = 'ficheinter';
150 } elseif ($module == 'invoice_supplier') {
151 $module = 'supplier_invoice';
152 } elseif ($module == 'order_supplier') {
153 $module = 'supplier_order';
154 }
155
156 // Get the relative path of directory
157 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
158 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
159 $s = '';
160 if ($mode != 'outputrel') {
161 // An entity with no declared directory returned an undefined index, so an empty path that
162 // made the caller read or write a relative path under the web root. Answer the error instead.
163 $entity = (int) (empty($object->entity) ? $conf->entity : $object->entity);
164 if (!isset($conf->$module->multidir_output[$entity])) {
165 return 'error-diroutput-not-defined-for-this-entity-and-object='.$module;
166 }
167 $s = $conf->$module->multidir_output[$entity];
168 }
169 if ($forobject && $object->id > 0) {
170 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
171 }
172 return $s;
173 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
174 $s = '';
175 if ($mode != 'outputrel') {
176 $s = $conf->$module->dir_output;
177 }
178 if ($forobject && $object->id > 0) {
179 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
180 }
181 return $s;
182 } else {
183 return 'error-diroutput-not-defined-for-this-entity-and-object='.$module;
184 }
185 } elseif ($mode == 'temp') {
186 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
187 // Same guard as the 'output' mode above, see the comment there
188 $entity = (int) (empty($object->entity) ? $conf->entity : $object->entity);
189 if (!isset($conf->$module->multidir_temp[$entity])) {
190 return 'error-dirtemp-not-defined-for-this-entity-and-object='.$module;
191 }
192 return $conf->$module->multidir_temp[$entity];
193 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
194 return $conf->$module->dir_temp;
195 } else {
196 return 'error-dirtemp-not-defined-for-this-entity-and-object='.$module;
197 }
198 } else {
199 return 'error-bad-value-for-mode';
200 }
201}
202
212function getMultidirTemp($object, $module = '', $forobject = 0)
213{
214 return getMultidirOutput($object, $module, $forobject, 'temp');
215}
216
226function getMultidirVersion($object, $module = '', $forobject = 0)
227{
228 return getMultidirOutput($object, $module, $forobject, 'version');
229}
230
231
240function getDolGlobalString($key, $default = '')
241{
242 global $conf;
243 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
244}
245
255function getDolGlobalInt($key, $default = 0)
256{
257 global $conf;
258 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
259}
260
270function getDolGlobalFloat($key, $default = 0)
271{
272 global $conf;
273 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
274}
275
284function getDolGlobalBool($key, $default = false)
285{
286 global $conf;
287 return (bool) ($conf->global->$key ?? $default);
288}
289
299function getDolUserString($key, $default = '', $tmpuser = null)
300{
301 if (empty($tmpuser)) {
302 global $user;
303 $tmpuser = $user;
304 }
305
306 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
307}
308
317function getDolUserInt($key, $default = 0, $tmpuser = null)
318{
319 if (empty($tmpuser)) {
320 global $user;
321 $tmpuser = $user;
322 }
323
324 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
325}
326
327
337define(
338 'MODULE_MAPPING',
339 array(
340 // Map deprecated names to new names
341 'adherent' => 'member', // Has new directory
342 'member_type' => 'adherent_type', // No directory, but file called adherent_type
343 'banque' => 'bank', // Has new directory
344 'contrat' => 'contract', // Has new directory
345 'entrepot' => 'stock', // Has new directory
346 'projet' => 'project', // Has new directory
347 'categorie' => 'category', // Has old directory
348 'commande' => 'order', // Has old directory
349 'expedition' => 'shipping', // Has old directory
350 'facture' => 'invoice', // Has old directory
351 'fichinter' => 'intervention', // Has old directory
352 'ficheinter' => 'intervention', // Backup for 'fichinter'
353 'propale' => 'propal', // Has old directory
354 'socpeople' => 'contact', // Has old directory
355 'fournisseur' => 'supplier', // Has old directory
356
357 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
358 'product_price' => 'productprice', // NO directory
359 'product_fournisseur_price' => 'productsupplierprice', // NO directory
360 )
361);
362
369function isModEnabled($module)
370{
371 global $conf;
372
373 // Fix old names (map to new names)
374 $arrayconv = MODULE_MAPPING;
375 $arrayconvbis = array_flip(MODULE_MAPPING);
376
377 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
378 // Special cases: both use the same module.
379 $arrayconv['supplier_order'] = 'fournisseur';
380 $arrayconv['supplier_invoice'] = 'fournisseur';
381 }
382 // Special case.
383 // @TODO Replace isModEnabled('delivery_note') with
384 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
385 if ($module == 'delivery_note') {
386 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
387 return false;
388 } else {
389 $module = 'shipping';
390 }
391 }
392
393 $module_alt = $module;
394 if (!empty($arrayconv[$module])) {
395 $module_alt = $arrayconv[$module];
396 }
397 $module_bis = $module;
398 if (!empty($arrayconvbis[$module])) {
399 $module_bis = $arrayconvbis[$module];
400 }
401
402 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
403 //return !empty($conf->$module->enabled);
404}
405
416function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
417{
418 global $conf;
419
420 // For compatibility with bad naming on module
421 $moduletomoduletouse = array(
422 'invoice' => 'facture',
423 );
424 $moduleParmsMapping = array(
425 'product' => 'produit',
426 );
427
428 if (!empty($moduletomoduletouse[$module])) {
429 $module = $moduletomoduletouse[$module];
430 }
431
432 $warningDelayPath = $parmlevel1;
433 if (!empty($moduleParmsMapping[$warningDelayPath])) {
434 $warningDelayPath = $moduleParmsMapping[$warningDelayPath];
435 }
436
437 if ($parmlevel2) {
438 if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
439 if (!empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
440 return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
441 }
442 }
443 } else {
444 if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
445 return (int) $conf->$module->$warningDelayPath->$parmlevel1->warning_delay;
446 }
447 }
448
449 return 0;
450}
451
458function isDolTms($timestamp)
459{
460 if ($timestamp === '') {
461 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
462 return false;
463 }
464 if (is_null($timestamp) || !is_numeric($timestamp)) {
465 return false;
466 }
467
468 return true;
469}
470
482function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
483{
484 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
485
486 $class = 'DoliDB'.ucfirst($type);
487 $db = new $class($type, $host, $user, $pass, $name, $port);
488 return $db;
489}
490
508function getEntity($element, $shared = 1, $currentobject = null)
509{
510 global $conf, $mc, $hookmanager, $object, $action, $db;
511
512 if (!is_object($hookmanager)) {
513 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
514 $hookmanager = new HookManager($db);
515 }
516
517 // fix different element names (France to English)
518 switch ($element) {
519 case 'projet':
520 $element = 'project';
521 break;
522 case 'contrat':
523 $element = 'contract';
524 break; // "/contrat/class/contrat.class.php"
525 case 'order_supplier':
526 $element = 'supplier_order';
527 break; // "/fourn/class/fournisseur.commande.class.php"
528 case 'invoice_supplier':
529 $element = 'supplier_invoice';
530 break; // "/fourn/class/fournisseur.facture.class.php"
531 }
532
533 if (is_object($mc)) {
534 $out = $mc->getEntity($element, $shared, $currentobject);
535 } else {
536 $out = '';
537 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
538 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
539 $addzero[] = 'c_holiday_types';
540 }
541 if (in_array($element, $addzero)) {
542 $out .= '0,';
543 }
544 $out .= ((int) $conf->entity);
545 }
546
547 // Manipulate entities to query on the fly
548 $parameters = array(
549 'element' => $element,
550 'shared' => $shared,
551 'object' => $object,
552 'currentobject' => $currentobject,
553 'out' => $out
554 );
555 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
556 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
557
558 if (is_numeric($reshook)) {
559 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
560 $out .= ','.$hookmanager->resPrint; // add
561 } elseif ($reshook == 1) {
562 $out = $hookmanager->resPrint; // replace
563 }
564 }
565
566 return $out;
567}
568
575function setEntity($currentobject)
576{
577 global $conf, $mc;
578
579 if (is_object($mc) && method_exists($mc, 'setEntity')) {
580 return $mc->setEntity($currentobject);
581 } else {
582 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
583 }
584}
585
592function isASecretKey($keyname)
593{
594 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
595}
596
597
604function num2Alpha($n)
605{
606 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
607 $r = chr($n % 26 + 0x41) . $r;
608 }
609 return $r;
610}
611
612
629function getBrowserInfo($user_agent)
630{
631 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
632
633 $name = 'unknown';
634 $version = '';
635 $os = 'unknown';
636 $phone = '';
637
638 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
639
640 $detectmobile = new Mobile_Detect(null, $user_agent);
641 $tablet = $detectmobile->isTablet();
642
643 if ($detectmobile->isMobile()) {
644 $phone = 'unknown';
645
646 // If phone/smartphone, we set phone os name.
647 if ($detectmobile->is('AndroidOS')) {
648 $os = $phone = 'android';
649 } elseif ($detectmobile->is('BlackBerryOS')) {
650 $os = $phone = 'blackberry';
651 } elseif ($detectmobile->is('iOS')) {
652 $os = 'ios';
653 $phone = 'iphone';
654 } elseif ($detectmobile->is('PalmOS')) {
655 $os = $phone = 'palm';
656 } elseif ($detectmobile->is('SymbianOS')) {
657 $os = 'symbian';
658 } elseif ($detectmobile->is('webOS')) {
659 $os = 'webos';
660 } elseif ($detectmobile->is('MaemoOS')) {
661 $os = 'maemo';
662 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
663 $os = 'windows';
664 }
665 }
666
667 // OS
668 if (preg_match('/linux/i', $user_agent)) {
669 $os = 'linux';
670 } elseif (preg_match('/macintosh/i', $user_agent)) {
671 $os = 'macintosh';
672 } elseif (preg_match('/windows/i', $user_agent)) {
673 $os = 'windows';
674 }
675
676 // Name
677 $reg = array();
678 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
679 $name = 'firefox';
680 $version = empty($reg[2]) ? '' : $reg[2];
681 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
682 $name = 'edge';
683 $version = empty($reg[2]) ? '' : $reg[2];
684 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
685 $name = 'chrome';
686 $version = empty($reg[2]) ? '' : $reg[2];
687 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
688 // we can have 'chrome (Mozilla...) chrome x.y' in one string
689 $name = 'chrome';
690 } elseif (preg_match('/iceweasel/i', $user_agent)) {
691 $name = 'iceweasel';
692 } elseif (preg_match('/epiphany/i', $user_agent)) {
693 $name = 'epiphany';
694 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
695 $name = 'safari';
696 $version = empty($reg[2]) ? '' : $reg[2];
697 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
698 // Safari is often present in string for mobile but its not.
699 $name = 'opera';
700 $version = empty($reg[2]) ? '' : $reg[2];
701 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
702 $name = 'ie';
703 $version = end($reg);
704 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
705 // MS products at end
706 $name = 'ie';
707 $version = end($reg);
708 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
709 // MS products at end
710 $name = 'textbrowser';
711 $version = empty($reg[3]) ? '' : $reg[3];
712 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
713 // MS products at end
714 $name = 'textbrowser';
715 $version = empty($reg[1]) ? '' : $reg[1];
716 }
717
718 if ($tablet) {
719 $layout = 'tablet';
720 } elseif ($phone) {
721 $layout = 'phone';
722 } else {
723 $layout = 'classic';
724 }
725
726 return array(
727 'browsername' => $name,
728 'browserversion' => $version,
729 'browseros' => $os,
730 'browserua' => $user_agent,
731 'layout' => $layout, // tablet, phone, classic
732 'phone' => $phone, // deprecated
733 'tablet' => $tablet // deprecated
734 );
735}
736
742function dol_shutdown()
743{
744 global $db;
745 $disconnectdone = false;
746 $depth = 0;
747 if (is_object($db) && !empty($db->connected)) {
748 $depth = $db->transaction_opened;
749 $disconnectdone = $db->close();
750 }
751 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));
752}
753
763function GETPOSTISSET($paramname)
764{
765 $isset = false;
766
767 $relativepathstring = $_SERVER["PHP_SELF"];
768 // Clean $relativepathstring
769 if (constant('DOL_URL_ROOT')) {
770 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
771 }
772 $relativepathstring = ltrim($relativepathstring, '/');
773 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
774 //var_dump($relativepathstring);
775 //var_dump($user->default_values);
776
777 // Code for search criteria persistence.
778 // Retrieve values if restore_lastsearch_values
779 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
780 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
781 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
782 if (is_array($tmp)) {
783 foreach ($tmp as $key => $val) {
784 if ($key == $paramname) { // We are on the requested parameter
785 $isset = true;
786 break;
787 }
788 }
789 }
790 }
791 // If there is saved contextpage, limit, page or mode
792 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
793 $isset = true;
794 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
795 $isset = true;
796 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
797 $isset = true;
798 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
799 $isset = true;
800 }
801 } else {
802 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
803 }
804
805 return $isset;
806}
807
816function GETPOSTISARRAY($paramname, $method = 0)
817{
818 // for $method test need return the same $val as GETPOST
819 if (empty($method)) {
820 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
821 } elseif ($method == 1) {
822 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
823 } elseif ($method == 2) {
824 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
825 } elseif ($method == 3) {
826 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
827 } else {
828 $val = 'BadFirstParameterForGETPOST';
829 }
830
831 return is_array($val);
832}
833
864function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
865{
866 global $mysoc, $user, $conf;
867
868 if (empty($paramname)) { // Explicit test for null for phan.
869 return 'BadFirstParameterForGETPOST';
870 }
871 if (empty($check)) {
872 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);
873 // Enable this line to know who call the GETPOST with '' $check parameter.
874 //var_dump(getCallerInfoString());
875 }
876
877 if (empty($method)) {
878 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
879 } elseif ($method == 1) {
880 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
881 } elseif ($method == 2) {
882 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
883 } elseif ($method == 3) {
884 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
885 } else {
886 return 'BadThirdParameterForGETPOST';
887 }
888
889 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
890
891 if (empty($method) || $method == 3 || $method == 4) {
892 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
893 // Clean $relativepathstring
894 if (constant('DOL_URL_ROOT')) {
895 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
896 }
897 $relativepathstring = ltrim($relativepathstring, '/');
898 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
899 //var_dump($relativepathstring);
900 //var_dump($user->default_values);
901
902 // Code for search criteria persistence.
903 // Retrieve saved values if restore_lastsearch_values is set
904 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
905 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
906 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
907 if (is_array($tmp)) {
908 foreach ($tmp as $key => $val) {
909 if ($key == $paramname) { // We are on the requested parameter
910 $out = $val;
911 break;
912 }
913 }
914 }
915 }
916 // If there is saved contextpage, page or limit
917 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
918 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
919 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
920 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
921 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
922 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
923 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
924 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
925 }
926 } elseif (!isset($_GET['sortfield'])) {
927 // Else, retrieve default values if we are not doing a sort
928 // 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
929 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
930 // Search default value from $object->field
931 global $object;
932 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
933 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
934 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
935 $out = $object->fields[$paramname]['default'];
936 }
937 }
938 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
939 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
940 // Now search in setup to overwrite default values
941 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
942 if (isset($user->default_values[$relativepathstring]['createform'])) {
943 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
944 $qualified = 0;
945 if ($defkey != '_noquery_') {
946 $tmpqueryarraytohave = explode('&', $defkey);
947 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
948 $foundintru = 0;
949 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
950 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
951 $foundintru = 1;
952 }
953 }
954 if (!$foundintru) {
955 $qualified = 1;
956 }
957 //var_dump($defkey.'-'.$qualified);
958 } else {
959 $qualified = 1;
960 }
961
962 if ($qualified) {
963 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
964 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
965 break;
966 }
967 }
968 }
969 }
970 }
971 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
972 // Management of default search_filters and sort order
973 if (!empty($user->default_values)) {
974 // $user->default_values defined from menu 'Setup - Default values'
975 //var_dump($user->default_values[$relativepathstring]);
976 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
977 // Sorted on which fields ? ASC or DESC ?
978 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
979 // Even if paramname is sortfield, data are stored into ['sortorder...']
980 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
981 $qualified = 0;
982 if ($defkey != '_noquery_') {
983 $tmpqueryarraytohave = explode('&', $defkey);
984 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
985 $foundintru = 0;
986 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
987 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
988 $foundintru = 1;
989 }
990 }
991 if (!$foundintru) {
992 $qualified = 1;
993 }
994 //var_dump($defkey.'-'.$qualified);
995 } else {
996 $qualified = 1;
997 }
998
999 if ($qualified) {
1000 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1001 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
1002 if ($out) {
1003 $out .= ', ';
1004 }
1005 if ($paramname == 'sortfield') {
1006 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
1007 }
1008 if ($paramname == 'sortorder') {
1009 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
1010 }
1011 }
1012 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
1013 }
1014 }
1015 }
1016 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
1017 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
1018 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
1019 continue;
1020 }
1021 $qualified = 0;
1022 if ($defkey != '_noquery_') {
1023 $tmpqueryarraytohave = explode('&', $defkey);
1024 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1025 $foundintru = 0;
1026 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1027 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1028 $foundintru = 1;
1029 }
1030 }
1031 if (!$foundintru) {
1032 $qualified = 1;
1033 }
1034 //var_dump($defkey.'-'.$qualified);
1035 } else {
1036 $qualified = 1;
1037 }
1038
1039 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
1040 // We must keep $_POST and $_GET here
1041 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
1042 // We made a search from quick search menu, do we still use default filter ?
1043 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
1044 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1045 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1046 }
1047 } else {
1048 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1049 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1050 }
1051 break;
1052 }
1053 }
1054 }
1055 }
1056 }
1057 }
1058 }
1059 }
1060
1061 // 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)
1062 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1063 // 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.
1064 '@phan-var-force string $paramname';
1065 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1066 $reg = array();
1067 $regreplace = array();
1068 $maxloop = 20;
1069 $loopnb = 0; // Protection against infinite loop
1070
1071 while (preg_match('/__([A-Z0-9]+(?:_[A-Z0-9]+){0,3})__/i', $out, $reg) && ($loopnb < $maxloop)) { // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side.
1072 $loopnb++;
1073 $newout = '';
1074
1075 if ($reg[1] == 'DAY') {
1076 $tmp = dol_getdate(dol_now(), true);
1077 $newout = $tmp['mday'];
1078 } elseif ($reg[1] == 'MONTH') {
1079 $tmp = dol_getdate(dol_now(), true);
1080 $newout = $tmp['mon'];
1081 } elseif ($reg[1] == 'YEAR') {
1082 $tmp = dol_getdate(dol_now(), true);
1083 $newout = $tmp['year'];
1084 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1085 $tmp = dol_getdate(dol_now(), true);
1086 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1087 $newout = $tmp2['day'];
1088 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1089 $tmp = dol_getdate(dol_now(), true);
1090 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1091 $newout = $tmp2['month'];
1092 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1093 $tmp = dol_getdate(dol_now(), true);
1094 $newout = ($tmp['year'] - 1);
1095 } elseif ($reg[1] == 'NEXT_DAY') {
1096 $tmp = dol_getdate(dol_now(), true);
1097 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1098 $newout = $tmp2['day'];
1099 } elseif ($reg[1] == 'NEXT_MONTH') {
1100 $tmp = dol_getdate(dol_now(), true);
1101 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1102 $newout = $tmp2['month'];
1103 } elseif ($reg[1] == 'NEXT_YEAR') {
1104 $tmp = dol_getdate(dol_now(), true);
1105 $newout = ($tmp['year'] + 1);
1106 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1107 $newout = $mysoc->country_id;
1108 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1109 $newout = $user->id;
1110 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1111 $newout = $user->fk_user;
1112 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1113 $newout = $conf->entity;
1114 } elseif ($reg[1] == 'ID') {
1115 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1116 } else {
1117 $newout = 'REGREPLACE_'.$loopnb; // Key not found, we replace with temporary string to reload later
1118 $regreplace[$loopnb] = $reg[0];
1119 }
1120 //var_dump('__'.$reg[1].'__ -> '.$newout);
1121 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1122 }
1123 if (!empty($regreplace)) {
1124 foreach ($regreplace as $key => $value) {
1125 $out = preg_replace('/REGREPLACE_'.$key.'/', $value, $out);
1126 }
1127 }
1128 }
1129
1130 // Check type of variable and make sanitization according to this
1131 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1132 $tmpcheck = 'alphanohtml';
1133 if (empty($out)) {
1134 $out = array();
1135 } elseif (!is_array($out)) {
1136 $out = explode(',', $out);
1137 } else {
1138 $tmparray = explode(':', $check);
1139 if (!empty($tmparray[1])) {
1140 $tmpcheck = $tmparray[1];
1141 }
1142 }
1143 foreach ($out as $outkey => $outval) {
1144 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1145 }
1146 } else {
1147 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1148 // 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
1149 if (strpos($paramname, 'search_') === 0) {
1150 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1151 }
1152
1153 // @phan-suppress-next-line UnknownSanitizeType
1154 $out = sanitizeVal($out, $check, $filter, $options);
1155 }
1156
1157 // Sanitizing for special parameters.
1158 // 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.
1159 // @TODO Merge backtopage with backtourl
1160 // @TODO Rename backtolist into backtopagelist
1161 if (preg_match('/^backto/i', $paramname)) {
1162 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1163 $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.
1164 do {
1165 $oldstringtoclean = $out;
1166 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1167 $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'
1168 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1169 } while ($oldstringtoclean != $out);
1170 }
1171
1172 // Code for search criteria persistence.
1173 // Save data into session if key start with 'search_'
1174 if (empty($method) || $method == 3 || $method == 4) {
1175 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1176 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1177
1178 // We save search key only if $out not empty that means:
1179 // - posted value not empty, or
1180 // - 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).
1181
1182 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1183 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1184 }
1185 }
1186 }
1187
1188 if ($paramname == 'hashp' && $out == 'shared') {
1189 $out = ''; // We refuse to have hashp=shared as a parameter
1190 }
1191
1192 return $out;
1193}
1194
1204function GETPOSTINT($paramname, $method = 0)
1205{
1206 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1207}
1208
1217function GETPOSTFLOAT($paramname, $rounding = '')
1218{
1219 // price2num() is used to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.)
1220 return (float) price2num(GETPOST($paramname), $rounding, 2);
1221}
1222
1238function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto', $saverestore = '')
1239{
1240 $m = array();
1241 if ($hourTime === 'getpost' || $hourTime === 'getpostend') {
1242 $hour = (GETPOSTISSET($prefix . 'hour') && GETPOSTINT($prefix . 'hour') >= 0) ? GETPOSTINT($prefix . 'hour') : ($hourTime === 'getpostend' ? 23 : 0);
1243 $minute = (GETPOSTISSET($prefix . 'min') && GETPOSTINT($prefix . 'min') >= 0) ? GETPOSTINT($prefix . 'min') : ($hourTime === 'getpostend' ? 59 : 0);
1244 $second = (GETPOSTISSET($prefix . 'sec') && GETPOSTINT($prefix . 'sec') >= 0) ? GETPOSTINT($prefix . 'sec') : ($hourTime === 'getpostend' ? 59 : 0);
1245 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1246 $hour = intval($m[1]);
1247 $minute = intval($m[2]);
1248 $second = intval($m[3]);
1249 } elseif ($hourTime === 'end') {
1250 $hour = 23;
1251 $minute = 59;
1252 $second = 59;
1253 } else {
1254 $hour = $minute = $second = 0;
1255 }
1256
1257 if (
1258 $saverestore
1259 && !GETPOSTISSET($prefix . 'day')
1260 && !GETPOSTISSET($prefix . 'month')
1261 && !GETPOSTISSET($prefix . 'year')
1262 && isset($_SESSION['DOLDATE_' . $saverestore . '_day'])
1263 && isset($_SESSION['DOLDATE_' . $saverestore . '_month'])
1264 && isset($_SESSION['DOLDATE_' . $saverestore . '_year'])
1265 ) {
1266 $day = $_SESSION['DOLDATE_'.$saverestore.'_day'];
1267 $month = $_SESSION['DOLDATE_'.$saverestore.'_month'];
1268 $year = $_SESSION['DOLDATE_'.$saverestore.'_year'];
1269 } else {
1270 $month = GETPOSTINT($prefix . 'month');
1271 $day = GETPOSTINT($prefix . 'day');
1272 $year = GETPOSTINT($prefix . 'year');
1273 }
1274
1275 // normalize out of range values
1276 $hour = (int) min($hour, 23);
1277 $minute = (int) min($minute, 59);
1278 $second = (int) min($second, 59);
1279
1280 if ($saverestore) {
1281 $_SESSION['DOLDATE_'.$saverestore.'_day'] = $day;
1282 $_SESSION['DOLDATE_'.$saverestore.'_month'] = $month;
1283 $_SESSION['DOLDATE_'.$saverestore.'_year'] = $year;
1284 }
1285
1286 //print "$hour, $minute, $second, $month, $day, $year, $gm<br>";
1287 return dol_mktime($hour, $minute, $second, $month, $day, $year, $gm);
1288}
1289
1290
1301function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1302{
1303 return sanitizeVal($out, $check, $filter, $options);
1304}
1305
1315function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1316{
1317 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1318 // Check is done after replacement
1319 if ($out === null) {
1320 $out = '';
1321 }
1322 switch ($check) {
1323 case 'none':
1324 case 'password':
1325 break;
1326 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1327 if (!is_numeric($out)) {
1328 $out = '';
1329 }
1330 break;
1331 case 'intcomma':
1332 if (is_array($out)) {
1333 $out = implode(',', $out);
1334 }
1335 if (preg_match('/[^0-9,-]+/i', $out)) {
1336 $out = '';
1337 }
1338 break;
1339 case 'san_alpha':
1340 $out = filter_var($out, FILTER_SANITIZE_STRING);
1341 break;
1342 case 'email':
1343 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1344 break;
1345 case 'url':
1346 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1347 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1348 // TODO Allow ( ) but only into password of https://login:password@domain...
1349 break;
1350 case 'aZ':
1351 if (!is_array($out)) {
1352 $out = trim($out);
1353 if (preg_match('/[^a-z]+/i', $out)) {
1354 $out = '';
1355 }
1356 }
1357 break;
1358 case 'aZ09':
1359 if (!is_array($out)) {
1360 $out = trim($out);
1361 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1362 $out = '';
1363 }
1364 }
1365 break;
1366 case 'aZ09arobase': // great to sanitize $objecttype parameter
1367 if (!is_array($out)) {
1368 $out = trim($out);
1369 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1370 $out = '';
1371 }
1372 }
1373 break;
1374 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1375 if (!is_array($out)) {
1376 $out = trim($out);
1377 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1378 $out = '';
1379 }
1380 }
1381 break;
1382 case 'alpha': // No html and no ../ and "
1383 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1384 if (!is_array($out)) {
1385 $out = trim($out);
1386 do {
1387 $oldstringtoclean = $out;
1388 // Remove html tags
1389 $out = dol_string_nohtmltag($out, 0);
1390 // 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).
1391 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1392 // Remove also other dangerous string sequences
1393 // '../' or '..\' is dangerous because it allows dir transversals
1394 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1395 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1396 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1397 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1398 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1399 } while ($oldstringtoclean != $out);
1400 // keep lines feed
1401 }
1402 break;
1403 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'
1404 if (!is_array($out)) {
1405 $out = trim($out);
1406 do {
1407 $oldstringtoclean = $out;
1408 // Decode html entities
1409 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1410 // 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).
1411 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1412 // Remove also other dangerous string sequences
1413 // '../' or '..\' is dangerous because it allows dir transversals
1414 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1415 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1416 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1417 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1418 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1419 } while ($oldstringtoclean != $out);
1420 }
1421 break;
1422 case 'nohtml': // No html. Valid for JSON strings.
1423 $out = dol_string_nohtmltag($out, 0);
1424 break;
1425 case 'restricthtmlnolink':
1426 case 'restricthtml': // Recommended for most html textarea
1427 case 'restricthtmlallowclass':
1428 case 'restricthtmlallowiframe':
1429 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1430 case 'restricthtmlallowunvalid':
1431 $out = dol_htmlwithnojs($out, 1, $check);
1432 break;
1433 case 'custom':
1434 if (!empty($out)) {
1435 if (empty($filter)) {
1436 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1437 }
1438 if (is_null($options)) {
1439 $options = 0;
1440 }
1441 $out = filter_var($out, $filter, $options);
1442 }
1443 break;
1444 default:
1445 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1446 $out = GETPOST($out, 'alphanohtml');
1447 break;
1448 }
1449
1450 return $out;
1451}
1452
1461function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1462{
1463 global $dolibarr_main_force_https;
1464
1465 if ($expire == -1) {
1466 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1467 }
1468
1469 if (PHP_VERSION_ID < 70300) {
1470 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1471 } else {
1472 // Only available for php >= 7.3
1473 $cookieparams = array(
1474 'expires' => empty($cookievalue) ? 0 : $expire,
1475 'path' => '/',
1476 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1477 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1478 'httponly' => true,
1479 'samesite' => 'Lax' // None || Lax || Strict
1480 );
1481 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1482 }
1483 if (empty($cookievalue)) {
1484 unset($_COOKIE[$cookiename]);
1485 }
1486}
1487
1488if (!function_exists('dol_getprefix')) {
1499 function dol_getprefix($mode = '')
1500 {
1501 // If prefix is for email (we need to have $conf already loaded for this case)
1502 if ($mode == 'email') {
1503 global $conf;
1504
1505 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1506 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1507 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1508 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1509 return $_SERVER["SERVER_NAME"];
1510 }
1511 }
1512
1513 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1514 if (!empty($conf->file->instance_unique_id)) {
1515 return sha1('dolibarr'.$conf->file->instance_unique_id);
1516 }
1517
1518 // For backward compatibility when instance_unique_id is not set
1519 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1520 }
1521
1522 // If prefix is for session (no need to have $conf loaded)
1523 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1524 $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
1525
1526 // The recommended value (may be not defined for old versions)
1527 if (!empty($tmp_instance_unique_id)) {
1528 return sha1('dolibarr'.$tmp_instance_unique_id);
1529 }
1530
1531 // For backward compatibility when instance_unique_id is not set
1532 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1533 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1534 } else {
1535 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1536 }
1537 }
1538}
1539
1550function dol_include_once($relpath, $classname = '')
1551{
1552 global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retrieved with $GLOBALS['var']
1553
1554 $fullpath = dol_buildpath($relpath);
1555
1556 if (!file_exists($fullpath)) {
1557 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1558 return false;
1559 }
1560 if (!empty($classname) && !class_exists($classname)) {
1561 return include $fullpath;
1562 } else {
1563 return include_once $fullpath;
1564 }
1565}
1566
1567
1581function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1582{
1583 global $conf;
1584
1585 $path = preg_replace('/^\//', '', $path);
1586
1587 if (empty($type)) { // For a filesystem path
1588 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1589 if (is_array($conf->file->dol_document_root)) {
1590 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1591 if ($key == 'main') {
1592 continue;
1593 }
1594 // if (@file_exists($dirroot.'/'.$path)) {
1595 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1596 $res = $dirroot.'/'.$path;
1597 return $res;
1598 }
1599 }
1600 }
1601 if ($returnemptyifnotfound) {
1602 // Not found into alternate dir
1603 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1604 return '';
1605 }
1606 }
1607 } else {
1608 // For an url path
1609 // We try to get local path of file on filesystem from url
1610 // Note that trying to know if a file on disk exist by forging path on disk from url
1611 // works only for some web server and some setup. This is bugged when
1612 // using proxy, rewriting, virtual path, etc...
1613 $res = '';
1614 if ($type == 1) {
1615 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1616 }
1617 if ($type == 2) {
1618 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1619 }
1620 if ($type == 3) {
1621 $res = DOL_URL_ROOT.'/'.$path;
1622 }
1623
1624 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1625 if ($key == 'main') {
1626 if ($type == 3) {
1627 /*global $dolibarr_main_url_root;*/
1628
1629 // Define $urlwithroot
1630 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1631 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1632 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1633
1634 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1635 }
1636 continue;
1637 }
1638 $regs = array();
1639 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1640 if (!empty($regs[1])) {
1641 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1642 //if (file_exists($dirroot.'/'.$regs[1])) {
1643 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1644 if ($type == 1) {
1645 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1646 } elseif ($type == 2) {
1647 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1648 } elseif ($type == 3) {
1649 /*global $dolibarr_main_url_root;*/
1650
1651 // Define $urlwithroot
1652 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1653 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1654 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1655
1656 $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
1657 }
1658 break;
1659 }
1660 }
1661 }
1662 }
1663
1664 return $res;
1665}
1666
1677function dol_get_object_properties($obj, $properties = [])
1678{
1679 // Get real properties using get_object_vars() if $properties is empty
1680 if (empty($properties)) {
1681 return get_object_vars($obj);
1682 }
1683
1684 $existingProperties = [];
1685 $realProperties = get_object_vars($obj);
1686
1687 // Get the real or magic property values
1688 foreach ($properties as $property) {
1689 if (array_key_exists($property, $realProperties)) {
1690 // Real property, add the value
1691 $existingProperties[$property] = $obj->{$property};
1692 } elseif (property_exists($obj, $property)) {
1693 // Magic property
1694 $existingProperties[$property] = $obj->{$property};
1695 }
1696 }
1697
1698 return $existingProperties;
1699}
1700
1701
1717function dol_clone($object, $native = 2)
1718{
1719 if ($native == 0) {
1720 // deprecated method, use the method with native = 2 instead
1721 $tmpsavdb = null;
1722 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1723 $tmpsavdb = $object->db;
1724 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1725 }
1726
1727 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1728
1729 if (!empty($tmpsavdb)) {
1730 $object->db = $tmpsavdb;
1731 }
1732 } elseif ($native == 2) {
1733 // recommended method to have a full isolated cloned object
1734 $myclone = new stdClass();
1735 $tmparray = get_object_vars($object); // return only public properties
1736
1737 if (is_array($tmparray)) {
1738 foreach ($tmparray as $propertykey => $propertyval) {
1739 if (is_scalar($propertyval) || is_array($propertyval)) {
1740 $myclone->$propertykey = $propertyval;
1741 }
1742 }
1743 }
1744 } else {
1745 $myclone = clone $object; // 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)
1746 }
1747
1748 return $myclone;
1749}
1750
1760function dol_size($size, $type = '')
1761{
1762 global $conf;
1763 if (empty($conf->dol_optimize_smallscreen)) {
1764 return $size;
1765 }
1766 if ($type == 'width' && $size > 250) {
1767 return 250;
1768 } else {
1769 return 10;
1770 }
1771}
1772
1773
1786function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0)
1787{
1788 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1789 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1790 // Char '/' and '\' are file delimiters.
1791 // 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
1792 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1793 if ($includequotes) {
1794 $filesystem_forbidden_chars[] = "'";
1795 }
1796 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1797 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1798 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1799 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1800 $tmp = str_replace('..', '', $tmp);
1801 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1802
1803 return $tmp;
1804}
1805
1806
1818function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1819{
1820 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1821 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1822 // 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
1823 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1824
1825 $tmp = $str;
1826 if ($unaccent) {
1827 $tmp = dol_string_unaccent($tmp);
1828 }
1829 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1830 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1831 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1832 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1833 $tmp = str_replace('..', '', $tmp);
1834 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1835
1836 return $tmp;
1837}
1838
1846function dol_sanitizeUrl($stringtoclean, $type = 1)
1847{
1848 // 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)
1849 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1850 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1851 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1852 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1853
1854 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1855 if ($type == 1) {
1856 // removing : should disable links to external url like http:aaa)
1857 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1858 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1859 }
1860
1861 do {
1862 $oldstringtoclean = $stringtoclean;
1863 // removing '&colon' should disable links to external url like http:aaa)
1864 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1865 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1866 } while ($oldstringtoclean != $stringtoclean);
1867
1868 if ($type == 1) {
1869 // removing '//' should disable links to external url like //aaa or http//)
1870 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1871 }
1872
1873 return $stringtoclean;
1874}
1875
1882function dol_sanitizeEmail($stringtoclean)
1883{
1884 do {
1885 $oldstringtoclean = $stringtoclean;
1886 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1887 } while ($oldstringtoclean != $stringtoclean);
1888
1889 return $stringtoclean;
1890}
1891
1900function dol_sanitizeKeyCode($str)
1901{
1902 return preg_replace('/[^\w]+/', '', $str);
1903}
1904
1905
1914function dol_string_unaccent($str)
1915{
1916 if (is_null($str)) {
1917 return '';
1918 }
1919
1920 if (utf8_check($str)) {
1921 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1922 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1923 return $transliterator->transliterate($str);
1924 }
1925 // See http://www.utf8-chartable.de/
1926 $string = rawurlencode($str);
1927 $replacements = array(
1928 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1929 '%C3%87' => 'C',
1930 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1931 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1932 '%C3%91' => 'N',
1933 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', '%C5%90' => 'O',
1934 '%C5%A0' => 'S',
1935 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', '%C5%B0' => 'U',
1936 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1937 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1938 '%C3%A7' => 'c',
1939 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1940 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1941 '%C3%B1' => 'n',
1942 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', '%C5%91' => 'o',
1943 '%C5%A1' => 's',
1944 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', '%C5%B1' => 'u',
1945 '%C3%BD' => 'y', '%C3%BF' => 'y'
1946 );
1947 $string = strtr($string, $replacements);
1948 return rawurldecode($string);
1949 } else {
1950 // See http://www.ascii-code.com/
1951 $string = strtr(
1952 $str,
1953 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1954 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1955 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1956 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1957 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1958 \xF9\xFA\xFB\xFC\xFD\xFF",
1959 "AAAAAAC
1960 EEEEIIIIDN
1961 OOOOOUUUY
1962 aaaaaaceeee
1963 iiiidnooooo
1964 uuuuyy"
1965 );
1966 $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"));
1967 return $string;
1968 }
1969}
1970
1984function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1985{
1986 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1987 if (empty($keepspaces)) {
1988 $forbidden_chars_to_replace[] = " ";
1989 }
1990 $forbidden_chars_to_remove = array();
1991 //$forbidden_chars_to_remove=array("(",")");
1992
1993 if (is_array($badcharstoreplace)) {
1994 $forbidden_chars_to_replace = $badcharstoreplace;
1995 }
1996 if (is_array($badcharstoremove)) {
1997 $forbidden_chars_to_remove = $badcharstoremove;
1998 }
1999
2000 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
2001 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
2002}
2003
2004
2018function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2019{
2020 if ($removetabcrlf) {
2021 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2022 } else {
2023 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
2024 }
2025}
2026
2033function dolSlugify($stringtoslugify)
2034{
2035 $slug = dol_string_unaccent($stringtoslugify);
2036
2037 // Convert special characters to their ASCII equivalents
2038 if (function_exists('iconv')) {
2039 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2040 }
2041
2042 // Convert to lowercase
2043 $slug = strtolower($slug);
2044
2045 // Replace non-alphanumeric characters with hyphens
2046 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2047
2048 // Remove leading and trailing hyphens
2049 $slug = trim($slug, '-');
2050
2051 return $slug;
2052}
2053
2062function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2063{
2064 if (is_null($stringtoescape)) {
2065 return '';
2066 }
2067
2068 // escape quotes and backslashes, newlines, etc.
2069 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2070 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2071 if (empty($noescapebackslashn)) {
2072 $substitjs["\n"] = '\\n';
2073 $substitjs['\\'] = '\\\\';
2074 }
2075 if (empty($mode)) {
2076 $substitjs["'"] = "\\'";
2077 $substitjs['"'] = "\\'";
2078 } elseif ($mode == 1) {
2079 $substitjs["'"] = "\\'";
2080 } elseif ($mode == 2) {
2081 $substitjs['"'] = '\\"';
2082 } elseif ($mode == 3) {
2083 $substitjs["'"] = "\\'";
2084 $substitjs['"'] = "\\\"";
2085 }
2086 return strtr((string) $stringtoescape, $substitjs);
2087}
2088
2098function dol_escape_uri($stringtoescape)
2099{
2100 return rawurlencode($stringtoescape);
2101}
2102
2109function dol_escape_json($stringtoescape)
2110{
2111 return str_replace('"', '\"', $stringtoescape);
2112}
2113
2121function dol_escape_php($stringtoescape, $stringforquotes = 2)
2122{
2123 if (is_null($stringtoescape)) {
2124 return '';
2125 }
2126
2127 if ($stringforquotes == 2) {
2128 return str_replace('"', "'", $stringtoescape);
2129 } elseif ($stringforquotes == 1) {
2130 // We remove the \ char.
2131 // If we allow the \ char, we can have $stringtoescape =
2132 // abc\';phpcodedanger; so the escapement will become
2133 // abc\\';phpcodedanger; and injecting this into
2134 // $a='...' will give $ac='abc\\';phpcodedanger;
2135 $stringtoescape = str_replace('\\', '', $stringtoescape);
2136 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2137 }
2138
2139 return 'Bad parameter for stringforquotes in dol_escape_php';
2140}
2141
2148function dol_escape_all($stringtoescape)
2149{
2150 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2151}
2152
2159function dol_escape_xml($stringtoescape)
2160{
2161 return $stringtoescape;
2162}
2163
2173function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2174{
2175 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2176}
2177
2186function dolPrintText($s)
2187{
2188 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2189}
2190
2201function dolPrintHTML($s, $allowiframe = 0)
2202{
2203 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2204 //$isAlreadyHTML = dol_textishtml($s);
2205
2206 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2207 // encode only special char like é but not &, <, >, ", ' if already HTML.
2208 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2209
2210 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2211}
2212
2223function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2224{
2225 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2226 if (!empty($allowothertags) && is_array($allowothertags)) {
2227 $allowedtags = array_merge($allowedtags, $allowothertags);
2228 }
2229 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2230 // The dol_escape_htmltag will escape html tags.
2231 if ($escapeonlyhtmltags) {
2232 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2233 } else {
2234 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2235 }
2236}
2237
2246function dolPrintHTMLForAttributeUrl($s)
2247{
2248 // 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)
2249 // The dol_escape_htmltag will escape html chars.
2250 $escapeonlyhtmltags = 1;
2251 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2252}
2253
2263function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2264{
2265 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2266}
2267
2274function dolPrintPassword($s)
2275{
2276 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2277}
2278
2279
2296function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2297{
2298 if ($noescapetags == 'common') {
2299 $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';
2300 // Add also html5 tags
2301 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2302 }
2303 if ($cleanalsojavascript) {
2304 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2305 }
2306
2307 // escape quotes and backslashes, newlines, etc.
2308 if ($escapeonlyhtmltags) {
2309 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2310 } else {
2311 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2312 // Because content can be or not already HTML.
2313 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2314 // In a future, we should not need this
2315
2316 $tmp = (string) $stringtoescape;
2317
2318 // We protect the 6 special entities that we don't want to decode.
2319 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2320 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2321 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2322 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2323 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2324 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2325
2326 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2327
2328 // We restore the 6 special entities that we don't want to have been decoded by previous command
2329 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2330 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2331 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2332 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2333 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2334 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2335
2336 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2337 }
2338 if (!$keepb) {
2339 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2340 }
2341 if (!$keepn) {
2342 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2343 } elseif ($keepn == -1) {
2344 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2345 }
2346
2347 if ($escapeonlyhtmltags) {
2348 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2349 return $tmp;
2350 } else {
2351 // Now we protect all the tags we want to keep
2352 $tmparrayoftags = array();
2353 if ($noescapetags) {
2354 $tmparrayoftags = explode(',', $noescapetags);
2355 }
2356
2357 if (count($tmparrayoftags)) {
2358 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2359
2360 $reg = array();
2361 // Remove reserved keywords. They are forbidden in a source string
2362 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2363
2364 foreach ($tmparrayoftags as $tagtoreplace) {
2365 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2366 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2367 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2368 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2369
2370 // For case of tag with attributes
2371 do {
2372 $tmpold = $tmp;
2373
2374 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2375 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2376 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2377 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2378 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2379 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2380 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2381 }
2382
2383 $diff = strcmp($tmpold, $tmp);
2384 } while ($diff);
2385 }
2386
2387 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2388 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2389 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2390 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2391 }
2392
2393 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2394 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2395
2396 //print $result;
2397
2398 if (count($tmparrayoftags)) {
2399 // Restore protected tags
2400 foreach ($tmparrayoftags as $tagtoreplace) {
2401 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2402 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2403 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2404 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2405 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2406 }
2407
2408 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2409
2410 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2411 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2412 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2413 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2414 }
2415
2416 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2417
2418 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2419
2420 return $result;
2421 }
2422}
2423
2431function dol_strtolower($string, $encoding = "UTF-8")
2432{
2433 if (function_exists('mb_strtolower')) {
2434 return mb_strtolower($string, $encoding);
2435 } else {
2436 return strtolower($string);
2437 }
2438}
2439
2448function dol_strtoupper($string, $encoding = "UTF-8")
2449{
2450 if (function_exists('mb_strtoupper')) {
2451 return mb_strtoupper($string, $encoding);
2452 } else {
2453 return strtoupper($string);
2454 }
2455}
2456
2465function dol_ucfirst($string, $encoding = "UTF-8")
2466{
2467 if (function_exists('mb_substr')) {
2468 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2469 } else {
2470 return ucfirst($string);
2471 }
2472}
2473
2482function dol_ucwords($string, $encoding = "UTF-8")
2483{
2484 if (function_exists('mb_convert_case')) {
2485 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2486 } else {
2487 return ucwords($string);
2488 }
2489}
2490
2491
2497function getCallerInfoString()
2498{
2499 $backtrace = debug_backtrace();
2500 $msg = "";
2501 if (count($backtrace) >= 1) {
2502 $pos = 1;
2503 if (count($backtrace) == 1) {
2504 $pos = 0;
2505 }
2506 $trace = $backtrace[$pos];
2507 if (isset($trace['file'], $trace['line'])) {
2508 $msg = " From {$trace['file']}:{$trace['line']}.";
2509 }
2510 }
2511 return $msg;
2512}
2513
2536function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2537{
2538 global $conf, $user, $debugbar;
2539
2540 // If syslog module enabled
2541 if (!isModEnabled('syslog')) {
2542 return;
2543 }
2544
2545 // Check if we are into execution of code of a website
2546 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2547 global $website, $websitekey;
2548 if (is_object($website) && !empty($website->ref)) {
2549 $suffixinfilename .= '_website_'.$website->ref;
2550 } elseif (!empty($websitekey)) {
2551 $suffixinfilename .= '_website_'.$websitekey;
2552 }
2553 }
2554
2555 // Check if we have a forced suffix
2556 if (defined('USESUFFIXINLOG')) {
2557 $suffixinfilename .= constant('USESUFFIXINLOG');
2558 }
2559
2560 if ($ident < 0) {
2561 foreach ($conf->loghandlers as $loghandlerinstance) {
2562 $loghandlerinstance->setIdent($ident);
2563 }
2564 }
2565
2566 if (!empty($message)) {
2567 // Test log level
2568 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2569 $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');
2570
2571 if (!array_key_exists($level, $logLevels)) {
2572 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2573 $level = LOG_ERR;
2574 }
2575 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2576 return;
2577 }
2578
2579 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2580 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2581 }
2582
2583 // If adding log inside HTML page is required
2584 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2585 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2586 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2587 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2588
2589 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2590 }
2591
2592 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2593 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2594 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2595 print "\n\n<!-- Log start\n";
2596 print dol_escape_htmltag($message)."\n";
2597 print "Log end -->\n";
2598 }
2599
2600 $data = array(
2601 'message' => $message,
2602 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2603 'level' => $level,
2604 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2605 'ip' => '',
2606 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2607 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2608 );
2609
2610 // For log, we want the reliable IP first.
2611 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2612 if (!empty($remoteip)) {
2613 $data['ip'] = $remoteip;
2614 // This is when server run behind a reverse proxy
2615 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2616 // $data['ip'] is last
2617 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2618 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2619 $data['ip'] = '';
2620 $foundremoteip = 0;
2621 $j = 0;
2622 foreach ($tmpips as $tmpip) {
2623 $tmpip = trim($tmpip);
2624 if (strtolower($tmpip) == strtolower($remoteip)) {
2625 $foundremoteip = 1;
2626 }
2627 if (empty($data['ip'])) {
2628 $data['ip'] = $tmpip;
2629 } else {
2630 $j++;
2631 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2632 }
2633 }
2634 if (!$foundremoteip) {
2635 $j++;
2636 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2637 }
2638 $data['ip'] .= (($j > 0) ? ']' : '');
2639 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2640 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2641 $data['ip'] = '';
2642 $foundremoteip = 0;
2643 $j = 0;
2644 foreach ($tmpips as $tmpip) {
2645 $tmpip = trim($tmpip);
2646 if (strtolower($tmpip) == strtolower($remoteip)) {
2647 $foundremoteip = 1;
2648 }
2649 if (empty($data['ip'])) {
2650 $data['ip'] = $tmpip;
2651 } else {
2652 $j++;
2653 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2654 }
2655 }
2656 if (!$foundremoteip) {
2657 $j++;
2658 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2659 }
2660 $data['ip'] .= (($j > 0) ? ']' : '');
2661 }
2662 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2663 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2664 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2665 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2666 // 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).
2667 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2668 } else {
2669 $data['ip'] = '???';
2670 }
2671
2672 if (!empty($_SERVER['USERNAME'])) {
2673 // 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).
2674 $data['osuser'] = (string) $_SERVER['USERNAME'];
2675 } elseif (!empty($_SERVER['LOGNAME'])) {
2676 // 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).
2677 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2678 }
2679
2680 // Loop on each log handler and send output
2681 foreach ($conf->loghandlers as $loghandlerinstance) {
2682 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2683 continue;
2684 }
2685 $loghandlerinstance->export($data, $suffixinfilename);
2686 }
2687 unset($data);
2688 }
2689
2690 if ($ident > 0) {
2691 foreach ($conf->loghandlers as $loghandlerinstance) {
2692 $loghandlerinstance->setIdent($ident);
2693 }
2694 }
2695}
2696
2708function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2709{
2710 global $langs, $db;
2711
2712 $form = new Form($db);
2713
2714 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2715 if (empty($templatenameforexport)) {
2716 $templatenameforexport = 'website_'.$website->ref;
2717 }
2718
2719 $out = '';
2720 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2721
2722 // for generate popup
2723 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2724 $out .= 'jQuery(document).ready(function () {';
2725 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2726 $out .= ' var dialogHtml = \'';
2727
2728 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2729 $dialogcontent .= ' <div style="margin-top: 20px;">';
2730 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2731 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2732 $dialogcontent .= ' </div>';
2733 $dialogcontent .= ' <br>';
2734 $dialogcontent .= ' <div style="margin-top: 20px;">';
2735 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2736 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2737 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2738 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2739 $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>';
2740 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2741 $dialogcontent .= ' </form>';
2742 $dialogcontent .= ' </div>';
2743 $dialogcontent .= ' </div>';
2744
2745 $out .= dol_escape_js($dialogcontent);
2746
2747 $out .= '\';';
2748
2749
2750 // Add the content of the dialog to the body of the page
2751 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2752 $out .= ' if ($dialog.length > 0) {
2753 $dialog.remove();
2754 }
2755 jQuery("body").append(dialogHtml);';
2756
2757 // Configuration of popup
2758 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2759 $out .= ' autoOpen: false,';
2760 $out .= ' modal: true,';
2761 $out .= ' height: 290,';
2762 $out .= ' width: "40%",';
2763 $out .= ' title: "' . dol_escape_js($label) . '",';
2764 $out .= ' });';
2765
2766 // Simulate a click on the original "submit" input to export the site.
2767 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2768 $out .= ' console.log("Clic on exportsite.");';
2769 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2770 $out .= ' console.log("element founded:", target.length > 0);';
2771 $out .= ' if (target.length > 0) { target.click(); }';
2772 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2773 $out .= ' });';
2774
2775 // open popup
2776 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2777 $out .= ' return false;';
2778 $out .= ' });';
2779 $out .= '});';
2780 $out .= '</script>';
2781
2782 return $out;
2783}
2784
2785
2802function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
2803{
2804 global $conf;
2805
2806 if (strpos($url, '?') > 0) {
2807 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2808 } else {
2809 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2810 }
2811
2812 $out = '';
2813
2814 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2815 $out .= '<!-- a link for button to open url into a dialog popup -->';
2816 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2817 if (empty($conf->use_javascript_ajax)) {
2818 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2819 } elseif ($jsonopen) {
2820 $out .= ' href="#" onclick="'.$jsonopen.'"';
2821 } else {
2822 $out .= ' href="#"';
2823 }
2824 $out .= '>'.$buttonstring.'</a>';
2825
2826 if (!empty($conf->use_javascript_ajax)) {
2827 // Add code to open url using the popup.
2828 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2829 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2830
2831 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2832 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2833 jQuery(document).ready(function () {
2834 jQuery(".button_'.$name.'").click(function () {
2835 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2836 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2837 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2838 $tmpdialog.dialog({
2839 autoOpen: false,
2840 modal: true,
2841 height: (window.innerHeight - 150),
2842 width: \'80%\',
2843 title: \''.dol_escape_js($label).'\',
2844 open: function (event, ui) {
2845 console.log("open popup name='.$name.'");
2846 },
2847 close: function (event, ui) {
2848 console.log("Popup is closed, run jsonclose = '.$jsonclose.'");
2849 '.(empty($jsonclose) ? '' : $jsonclose.';').'
2850 }
2851 });
2852
2853 $tmpdialog.dialog(\'open\');
2854 return false;
2855 });
2856 });
2857 </script>';
2858 }
2859 return $out;
2860}
2861
2878function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2879{
2880 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2881}
2882
2900function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
2901{
2902 global $conf, $langs, $hookmanager;
2903
2904 // Show title
2905 $showtitle = 1;
2906 if (!empty($conf->dol_optimize_smallscreen)) {
2907 $showtitle = 0;
2908 }
2909
2910 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2911
2912 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2913 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2914 }
2915
2916 // Show right part
2917 if ($morehtmlright) {
2918 $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.
2919 }
2920
2921 // Show tabs
2922
2923 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2924 $maxkey = -1;
2925 if (is_array($links) && !empty($links)) {
2926 $keys = array_keys($links);
2927 if (count($keys)) {
2928 $maxkey = max($keys);
2929 }
2930 }
2931
2932 // Show tabs
2933 // if =0 we don't use the feature
2934 if (empty($limittoshow)) {
2935 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2936 }
2937 if (!empty($conf->dol_optimize_smallscreen)) {
2938 $limittoshow = 2;
2939 }
2940
2941 $displaytab = 0;
2942 $nbintab = 0;
2943 $popuptab = 0;
2944 $outmore = '';
2945 for ($i = 0; $i <= $maxkey; $i++) {
2946 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2947 // If active tab is already present
2948 if ($i >= $limittoshow) {
2949 $limittoshow--;
2950 }
2951 }
2952 }
2953
2954 for ($i = 0; $i <= $maxkey; $i++) {
2955 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2956 $isactive = true;
2957 } else {
2958 $isactive = false;
2959 }
2960
2961 if ($i < $limittoshow || $isactive) {
2962 // Output entry with a visible tab
2963 $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])).' -->';
2964
2965 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2966 if (!empty($links[$i][0])) {
2967 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2968 } else {
2969 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2970 }
2971 } elseif (!empty($links[$i][1])) {
2972 //print "x $i $active ".$links[$i][2]." z";
2973 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2974
2975 if (!empty($links[$i][0])) {
2976 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2977 $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).'">';
2978 }
2979
2980 if ($displaytab == 0 && $picto) {
2981 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2982 }
2983
2984 $out .= $links[$i][1];
2985 if (!empty($links[$i][0])) {
2986 $out .= '</a>'."\n";
2987 }
2988 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2989 $out .= '</div>';
2990 }
2991
2992 $out .= '</div>';
2993 } else {
2994 // Add entry into the combo popup with the other tabs
2995 if (!$popuptab) {
2996 $popuptab = 1;
2997 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2998 }
2999 $outmore_content = '';
3000
3001 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
3002 if (!empty($links[$i][0])) {
3003 $outmore_content .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
3004 } else {
3005 $outmore_content .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
3006 }
3007 } elseif (!empty($links[$i][1])) {
3008 $outmore_content .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
3009 $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.
3010 $outmore_content .= '</a>'."\n";
3011 }
3012 if ($outmore_content !== '') {
3013 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
3014 }
3015
3016 $nbintab++;
3017 }
3018
3019 $displaytab = $i + 1;
3020 }
3021 if ($popuptab) {
3022 $outmore .= '</div>';
3023 }
3024
3025 if ($popuptab) { // If there is some tabs not shown
3026 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3027 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3028 $widthofpopup = 240;
3029
3030 $tabsname = $moretabssuffix;
3031 if (empty($tabsname)) {
3032 $tabsname = str_replace("@", "", $picto);
3033 }
3034 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
3035 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3036 $out .= '<div class="tab valignmiddle"><a href="#" class="tab moretab inline-block tabunactive valignmiddle"><span class="hideonsmartphone">'.$langs->trans("More").'</span>... ('.$nbintab.')</a></div>'; // Do not use "reposition" class in the "More".
3037 }
3038 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
3039 $out .= $outmore;
3040 $out .= '</div>';
3041 $out .= '<div></div>';
3042 $out .= "</div>\n";
3043
3044 $out .= '<script nonce="'.getNonce().'">';
3045 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
3046 var x = this.offsetLeft, y = this.offsetTop;
3047 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3048 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
3049 $('#moretabsList".$tabsname."').css('".$right."','8px');
3050 }
3051 $('#moretabsList".$tabsname."').css('".$left."','auto');
3052 });
3053 ";
3054 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
3055 $out .= "</script>";
3056 }
3057
3058 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3059 $out .= "</div>\n";
3060 }
3061
3062 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3063 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
3064 $out .= ($morecssdiv ? ' '.$morecssdiv : '');
3065 $out .= '">'."\n";
3066 }
3067 if (!empty($dragdropfile)) {
3068 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3069 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3070 }
3071 $parameters = array('tabname' => $active, 'out' => $out);
3072 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3073 if ($reshook > 0) {
3074 $out = $hookmanager->resPrint;
3075 }
3076
3077 return $out;
3078}
3079
3087function dol_fiche_end($notab = 0)
3088{
3089 print dol_get_fiche_end($notab);
3090}
3091
3098function dol_get_fiche_end($notab = 0)
3099{
3100 if (!$notab || $notab == -1) {
3101 return "\n</div>\n";
3102 } else {
3103 return '';
3104 }
3105}
3106
3126function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3127{
3128 global $conf, $form, $user, $langs, $hookmanager, $action;
3129
3130 $error = 0;
3131
3132 $maxvisiblephotos = 1;
3133 $showimage = 1;
3134 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3135 // @phan-suppress-next-line PhanUndeclaredMethod
3136 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3137 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3138 $showbarcode = 0;
3139 }
3140 $modulepart = 'unknown';
3141
3142 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3143 $modulepart = $object->element;
3144 } elseif ($object->element == 'member') {
3145 $modulepart = 'memberphoto';
3146 } elseif ($object->element == 'user') {
3147 $modulepart = 'userphoto';
3148 }
3149
3150 if (class_exists("Imagick")) {
3151 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3152 $modulepart = $object->element;
3153 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3154 $modulepart = 'ficheinter';
3155 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3156 $modulepart = 'contract';
3157 } elseif ($object->element == 'order_supplier') {
3158 $modulepart = 'supplier_order';
3159 } elseif ($object->element == 'invoice_supplier') {
3160 $modulepart = 'supplier_invoice';
3161 }
3162 }
3163
3164 if ($object->element == 'product') {
3166 '@phan-var-force Product $object';
3167 $width = 80;
3168 $cssclass = 'photowithmargin photoref';
3169 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3170 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3171 if ($conf->browser->layout == 'phone') {
3172 $maxvisiblephotos = 1;
3173 }
3174 if ($showimage) {
3175 $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>';
3176 } else {
3177 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3178 $nophoto = '';
3179 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3180 } else { // Show no photo link
3181 $nophoto = '/public/theme/common/nophoto.png';
3182 $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>';
3183 }
3184 }
3185 } elseif ($object->element == 'category') {
3187 '@phan-var-force Categorie $object';
3188 $width = 80;
3189 $cssclass = 'photowithmargin photoref';
3190 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3191 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3192 if ($conf->browser->layout == 'phone') {
3193 $maxvisiblephotos = 1;
3194 }
3195 if ($showimage) {
3196 $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>';
3197 } else {
3198 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3199 $nophoto = '';
3200 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3201 } else { // Show no photo link
3202 $nophoto = '/public/theme/common/nophoto.png';
3203 $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>';
3204 }
3205 }
3206 } elseif ($object->element == 'bom') {
3208 '@phan-var-force Bom $object';
3209 $width = 80;
3210 $cssclass = 'photowithmargin photoref';
3211 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3212 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3213 if ($conf->browser->layout == 'phone') {
3214 $maxvisiblephotos = 1;
3215 }
3216 if ($showimage) {
3217 $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>';
3218 } else {
3219 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3220 $nophoto = '';
3221 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3222 } else { // Show no photo link
3223 $nophoto = '/public/theme/common/nophoto.png';
3224 $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>';
3225 }
3226 }
3227 } elseif ($object->element == 'ticket') {
3228 $width = 80;
3229 $cssclass = 'photoref';
3231 '@phan-var-force Ticket $object';
3232 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
3233 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3234 if ($conf->browser->layout == 'phone') {
3235 $maxvisiblephotos = 1;
3236 }
3237
3238 if ($showimage) {
3239 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3240 if ($object->nbphoto > 0) {
3241 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
3242 } else {
3243 $showimage = 0;
3244 }
3245 }
3246 if (!$showimage) {
3247 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3248 $nophoto = '';
3249 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3250 } else { // Show no photo link
3251 $nophoto = img_picto('No photo', 'object_ticket');
3252 $morehtmlleft .= '<!-- No photo to show -->';
3253 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3254 $morehtmlleft .= $nophoto;
3255 $morehtmlleft .= '</div></div>';
3256 }
3257 }
3258 } else {
3259 if ($showimage) {
3260 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3261 $phototoshow = '';
3262 // Check if a preview file is available
3263 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3264 $objectref = dol_sanitizeFileName($object->ref);
3265 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
3266 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3267 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3268 $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
3269 } else {
3270 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3271 }
3272 if (empty($subdir)) {
3273 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3274 }
3275
3276 $filepath = $dir_output.$subdir."/";
3277
3278 $filepdf = $filepath.$objectref.".pdf";
3279 $relativepath = $subdir.'/'.$objectref.'.pdf';
3280
3281 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3282 $fileimage = $filepdf.'_preview.png';
3283 $relativepathimage = $relativepath.'_preview.png';
3284
3285 $pdfexists = file_exists($filepdf);
3286
3287 // If PDF file exists
3288 if ($pdfexists) {
3289 // Conversion du PDF en image png si fichier png non existent
3290 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3291 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3292 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3293 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3294 if ($ret < 0) {
3295 $error++;
3296 }
3297 }
3298 }
3299 }
3300
3301 if ($pdfexists && !$error) {
3302 $heightforphotref = 80;
3303 if (!empty($conf->dol_optimize_smallscreen)) {
3304 $heightforphotref = 60;
3305 }
3306 // If the preview file is found
3307 if (file_exists($fileimage)) {
3308 $phototoshow = '<div class="photoref">';
3309 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3310 $phototoshow .= '</div>';
3311 }
3312 }
3313 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3314 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3315 }
3316
3317 if ($phototoshow) {
3318 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3319 $morehtmlleft .= $phototoshow;
3320 $morehtmlleft .= '</div>';
3321 }
3322 }
3323
3324 if (empty($phototoshow)) { // Show No photo link (picto of object)
3325 if ($object->element == 'action') {
3326 $width = 80;
3327 $cssclass = 'photorefcenter';
3328 $nophoto = img_picto('No photo', 'title_agenda');
3329 } else {
3330 $width = 14;
3331 $cssclass = 'photorefcenter';
3332 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3333 $prefix = 'object_';
3334 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3335 $picto = 'project'; // instead of projectpub
3336 }
3337 if (strpos($picto, 'fontawesome_') !== false) {
3338 $prefix = '';
3339 }
3340 $nophoto = img_picto('No photo', $prefix.$picto);
3341 }
3342 $morehtmlleft .= '<!-- No photo to show -->';
3343 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3344 $morehtmlleft .= $nophoto;
3345 $morehtmlleft .= '</div></div>';
3346 }
3347 }
3348 }
3349
3350 // Show barcode
3351 if ($showbarcode) {
3352 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3353 }
3354
3355 if ($object->element == 'societe') {
3356 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3357 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3358 } else {
3359 $morehtmlstatus .= $object->getLibStatut(6);
3360 }
3361 } elseif ($object->element == 'product') {
3362 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3363 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3364 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3365 } else {
3366 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3367 }
3368 $morehtmlstatus .= ' &nbsp; ';
3369 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3370 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3371 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3372 } else {
3373 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3374 }
3375 } elseif (in_array($object->element, array('salary'))) {
3376 '@phan-var-force Salary $object';
3377 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3378 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3379 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3380 }
3381 $morehtmlstatus .= $tmptxt;
3382 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3383 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3384 $totalallpayments = $object->getSommePaiement(0);
3385 $totalallpayments += $object->getSumCreditNotesUsed(0);
3386 $totalallpayments += $object->getSumDepositsUsed(0);
3387 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3388 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3389 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3390 }
3391 $morehtmlstatus .= $tmptxt;
3392 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3393 '@phan-var-force ChargeSociales|Loan|Tva $object';
3394 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3395 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3396 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3397 }
3398 $morehtmlstatus .= $tmptxt;
3399 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3400 if ($object->statut == 0) {
3401 $morehtmlstatus .= $object->getLibStatut(5);
3402 } else {
3403 $morehtmlstatus .= $object->getLibStatut(4);
3404 }
3405 } elseif ($object->element == 'facturerec') {
3406 '@phan-var-force FactureRec $object';
3407 if ($object->frequency == 0) {
3408 $morehtmlstatus .= $object->getLibStatut(2);
3409 } else {
3410 $morehtmlstatus .= $object->getLibStatut(5);
3411 }
3412 } elseif ($object->element == 'project_task') {
3413 $tmptxt = $object->getLibStatut(4);
3414 $morehtmlstatus .= $tmptxt;
3415 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3416 $tmptxt = $object->getLibStatut(6);
3417 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3418 $tmptxt = $object->getLibStatut(5);
3419 }
3420 $morehtmlstatus .= $tmptxt;
3421 }
3422
3423 // Say if object was dispatched/transferred "into accountancy"
3424 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3425 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3426 if (method_exists($object, 'getVentilExportCompta')) {
3427 $accounted = $object->getVentilExportCompta(1);
3428 $langs->load("accountancy");
3429 $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>';
3430 }
3431 }
3432
3433 // Add alias for thirdparty
3434 if (!empty($object->name_alias)) {
3435 '@phan-var-force Societe $object';
3436 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3437 }
3438
3439 // Add label
3440 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3441 if (!empty($object->label)) {
3442 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3443 }
3444 }
3445 // Show address and email
3446 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3447 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3448 if ($moreaddress) {
3449 $morehtmlref .= '<div class="refidno refaddress">';
3450 $morehtmlref .= $moreaddress;
3451 $morehtmlref .= '</div>';
3452 }
3453 }
3454 if (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id)) {
3455 $morehtmlref .= '<div style="clear: both;"></div>';
3456 $morehtmlref .= '<div class="refidno opacitymedium">';
3457 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3458 $morehtmlref .= '</div>';
3459 }
3460
3461 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3462 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3463 if ($reshook < 0) {
3464 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3465 } elseif (empty($reshook)) {
3466 $morehtmlref .= $hookmanager->resPrint;
3467 } elseif ($reshook > 0) {
3468 $morehtmlref = $hookmanager->resPrint;
3469 }
3470
3471 // $morehtml is the right part (link "Back to list")
3472 // $morehtmlleft is the picto or photo of banner
3473 // $morehtmlstatus is part under the status
3474 // $morehtmlright is part of htmlright
3475
3476 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3477 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3478 print '</div>';
3479 print '<div class="underrefbanner clearboth"></div>';
3480}
3481
3491function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3492{
3493 global $langs;
3494 $ret = '';
3495 if ($fieldrequired) {
3496 $ret .= '<span class="fieldrequired">';
3497 }
3498 $ret .= '<label for="'.$fieldkey.'">';
3499 $ret .= $langs->trans($langkey);
3500 $ret .= '</label>';
3501 if ($fieldrequired) {
3502 $ret .= '</span>';
3503 }
3504 return $ret;
3505}
3506
3520function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3521{
3522 global $langs, $hookmanager;
3523
3524 $ret = '';
3525 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3526
3527 // See format of addresses on https://en.wikipedia.org/wiki/Address
3528 // Address
3529 if (empty($mode)) {
3530 $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)));
3531 }
3532 // Zip/Town/State
3533 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3534 // US: title firstname name \n address lines \n town, state, zip \n country
3535 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3536 $ret .= (($ret && $town) ? $sep : '').$town;
3537
3538 if (!empty($object->state)) {
3539 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3540 }
3541 if (!empty($object->zip)) {
3542 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3543 }
3544 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3545 // UK: title firstname name \n address lines \n town state \n zip \n country
3546 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3547 $ret .= ($ret ? $sep : '').$town;
3548 if (!empty($object->state)) {
3549 $ret .= ($ret ? ", " : '').$object->state;
3550 }
3551 if (!empty($object->zip)) {
3552 $ret .= ($ret ? $sep : '').$object->zip;
3553 }
3554 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3555 // ES: title firstname name \n address lines \n zip town \n state \n country
3556 $ret .= ($ret ? $sep : '').$object->zip;
3557 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3558 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3559 if (!empty($object->state)) {
3560 $ret .= $sep.$object->state;
3561 }
3562 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3563 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3564 // See https://www.sljfaq.org/afaq/addresses.html
3565 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3566 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3567 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3568 // IT: title firstname name\n address lines \n zip town state_code \n country
3569 $ret .= ($ret ? $sep : '').$object->zip;
3570 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3571 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3572 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3573 } else {
3574 // Other: title firstname name \n address lines \n zip town[, state] \n country
3575 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3576 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3577 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3578 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3579 $ret .= ($ret ? ", " : '').$object->state;
3580 }
3581 }
3582
3583 if (!is_object($outputlangs)) {
3584 $outputlangs = $langs;
3585 }
3586 if ($withcountry) {
3587 $langs->load("dict");
3588 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3589 }
3590 if ($hookmanager) {
3591 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3592 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3593 if ($reshook > 0) {
3594 $ret = '';
3595 }
3596 $ret .= $hookmanager->resPrint;
3597 }
3598
3599 return $ret;
3600}
3601
3602
3603
3613function dol_strftime($fmt, $ts = false, $is_gmt = false)
3614{
3615 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3616 return dol_print_date($ts, $fmt, $is_gmt);
3617 } else {
3618 return 'Error date outside supported range';
3619 }
3620}
3621
3643function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3644{
3645 global $conf, $langs;
3646
3647 // If date undefined or "", we return ""
3648 if (dol_strlen((string) $time) == 0) {
3649 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3650 }
3651
3652 if ($tzoutput === 'auto') {
3653 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3654 }
3655
3656 // Clean parameters
3657 $to_gmt = false;
3658 $offsettz = $offsetdst = 0;
3659 if ($tzoutput) {
3660 $to_gmt = true; // For backward compatibility
3661 if (is_string($tzoutput)) {
3662 if ($tzoutput == 'tzserver') {
3663 $to_gmt = false;
3664 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3665 // @phan-suppress-next-line PhanPluginRedundantAssignment
3666 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3667 // @phan-suppress-next-line PhanPluginRedundantAssignment
3668 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3669 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3670 $to_gmt = true;
3671 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3672
3673 if (class_exists('DateTimeZone')) {
3674 $user_date_tz = new DateTimeZone($offsettzstring);
3675 $user_dt = new DateTime();
3676 $user_dt->setTimezone($user_date_tz);
3677 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3678 $offsettz = $user_dt->getOffset(); // should include dst ?
3679 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3680 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3681 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3682 }
3683 }
3684 }
3685 }
3686 if (!is_object($outputlangs)) {
3687 $outputlangs = $langs;
3688 }
3689 if (!$format) {
3690 $format = 'daytextshort';
3691 }
3692
3693 // Do we have to reduce the length of date (year on 2 chars) to save space.
3694 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3695 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3696 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3697 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3698 if ($formatwithoutreduce != $format) {
3699 $format = $formatwithoutreduce;
3700 $reduceformat = 1;
3701 } // so format 'dayreduceformat' is processed like day
3702
3703 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3704 // TODO Add format daysmallyear and dayhoursmallyear
3705 if ($format == 'day') {
3706 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3707 } elseif ($format == 'hour') {
3708 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3709 } elseif ($format == 'hourduration') {
3710 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3711 } elseif ($format == 'daytext') {
3712 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3713 } elseif ($format == 'daytextshort') {
3714 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3715 } elseif ($format == 'dayhour') {
3716 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3717 } elseif ($format == 'dayhoursec') {
3718 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3719 } elseif ($format == 'dayhourtext') {
3720 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3721 } elseif ($format == 'dayhourtextshort') {
3722 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3723 } elseif ($format == 'dayhourlog') {
3724 // Format not sensitive to language
3725 $format = '%Y%m%d%H%M%S';
3726 } elseif ($format == 'dayhourlogsmall') {
3727 // Format not sensitive to language
3728 $format = '%y%m%d%H%M';
3729 } elseif ($format == 'dayhourldap') {
3730 $format = '%Y%m%d%H%M%SZ';
3731 } elseif ($format == 'dayhourxcard') {
3732 $format = '%Y%m%dT%H%M%SZ';
3733 } elseif ($format == 'dayxcard') {
3734 $format = '%Y%m%d';
3735 } elseif ($format == 'dayrfc') {
3736 $format = '%Y-%m-%d'; // DATE_RFC3339
3737 } elseif ($format == 'dayhourrfc') {
3738 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3739 } elseif ($format == 'standard') {
3740 $format = '%Y-%m-%d %H:%M:%S';
3741 }
3742
3743 if ($reduceformat) {
3744 $format = str_replace('%Y', '%y', $format);
3745 $format = str_replace('yyyy', 'yy', $format);
3746 }
3747
3748 // Clean format
3749 if (preg_match('/%b/i', $format)) { // There is some text to translate
3750 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3751 $format = str_replace('%b', '__b__', $format);
3752 $format = str_replace('%B', '__B__', $format);
3753 }
3754 if (preg_match('/%a/i', $format)) { // There is some text to translate
3755 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3756 $format = str_replace('%a', '__a__', $format);
3757 $format = str_replace('%A', '__A__', $format);
3758 }
3759
3760 // Analyze date
3761 $reg = array();
3762 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
3763 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]));
3764 return '';
3765 } 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
3766 // This part of code should not be used anymore.
3767 dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]), LOG_WARNING);
3768 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3769 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3770 $syear = (!empty($reg[1]) ? $reg[1] : '');
3771 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3772 $sday = (!empty($reg[3]) ? $reg[3] : '');
3773 $shour = (!empty($reg[4]) ? $reg[4] : '');
3774 $smin = (!empty($reg[5]) ? $reg[5] : '');
3775 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3776
3777 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3778
3779 if ($to_gmt) {
3780 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3781 } else {
3782 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3783 }
3784 $dtts = new DateTime();
3785 $dtts->setTimestamp($time);
3786 $dtts->setTimezone($tzo);
3787 $newformat = str_replace(
3788 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3789 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3790 $format
3791 );
3792 $ret = $dtts->format($newformat);
3793 $ret = str_replace(
3794 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3795 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3796 $ret
3797 );
3798 } else {
3799 // Date is a timestamps
3800 if ($time < 100000000000) { // Protection against bad date values
3801 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3802
3803 if ($to_gmt) {
3804 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3805 } else {
3806 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3807 }
3808 $dtts = new DateTime();
3809 $dtts->setTimestamp($timetouse);
3810 $dtts->setTimezone($tzo);
3811 $newformat = str_replace(
3812 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3813 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3814 $format
3815 );
3816 $ret = $dtts->format($newformat);
3817 $ret = str_replace(
3818 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3819 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3820 $ret
3821 );
3822 //var_dump($ret);exit;
3823 } else {
3824 $ret = 'Bad value '.$time.' for date';
3825 }
3826 }
3827
3828 if (preg_match('/__b__/i', $format)) {
3829 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3830
3831 if ($to_gmt) {
3832 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3833 } else {
3834 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3835 }
3836 $dtts = new DateTime();
3837 $dtts->setTimestamp($timetouse);
3838 $dtts->setTimezone($tzo);
3839 $month = (int) $dtts->format("m");
3840 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3841 if ($encodetooutput) {
3842 $monthtext = $outputlangs->transnoentities('Month'.$month);
3843 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3844 } else {
3845 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3846 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3847 }
3848 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3849 $ret = str_replace('__b__', $monthtextshort, $ret);
3850 $ret = str_replace('__B__', $monthtext, $ret);
3851 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3852 //return $ret;
3853 }
3854 if (preg_match('/__a__/i', $format)) {
3855 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3856 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3857
3858 if ($to_gmt) {
3859 $tzo = new DateTimeZone('UTC');
3860 } else {
3861 $tzo = new DateTimeZone(date_default_timezone_get());
3862 }
3863 $dtts = new DateTime();
3864 $dtts->setTimestamp($timetouse);
3865 $dtts->setTimezone($tzo);
3866 $w = $dtts->format("w");
3867 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3868
3869 $ret = str_replace('__A__', $dayweek, $ret);
3870 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3871 }
3872
3873 return $ret;
3874}
3875
3876
3897function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3898{
3899 if ($timestamp === '') {
3900 return array();
3901 }
3902
3903 $datetimeobj = new DateTime();
3904 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3905 if ($forcetimezone) {
3906 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3907 }
3908 $arrayinfo = array(
3909 'year' => ((int) date_format($datetimeobj, 'Y')),
3910 'mon' => ((int) date_format($datetimeobj, 'm')),
3911 'mday' => ((int) date_format($datetimeobj, 'd')),
3912 'wday' => ((int) date_format($datetimeobj, 'w')),
3913 'yday' => ((int) date_format($datetimeobj, 'z')),
3914 'hours' => ((int) date_format($datetimeobj, 'H')),
3915 'minutes' => ((int) date_format($datetimeobj, 'i')),
3916 'seconds' => ((int) date_format($datetimeobj, 's')),
3917 '0' => $timestamp
3918 );
3919
3920 return $arrayinfo;
3921}
3922
3944function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3945{
3946 global $conf;
3947 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3948
3949 if ($gm === 'auto') {
3950 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3951 }
3952 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3953
3954 // Clean parameters
3955 if ($hour == -1 || empty($hour)) {
3956 $hour = 0;
3957 }
3958 if ($minute == -1 || empty($minute)) {
3959 $minute = 0;
3960 }
3961 if ($second == -1 || empty($second)) {
3962 $second = 0;
3963 }
3964
3965 // Check parameters
3966 if ($check) {
3967 if (!$month || !$day) {
3968 return '';
3969 }
3970 if ($day > 31) {
3971 return '';
3972 }
3973 if ($month > 12) {
3974 return '';
3975 }
3976 if ($hour < 0 || $hour > 24) {
3977 return '';
3978 }
3979 if ($minute < 0 || $minute > 60) {
3980 return '';
3981 }
3982 if ($second < 0 || $second > 60) {
3983 return '';
3984 }
3985 }
3986
3987 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3988 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3989 $localtz = new DateTimeZone($default_timezone);
3990 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3991 // We use dol_tz_string first because it is more reliable.
3992 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3993 try {
3994 $localtz = new DateTimeZone($default_timezone);
3995 } catch (Exception $e) {
3996 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3997 $default_timezone = @date_default_timezone_get();
3998 }
3999 } elseif (strrpos($gm, "tz,") !== false) {
4000 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
4001 try {
4002 $localtz = new DateTimeZone($timezone);
4003 } catch (Exception $e) {
4004 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
4005 }
4006 }
4007
4008 if (empty($localtz)) {
4009 $localtz = new DateTimeZone('UTC');
4010 }
4011 //var_dump($localtz);
4012 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
4013 $dt = new DateTime('now', $localtz);
4014 $dt->setDate((int) $year, (int) $month, (int) $day);
4015 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4016 $date = $dt->getTimestamp(); // should include daylight saving time
4017 //var_dump($date);
4018 return $date;
4019}
4020
4021
4032function dol_now($mode = 'auto')
4033{
4034 $ret = 0;
4035
4036 if ($mode === 'auto') {
4037 $mode = 'gmt';
4038 }
4039
4040 if ($mode == 'gmt') {
4041 $ret = time(); // Time for now at greenwich.
4042 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4043 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4044 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4045 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4046 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4047 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4048 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4049 // $ret=dol_now('gmt')+($tzsecond*3600);
4050 //}
4051 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4052 // Time for now with user timezone added
4053 //print 'time: '.time();
4054 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4055 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4056 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4057 }
4058
4059 return $ret;
4060}
4061
4062
4071function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4072{
4073 global $conf, $langs;
4074 $level = 1024;
4075
4076 if (!empty($conf->dol_optimize_smallscreen)) {
4077 $shortunit = 1;
4078 }
4079
4080 // Set value text
4081 if (empty($shortvalue) || $size < ($level * 10)) {
4082 $ret = $size;
4083 $textunitshort = $langs->trans("b");
4084 $textunitlong = $langs->trans("Bytes");
4085 } else {
4086 $ret = round($size / $level, 0);
4087 $textunitshort = $langs->trans("Kb");
4088 $textunitlong = $langs->trans("KiloBytes");
4089 }
4090 // Use long or short text unit
4091 if (empty($shortunit)) {
4092 $ret .= ' '.$textunitlong;
4093 } else {
4094 $ret .= ' '.$textunitshort;
4095 }
4096
4097 return $ret;
4098}
4099
4110function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4111{
4112 global $langs;
4113
4114 if (empty($url)) {
4115 return '';
4116 }
4117
4118 $linkstart = '<a href="';
4119 if (!preg_match('/^http/i', $url)) {
4120 $linkstart .= 'http://';
4121 }
4122 $linkstart .= $url;
4123 $linkstart .= '"';
4124 if ($target) {
4125 $linkstart .= ' target="'.$target.'"';
4126 }
4127 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
4128 $linkstart .= '>';
4129
4130 $link = '';
4131 if (!preg_match('/^http/i', $url)) {
4132 $link .= 'http://';
4133 }
4134 $link .= dol_trunc($url, $max);
4135
4136 $linkend = '</a>';
4137
4138 if ($morecss == 'float') { // deprecated
4139 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
4140 } else {
4141 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
4142 }
4143}
4144
4158function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
4159{
4160 global $user, $langs, $hookmanager;
4161
4162 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4163 //$showinvalid = 1; $email = 'rrrrr';
4164
4165 $newemail = dol_escape_htmltag($email);
4166
4167 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4168 $withpicto = 0;
4169 }
4170
4171 if (empty($email)) {
4172 return '&nbsp;';
4173 }
4174
4175 if ($addlink == 1) {
4176 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
4177 if (!preg_match('/^mailto:/i', $email)) {
4178 $newemail .= 'mailto:';
4179 }
4180 $newemail .= $email;
4181 $newemail .= '" target="_blank">';
4182
4183 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4184
4185 if ($max > 0) {
4186 $newemail .= dol_trunc($email, $max);
4187 } else {
4188 $newemail .= $email;
4189 }
4190 $newemail .= '</a>';
4191 if ($showinvalid && !isValidEmail($email)) {
4192 $langs->load("errors");
4193 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
4194 }
4195
4196 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4197 $type = 'AC_EMAIL';
4198 $linktoaddaction = '';
4199 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4200 $linktoaddaction = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.urlencode($type).'&amp;contactid='.((int) $cid).'&amp;socid='.((int) $socid).'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
4201 }
4202 if ($linktoaddaction) {
4203 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
4204 }
4205 }
4206 } elseif ($addlink === 'thirdparty') {
4207 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
4208 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4209 if ($withpicto == 1) {
4210 $tmpnewemail .= $newemail;
4211 }
4212 $tmpnewemail .= '</a>';
4213
4214 $newemail = $tmpnewemail;
4215 } else {
4216 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4217
4218 if ($showinvalid && !isValidEmail($email)) {
4219 $langs->load("errors");
4220 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
4221 }
4222 }
4223
4224 //$rep = '<div class="nospan" style="margin-right: 10px">';
4225 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4226 //$rep .= '</div>';
4227 $rep = $newemail;
4228
4229 if ($hookmanager) {
4230 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4231
4232 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4233 if ($reshook > 0) {
4234 $rep = '';
4235 }
4236 $rep .= $hookmanager->resPrint;
4237 }
4238
4239 return $rep;
4240}
4241
4247function getArrayOfSocialNetworks()
4248{
4249 global $conf, $db;
4250
4251 $socialnetworks = array();
4252 // Enable caching of array
4253 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
4254 $cachekey = 'socialnetworks_' . $conf->entity;
4255 $dataretrieved = dol_getcache($cachekey);
4256 if (!is_null($dataretrieved)) {
4257 $socialnetworks = $dataretrieved;
4258 } else {
4259 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
4260 $sql .= " WHERE entity=".$conf->entity;
4261 $resql = $db->query($sql);
4262 if ($resql) {
4263 while ($obj = $db->fetch_object($resql)) {
4264 $socialnetworks[$obj->code] = array(
4265 'rowid' => $obj->rowid,
4266 'label' => $obj->label,
4267 'url' => $obj->url,
4268 'icon' => $obj->icon,
4269 'active' => $obj->active,
4270 );
4271 }
4272 }
4273 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4274 }
4275 return $socialnetworks;
4276}
4277
4288function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4289{
4290 global $hookmanager, $langs, $user;
4291
4292 $htmllink = $value;
4293
4294 if (empty($value)) {
4295 return '&nbsp;';
4296 }
4297
4298 if (!empty($type)) {
4299 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4300 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4301 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4302 if ($type == 'skype') {
4303 $htmllink .= dol_escape_htmltag($value);
4304 $htmllink .= '&nbsp; <a href="skype:';
4305 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4306 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4307 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4308 $htmllink .= '</a><a href="skype:';
4309 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4310 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4311 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4312 $htmllink .= '</a>';
4313 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4314 $addlink = 'AC_SKYPE';
4315 $link = '';
4316 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4317 $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>';
4318 }
4319 $htmllink .= ($link ? ' ' . $link : '');
4320 }
4321 } else {
4322 if (!empty($dictsocialnetworks[$type]['url'])) {
4323 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4324 if ($tmpvirginurl) {
4325 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4326 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4327
4328 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4329 if ($tmpvirginurl3) {
4330 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4331 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4332 }
4333
4334 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4335 if ($tmpvirginurl2) {
4336 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4337 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4338 }
4339 }
4340 if (preg_match('/^https?:\/\//i', $value)) {
4341 $link = $value;
4342 } else {
4343 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4344 }
4345 $valuetoshow = $value;
4346 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4347 if (preg_match('/^https?:\/\//i', $link)) {
4348 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4349 } else {
4350 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4351 }
4352 } else {
4353 $htmllink .= dol_escape_htmltag($value);
4354 }
4355 }
4356 $htmllink .= '</div>';
4357 } else {
4358 $langs->load("errors");
4359 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4360 }
4361
4362 if ($hookmanager) {
4363 $parameters = array(
4364 'value' => $value,
4365 'cid' => $contactid,
4366 'socid' => $socid,
4367 'type' => $type,
4368 'dictsocialnetworks' => $dictsocialnetworks,
4369 );
4370
4371 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4372 if ($reshook > 0) {
4373 $htmllink = '';
4374 }
4375 $htmllink .= $hookmanager->resPrint;
4376 }
4377
4378 return $htmllink;
4379}
4380
4390function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4391{
4392 global $mysoc;
4393
4394 if (empty($profID) || empty($profIDtype)) {
4395 return '';
4396 }
4397 if (empty($countrycode)) {
4398 $countrycode = $mysoc->country_code;
4399 }
4400 $newProfID = $profID;
4401 $id = substr($profIDtype, -1);
4402 $ret = '';
4403 if (strtoupper($countrycode) == 'FR') {
4404 // France
4405 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4406
4407 if ($id == 1 && dol_strlen($newProfID) == 9) {
4408 // SIREN (ex: 123 123 123)
4409 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4410 }
4411 if ($id == 2 && dol_strlen($newProfID) == 14) {
4412 // SIRET (ex: 123 123 123 12345)
4413 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4414 }
4415 if ($id == 3 && dol_strlen($newProfID) == 5) {
4416 // NAF/APE (ex: 69.20Z)
4417 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4418 }
4419 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4420 // TVA intracommunautaire (ex: FR12 123 123 123)
4421 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4422 }
4423 }
4424 if (!empty($addcpButton)) {
4425 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4426 } else {
4427 $ret = $newProfID;
4428 }
4429 return $ret;
4430}
4431
4447function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4448{
4449 global $conf, $user, $langs, $mysoc, $hookmanager;
4450
4451 // Clean phone parameter
4452 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4453 if (empty($phone)) {
4454 return '';
4455 }
4456 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4457 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4458 }
4459 if (empty($countrycode) && is_object($mysoc)) {
4460 $countrycode = $mysoc->country_code;
4461 }
4462
4463 // Short format for small screens
4464 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4465 $separ = '';
4466 }
4467
4468 $newphone = $phone;
4469 $newphonewa = $phone;
4470 if (strtoupper($countrycode) == "FR") {
4471 // France
4472 if (dol_strlen($phone) == 10) {
4473 $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);
4474 } elseif (dol_strlen($phone) == 7) {
4475 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4476 } elseif (dol_strlen($phone) == 9) {
4477 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4478 } elseif (dol_strlen($phone) == 11) {
4479 $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);
4480 } elseif (dol_strlen($phone) == 12) {
4481 $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);
4482 } elseif (dol_strlen($phone) == 13) {
4483 $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);
4484 }
4485 } elseif (strtoupper($countrycode) == "CA") {
4486 if (dol_strlen($phone) == 10) {
4487 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4488 }
4489 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4490 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4491 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4492 }
4493 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4494 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4495 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4496 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4497 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4498 }
4499 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4500 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4501 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4502 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4503 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4504 }
4505 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4506 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4507 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4508 }
4509 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4510 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4511 $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);
4512 }
4513 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4514 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4515 $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);
4516 }
4517 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4518 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4519 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4520 }
4521 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4522 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4523 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4524 }
4525 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4526 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4527 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4528 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4529 $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);
4530 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4531 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4532 }
4533 } elseif (strtoupper($countrycode) == "ML") {//Mali
4534 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4535 $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);
4536 }
4537 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4538 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4539 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4540 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4541 $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);
4542 }
4543 } elseif (strtoupper($countrycode) == "MU") {
4544 //Maurice
4545 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4546 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4547 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4548 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4549 }
4550 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4551 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4552 $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);
4553 }
4554 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4555 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4556 $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);
4557 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4558 $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);
4559 }
4560 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4561 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4562 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4563 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4564 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4565 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4566 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4567 }
4568 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4569 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4570 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4571 }
4572 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4573 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4574 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4575 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4576 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4577 }
4578 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4579 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4580 $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);
4581 }
4582 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4583 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4584 $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);
4585 }
4586 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4587 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4588 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 1).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4589 }
4590 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4591 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4592 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4593 }
4594 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4595 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4596 $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);
4597 }
4598 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4599 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4600 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4601 }
4602 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4603 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4604 $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);
4605 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4606 $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);
4607 }
4608 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4609 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4610 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4611 }
4612 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4613 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4614 $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);
4615 }
4616 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4617 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4618 $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);
4619 }
4620 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4621 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4622 $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);
4623 }
4624 } elseif (strtoupper($countrycode) == "IT") {//Italie
4625 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4626 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4627 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4628 $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);
4629 }
4630 } elseif (strtoupper($countrycode) == "AU") {
4631 //Australie
4632 if (dol_strlen($phone) == 12) {
4633 //ex: +61_A_BCDE_FGHI
4634 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4635 }
4636 } elseif (strtoupper($countrycode) == "LU") {
4637 // Luxembourg
4638 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4639 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4640 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4641 $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);
4642 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4643 $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);
4644 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4645 $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);
4646 }
4647 } elseif (strtoupper($countrycode) == "PE") {
4648 // Peru
4649 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4650 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4651 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4652 $newphonewa = '+51'.$newphone;
4653 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3);
4654 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4655 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4656 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4657 $newphonewa = $newphone;
4658 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4659 }
4660 } elseif (strtoupper($countrycode) == "IN") {//India
4661 if (dol_strlen($phone) == 13) {
4662 if ($withpicto == 'phone') {//ex: +91_AB_CDEF_GHIJ
4663 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 4).$separ.substr($newphone, 9, 4);
4664 } else {//ex: +91_ABCDE_FGHIJ
4665 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 5).$separ.substr($newphone, 8, 5);
4666 }
4667 }
4668 }
4669
4670 $newphoneastart = $newphoneaend = '';
4671 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4672 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
4673 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4674 $newphoneaend .= '</a>';
4675 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4676 if (empty($user->clicktodial_loaded)) {
4677 $user->fetch_clicktodial();
4678 }
4679
4680 // Define urlmask
4681 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4682 if (!empty($user->clicktodial_url)) {
4683 $urlmask = $user->clicktodial_url;
4684 }
4685
4686 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4687 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4688 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4689 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4690 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4691 // Those lines are for substitution
4692 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4693 '__PHONETO__' => urlencode($phone),
4694 '__LOGIN__' => $clicktodial_login,
4695 '__PASS__' => $clicktodial_password);
4696 $url = make_substitutions($url, $substitarray);
4697 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4698 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4699 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4700 $newphoneaend = '</a>';
4701 } else {
4702 // Old method
4703 $newphoneastart = '<a href="'.$url.'"';
4704 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4705 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4706 }
4707 $newphoneastart .= '>';
4708 $newphoneaend .= '</a>';
4709 }
4710 }
4711
4712 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4713 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4714 $type = 'AC_TEL';
4715 $addlinktoagenda = '';
4716 if ($addlink == 'AC_FAX') {
4717 $type = 'AC_FAX';
4718 }
4719 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4720 $addlinktoagenda = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage='. urlencode($_SERVER['REQUEST_URI']) .'&amp;actioncode='.$type.($cid ? '&amp;contactid='.$cid : '').($socid ? '&amp;socid='.$socid : '').'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
4721 }
4722 if ($addlinktoagenda) {
4723 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4724 }
4725 }
4726 }
4727
4728 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4729 // Link to Whatsapp
4730 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4731 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4732 }
4733
4734 if (empty($titlealt)) {
4735 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4736 }
4737 $rep = '';
4738
4739 if ($hookmanager) {
4740 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4741 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4742 $rep .= $hookmanager->resPrint;
4743 }
4744 if (empty($reshook)) {
4745 $picto = '';
4746 if ($withpicto) {
4747 if ($withpicto == 'fax') {
4748 $picto = 'phoning_fax';
4749 } elseif ($withpicto == 'phone') {
4750 $picto = 'phoning';
4751 } elseif ($withpicto == 'mobile') {
4752 $picto = 'phoning_mobile';
4753 } else {
4754 $picto = '';
4755 }
4756 }
4757 if ($adddivfloat == 1) {
4758 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4759 } elseif (empty($adddivfloat)) {
4760 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4761 }
4762
4763 $rep .= $newphoneastart;
4764 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4765 if ($separ != 'hidenum') {
4766 $rep .= ($withpicto ? ' ' : '').$newphone;
4767 }
4768 $rep .= $newphoneaend;
4769
4770 if ($adddivfloat == 1) {
4771 $rep .= '</div>';
4772 } elseif (empty($adddivfloat)) {
4773 $rep .= '</span>';
4774 }
4775 }
4776
4777 return $rep;
4778}
4779
4788function dol_print_ip($ip, $mode = 0, $showname = 0)
4789{
4790 global $conf;
4791
4792 $ret = '';
4793 if (!isset($conf->cache['resolveips'])) {
4794 $conf->cache['resolveips'] = array();
4795 }
4796
4797 if ($mode != 2) {
4798 $countrycode = dolGetCountryCodeFromIp($ip);
4799 if ($countrycode) { // If success, countrycode is us, fr, ...
4800 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4801 $ret .= picto_from_langcode($countrycode);
4802 // $ret .= img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4803 } else {
4804 $ret .= '('.$countrycode.')';
4805 }
4806 $ret .= '&nbsp;';
4807 } else {
4808 // Nothing
4809 }
4810 }
4811
4812 if (in_array($mode, [0, 2])) {
4813 $domain = '';
4814 if ($showname) {
4815 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
4816 $domain = gethostbyaddr($ip);
4817 $conf->cache['resolveips'][$ip] = $domain; // false or domain
4818 } else {
4819 $domain = $conf->cache['resolveips'][$ip];
4820 }
4821 }
4822 if ($domain) {
4823 $ret .= $domain;
4824 } else {
4825 $ret .= $ip;
4826 }
4827 }
4828
4829 return $ret;
4830}
4831
4844function getUserRemoteIP($trusted = 0)
4845{
4846 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
4847 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
4848 // 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)
4849 // This can happen if the proxy were added in the list of trusted proxy.
4850 return $ip;
4851 }
4852
4853 // Try to guess the real IP of client (but this may not be reliable)
4854 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4855 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
4856 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4857 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
4858 } else {
4859 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4860 }
4861 } else {
4862 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
4863 }
4864 } else {
4865 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
4866 }
4867 return $ip;
4868}
4869
4878function isHTTPS()
4879{
4880 $isSecure = false;
4881 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4882 $isSecure = true;
4883 } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') {
4884 $isSecure = true;
4885 }
4886 return $isSecure;
4887}
4888
4895function dolGetCountryCodeFromIp($ip)
4896{
4897 $countrycode = '';
4898
4899 if (isModEnabled('geoipmaxmind')) {
4900 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4901 //$ip='24.24.24.24';
4902 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4903 if ($datafile) {
4904 try {
4905 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4906 $geoip = new DolGeoIP('country', $datafile);
4907 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4908 $countrycode = $geoip->getCountryCodeFromIP($ip);
4909 } catch (Exception $e) {
4910 //print 'Error with GeoIP database: '.$e->getMessage();
4911 }
4912 }
4913 }
4914
4915 return $countrycode;
4916}
4917
4918
4925function dol_user_country()
4926{
4927 global $conf, $langs, $user;
4928
4929 //$ret=$user->xxx;
4930 $ret = '';
4931 if (isModEnabled('geoipmaxmind')) {
4932 $ip = getUserRemoteIP();
4933 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4934 //$ip='24.24.24.24';
4935 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4936 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4937 $geoip = new DolGeoIP('country', $datafile);
4938 $countrycode = $geoip->getCountryCodeFromIP($ip);
4939 $ret = $countrycode;
4940 }
4941 return $ret;
4942}
4943
4956function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4957{
4958 global $hookmanager;
4959
4960 $out = '';
4961
4962 if ($address) {
4963 if ($hookmanager) {
4964 $parameters = array('element' => $element, 'id' => $id);
4965 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4966 $out .= $hookmanager->resPrint;
4967 }
4968 if (empty($reshook)) {
4969 if (empty($charfornl)) {
4970 $out .= nl2br((string) $address);
4971 } else {
4972 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
4973 }
4974
4975 // TODO Remove this block, we can add this using the hook now
4976 $showgmap = $showomap = 0;
4977 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4978 $showgmap = 1;
4979 }
4980 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4981 $showgmap = 1;
4982 }
4983 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4984 $showgmap = 1;
4985 }
4986 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4987 $showgmap = 1;
4988 }
4989 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4990 $showomap = 1;
4991 }
4992 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4993 $showomap = 1;
4994 }
4995 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4996 $showomap = 1;
4997 }
4998 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4999 $showomap = 1;
5000 }
5001 if ($showgmap) {
5002 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
5003 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
5004 }
5005 if ($showomap) {
5006 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
5007 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
5008 }
5009 }
5010 }
5011 if ($noprint) {
5012 return $out;
5013 } else {
5014 print $out;
5015 return null;
5016 }
5017}
5018
5019
5029function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5030{
5031 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5032 return true;
5033 }
5034 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5035 return true;
5036 }
5037 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5038 return true;
5039 }
5040
5041 return false;
5042}
5043
5053function isValidMXRecord($domain)
5054{
5055 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5056 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5057 return 0;
5058 }
5059 if (function_exists('getmxrr')) {
5060 $mxhosts = array();
5061 $weight = array();
5062 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5063 if (count($mxhosts) > 1) {
5064 return 1;
5065 }
5066 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5067 return 1;
5068 }
5069
5070 return 0;
5071 }
5072 }
5073
5074 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5075 return -1;
5076}
5077
5085function isValidPhone($phone)
5086{
5087 return true;
5088}
5089
5090
5100function dolGetFirstLetters($s, $nbofchar = 1)
5101{
5102 $ret = '';
5103 $tmparray = explode(' ', $s);
5104 foreach ($tmparray as $tmps) {
5105 $ret .= dol_substr($tmps, 0, $nbofchar);
5106 }
5107
5108 return $ret;
5109}
5110
5111
5119function dol_strlen($string, $stringencoding = 'UTF-8')
5120{
5121 if (is_null($string)) {
5122 return 0;
5123 }
5124
5125 if (function_exists('mb_strlen')) {
5126 return mb_strlen($string, $stringencoding);
5127 } else {
5128 return strlen($string);
5129 }
5130}
5131
5142function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5143{
5144 global $langs;
5145
5146 if (empty($stringencoding)) {
5147 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5148 }
5149
5150 $ret = '';
5151 if (empty($trunconbytes)) {
5152 if (function_exists('mb_substr')) {
5153 $ret = mb_substr($string, $start, $length, $stringencoding);
5154 } else {
5155 $ret = substr($string, $start, $length);
5156 }
5157 } else {
5158 if (function_exists('mb_strcut')) {
5159 $ret = mb_strcut($string, $start, $length, $stringencoding);
5160 } else {
5161 $ret = substr($string, $start, $length);
5162 }
5163 }
5164 return $ret;
5165}
5166
5167
5181function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5182{
5183 global $conf;
5184
5185 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5186 return $string;
5187 }
5188
5189 if (empty($stringencoding)) {
5190 $stringencoding = 'UTF-8';
5191 }
5192 // reduce for small screen
5193 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5194 $size = round($size / 3);
5195 }
5196
5197 // We go always here
5198 if ($trunc == 'right') {
5199 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5200 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5201 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5202 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
5203 } else {
5204 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5205 return $string;
5206 }
5207 } elseif ($trunc == 'middle') {
5208 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5209 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5210 $size1 = (int) round($size / 2);
5211 $size2 = (int) round($size / 2);
5212 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5213 } else {
5214 return $string;
5215 }
5216 } elseif ($trunc == 'left') {
5217 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5218 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5219 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5220 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5221 } else {
5222 return $string;
5223 }
5224 } elseif ($trunc == 'wrap') {
5225 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5226 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5227 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5228 } else {
5229 return $string;
5230 }
5231 } else {
5232 return 'BadParam3CallingDolTrunc';
5233 }
5234}
5235
5243function getPictoForType($key, $morecss = '')
5244{
5245 // Set array with type -> picto
5246 $type2picto = array(
5247 'varchar' => 'font',
5248 'text' => 'font',
5249 'html' => 'code',
5250 'int' => 'sort-numeric-down',
5251 'double' => 'sort-numeric-down',
5252 'price' => 'currency',
5253 'pricecy' => 'multicurrency',
5254 'password' => 'key',
5255 'boolean' => 'check-square',
5256 'date' => 'calendar',
5257 'datetime' => 'calendar',
5258 'duration' => 'hourglass',
5259 'phone' => 'phone',
5260 'mail' => 'email',
5261 'url' => 'url',
5262 'ip' => 'country',
5263 'select' => 'list',
5264 'sellist' => 'list',
5265 'stars' => 'fontawesome_star_fas',
5266 'radio' => 'check-circle',
5267 'checkbox' => 'list',
5268 'chkbxlst' => 'list',
5269 'link' => 'link',
5270 'icon' => "question",
5271 'point' => "country",
5272 'multipts' => 'country',
5273 'linestrg' => "country",
5274 'polygon' => "country",
5275 'separate' => 'minus'
5276 );
5277
5278 if (!empty($type2picto[$key])) {
5279 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5280 }
5281
5282 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5283}
5284
5285
5308function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5309{
5310 global $conf;
5311
5312 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5313 $url = DOL_URL_ROOT;
5314 $theme = isset($conf->theme) ? $conf->theme : null;
5315 $path = 'theme/'.$theme;
5316 if (empty($picto)) {
5317 $picto = 'generic';
5318 }
5319
5320 // Define fullpathpicto to use into src
5321 if ($pictoisfullpath) {
5322 // Clean parameters
5323 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5324 $picto .= '.png';
5325 }
5326 $fullpathpicto = $picto;
5327 $reg = array();
5328 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5329 $morecss .= ($morecss ? ' ' : '').$reg[1];
5330 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5331 }
5332 } else {
5333 // $picto can not be null since replaced with 'generic' in that case
5334 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5335 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5336 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5337 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5338
5339 // Fix some values of $pictowithouttext
5340 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5341 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5342 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5343 }
5344
5345 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5346 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5347 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5348 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5349
5350 // Compatibility with old fontawesome versions
5351 if ($pictowithouttext == 'file-o') {
5352 $pictowithouttext = 'file';
5353 }
5354
5355 $pictowithouttextarray = explode('_', $pictowithouttext);
5356 $marginleftonlyshort = 0;
5357
5358 if (!empty($pictowithouttextarray[1])) {
5359 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5360 $fakey = 'fa-'.$pictowithouttextarray[0];
5361 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5362 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5363 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5364 } else {
5365 $fakey = 'fa-'.$pictowithouttext;
5366 $faprefix = 'fas';
5367 $facolor = '';
5368 $fasize = '';
5369 }
5370
5371 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5372 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5373 $morestyle = '';
5374 $reg = array();
5375 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5376 $morecss .= ($morecss ? ' ' : '').$reg[1];
5377 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5378 }
5379 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5380 $morestyle = $reg[1];
5381 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5382 }
5383 $moreatt = trim($moreatt);
5384
5385 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5386 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5387 $enabledisablehtml .= '</span>';
5388
5389 return $enabledisablehtml;
5390 }
5391
5392 if (empty($srconly) && in_array($pictowithouttext, getImgPictoNameList())) {
5393 $fakey = $pictowithouttext;
5394 $facolor = '';
5395 $fasize = '';
5396 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5397 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'))) {
5398 $fa = 'far';
5399 }
5400 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'))) {
5401 $fa = 'fab';
5402 }
5403
5404 $arrayconvpictotofa = getImgPictoConv('fa');
5405
5406 if ($pictowithouttext == 'off') {
5407 $fakey = 'fa-square';
5408 $fasize = '1.3em';
5409 } elseif ($pictowithouttext == 'on') {
5410 $fakey = 'fa-check-square';
5411 $fasize = '1.3em';
5412 } elseif ($pictowithouttext == 'listlight') {
5413 $fakey = 'fa-download';
5414 $marginleftonlyshort = 1;
5415 } elseif ($pictowithouttext == 'printer') {
5416 $fakey = 'fa-print';
5417 $fasize = '1.2em';
5418 } elseif ($pictowithouttext == 'note') {
5419 $fakey = 'fa-sticky-note';
5420 $marginleftonlyshort = 1;
5421 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5422 $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');
5423 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5424 if (preg_match('/selected/', $pictowithouttext)) {
5425 $facolor = '#888';
5426 }
5427 $marginleftonlyshort = 1;
5428 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5429 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5430 } else {
5431 $fakey = 'fa-'.$pictowithouttext;
5432 }
5433
5434 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5435 $morecss .= ' em092';
5436 }
5437 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5438 $morecss .= ' em088';
5439 }
5440 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5441 $morecss .= ' em080';
5442 }
5443
5444 // Define $marginleftonlyshort
5445 $arrayconvpictotomarginleftonly = array(
5446 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5447 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5448 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5449 );
5450 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5451 $marginleftonlyshort = 0;
5452 }
5453
5454 // Add CSS
5455 $arrayconvpictotomorcess = array(
5456 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5457 'bank_account' => 'infobox-bank_account',
5458 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5459 'bookcal' => 'infobox-action',
5460 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5461 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5462 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5463 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5464 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5465 'incoterm' => 'infobox-supplier_proposal',
5466 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5467 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5468 'order' => 'infobox-commande',
5469 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5470 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5471 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'info_black' => 'font-status1', 'invoice' => 'infobox-commande',
5472 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5473 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5474 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5475 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5476 'resource' => 'infobox-action',
5477 'salary' => 'infobox-bank_account', 'shapes' => 'infobox-adherent', 'shipment' => 'infobox-commande', 'stripe' => 'infobox-bank_account', 'supplier_invoice' => 'infobox-order_supplier', 'supplier_invoicea' => 'infobox-order_supplier', 'supplier_invoiced' => 'infobox-order_supplier',
5478 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5479 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5480 'vat' => 'infobox-bank_account',
5481 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5482 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode', 'calendarpertype' => 'imgforviewmode'
5483 );
5484 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5485 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5486 }
5487
5488 // Define $color
5489 $arrayconvpictotocolor = array(
5490 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5491 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5492 'dynamicprice' => '#a69944',
5493 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5494 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5495 'lock' => '#ddd', 'lot' => '#a69944',
5496 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5497 'other' => '#ddd', 'world' => '#986c6a',
5498 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5499 //'shipment'=>'#a69944',
5500 'search-plus' => '#808080', 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5501 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5502 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5503 'website' => '#304', 'workstation' => '#a69944'
5504 );
5505 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5506 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5507 }
5508
5509 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5510 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5511 $morestyle = '';
5512 $reg = array();
5513 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5514 $morecss .= ($morecss ? ' ' : '').$reg[1];
5515 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5516 }
5517 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5518 $morestyle = $reg[1];
5519 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5520 }
5521 $moreatt = trim($moreatt);
5522
5523 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5524 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5525 $enabledisablehtml .= '</span>';
5526
5527 return $enabledisablehtml;
5528 }
5529
5530 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5531 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5532 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5533 $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
5534 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5535 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5536 }
5537
5538 // If we ask an image into $url/$mymodule/img (instead of default path)
5539 $regs = array();
5540 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5541 $picto = $regs[1];
5542 $path = $regs[2]; // $path is $mymodule
5543 }
5544
5545 // Clean parameters
5546 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5547 $picto .= '.png';
5548 }
5549 // If alt path are defined, define url where img file is, according to physical path
5550 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5551 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5552 if ($type == 'main') {
5553 continue;
5554 }
5555 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5556 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5557 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5558 break;
5559 }
5560 }
5561
5562 // $url is '' or '/custom', $path is current theme or
5563 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5564 }
5565
5566 if ($srconly) {
5567 return $fullpathpicto;
5568 }
5569
5570 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5571 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
5572}
5573
5579function getImgPictoNameList()
5580{
5581 return array(
5582 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5583 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5584 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5585 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5586 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5587 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5588 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-up',
5589 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5590 'commercial', 'companies',
5591 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5592 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5593 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5594 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5595 'hands-helping', 'help', 'holiday',
5596 'id-card', 'images', 'incoterm', 'info', 'info_black', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5597 'key', 'knowledgemanagement',
5598 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5599 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5600 'off', 'on', 'order',
5601 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5602 'resize', 'search', 'service', 'stats', 'stock',
5603 'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_grey', 'switch_on_warning', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
5604 'discord', 'facebook', 'flickr', 'instagram','linkedin', 'github', 'google', 'jabber', 'meetup', 'microsoft', 'skype', 'slack', 'twitter', 'pinterest', 'reddit', 'snapchat', 'tumblr', 'youtube', 'viadeo', 'google-plus-g', 'whatsapp',
5605 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5606 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5607 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5608 'technic', 'ticket',
5609 'error', 'warning',
5610 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring', 'rss',
5611 'search-plus', 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'store', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5612 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5613 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'upload', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5614 'conferenceorbooth', 'eventorganization',
5615 'stamp', 'signature',
5616 'webportal'
5617 );
5618}
5619
5627function getImgPictoConv($mode = 'fa')
5628{
5629 global $conf;
5630
5631 // Array when the fa picto key is different than the Dolibarr picto key.
5632 $arrayconvpictotofa = array(
5633 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5634 'asset' => 'money-check-alt', 'autofill' => 'fill',
5635 'back' => 'arrow-left', 'bank_account' => 'university',
5636 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5637 'bookcal' => 'calendar-check',
5638 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5639 'bom' => 'shapes',
5640 'card' => 'address-card', 'chart' => 'chart-line', 'company' => 'building', 'contact' => 'address-book', 'contract' => 'suitcase', 'collab' => 'people-arrows', 'conversation' => 'comments', 'country' => 'globe-americas', 'cron' => 'business-time', 'cross' => 'times',
5641 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5642 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5643 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5644 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5645 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5646 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5647 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5648 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5649 'generic' => 'file', 'holiday' => 'umbrella-beach',
5650 'info' => 'info-circle', 'info_black' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5651 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5652 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5653 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5654 'sign-out' => 'sign-out-alt',
5655 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_grey' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
5656 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5657 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table', 'calendarpertype' => 'table',
5658 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5659 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5660 'other' => 'square',
5661 'playdisabled' => 'play', 'pdf' => 'file-pdf', 'poll' => 'check-double', 'pos' => 'cash-register', 'preview' => 'binoculars', 'project' => 'project-diagram', 'projectpub' => 'project-diagram', 'projecttask' => 'tasks', 'propal' => 'file-signature', 'proposal' => 'file-signature',
5662 'partnership' => 'handshake', 'payment' => 'money-check-alt', 'payment_vat' => 'money-check-alt', 'pictoconfirm' => 'check-square', 'phoning' => 'phone', 'phoning_mobile' => 'mobile-alt', 'phoning_fax' => 'fax', 'previous' => 'arrow-alt-circle-left', 'printer' => 'print', 'product' => 'cube', 'puce' => 'angle-right',
5663 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5664 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5665 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5666 'service' => 'concierge-bell',
5667 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5668 'status' => 'stop-circle',
5669 'stripe' => 'stripe-s', 'supplier' => 'building',
5670 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5671 'title_agenda' => 'calendar-alt',
5672 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5673 'jabber' => 'comment',
5674 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5675 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5676 'webportal' => 'door-open'
5677 );
5678
5679 if ($conf->currency == 'EUR') {
5680 $arrayconvpictotofa['currency'] = 'euro-sign';
5681 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5682 } else {
5683 $arrayconvpictotofa['currency'] = 'dollar-sign';
5684 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5685 }
5686
5687 return $arrayconvpictotofa;
5688}
5689
5690
5705function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
5706{
5707 if (strpos($picto, '^') === 0) {
5708 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5709 } else {
5710 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5711 }
5712}
5713
5725function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5726{
5727 global $conf;
5728
5729 if (is_numeric($picto)) {
5730 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5731 //$picto = $leveltopicto[$picto];
5732 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5733 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5734 $picto .= '.png';
5735 }
5736
5737 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5738
5739 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5740}
5741
5753function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5754{
5755 global $conf;
5756
5757 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5758 $picto .= '.png';
5759 }
5760
5761 if ($pictoisfullpath) {
5762 $path = $picto;
5763 } else {
5764 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5765
5766 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5767 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5768
5769 if (file_exists($themepath)) {
5770 $path = $themepath;
5771 }
5772 }
5773 }
5774
5775 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5776}
5777
5791function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5792{
5793 global $langs;
5794
5795 if (empty($titlealt) || $titlealt == 'default') {
5796 if ($numaction == '-1' || $numaction == 'ST_NO') {
5797 $numaction = -1;
5798 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5799 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5800 $numaction = 0;
5801 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5802 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5803 $numaction = 1;
5804 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5805 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5806 $numaction = 2;
5807 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5808 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5809 $numaction = 3;
5810 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5811 } else {
5812 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5813 $numaction = 0;
5814 }
5815 }
5816 if (!is_numeric($numaction)) {
5817 $numaction = 0;
5818 }
5819
5820 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5821}
5822
5830function img_pdf($titlealt = 'default', $size = 3)
5831{
5832 global $langs;
5833
5834 if ($titlealt == 'default') {
5835 $titlealt = $langs->trans('Show');
5836 }
5837
5838 return img_picto($titlealt, 'pdf'.$size.'.png');
5839}
5840
5848function img_edit_add($titlealt = 'default', $other = '')
5849{
5850 global $langs;
5851
5852 if ($titlealt == 'default') {
5853 $titlealt = $langs->trans('Add');
5854 }
5855
5856 return img_picto($titlealt, 'edit_add.png', $other);
5857}
5865function img_edit_remove($titlealt = 'default', $other = '')
5866{
5867 global $langs;
5868
5869 if ($titlealt == 'default') {
5870 $titlealt = $langs->trans('Remove');
5871 }
5872
5873 return img_picto($titlealt, 'edit_remove.png', $other);
5874}
5875
5884function img_edit($titlealt = 'default', $float = 0, $other = '')
5885{
5886 global $langs;
5887
5888 if ($titlealt == 'default') {
5889 $titlealt = $langs->trans('Modify');
5890 }
5891
5892 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5893}
5894
5903function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5904{
5905 global $langs;
5906
5907 if ($titlealt == 'default') {
5908 $titlealt = $langs->trans('View');
5909 }
5910
5911 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5912
5913 return img_picto($titlealt, 'eye', $moreatt);
5914}
5915
5924function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5925{
5926 global $langs;
5927
5928 if ($titlealt == 'default') {
5929 $titlealt = $langs->trans('Delete');
5930 }
5931
5932 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5933}
5934
5942function img_printer($titlealt = "default", $other = '')
5943{
5944 global $langs;
5945 if ($titlealt == "default") {
5946 $titlealt = $langs->trans("Print");
5947 }
5948 return img_picto($titlealt, 'printer.png', $other);
5949}
5950
5958function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5959{
5960 global $langs;
5961
5962 if ($titlealt == 'default') {
5963 $titlealt = $langs->trans('Split');
5964 }
5965
5966 return img_picto($titlealt, 'split.png', $other);
5967}
5968
5976function img_help($usehelpcursor = 1, $usealttitle = 1)
5977{
5978 global $langs;
5979
5980 if ($usealttitle) {
5981 if (is_string($usealttitle)) {
5982 $usealttitle = dol_escape_htmltag($usealttitle);
5983 } else {
5984 $usealttitle = $langs->trans('Info');
5985 }
5986 }
5987
5988 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5989}
5990
5997function img_info($titlealt = 'default')
5998{
5999 global $langs;
6000
6001 if ($titlealt == 'default') {
6002 $titlealt = $langs->trans('Informations');
6003 }
6004
6005 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
6006}
6007
6016function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6017{
6018 global $langs;
6019
6020 if ($titlealt == 'default') {
6021 $titlealt = $langs->trans('Warning');
6022 }
6023
6024 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6025 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
6026}
6027
6034function img_error($titlealt = 'default')
6035{
6036 global $langs;
6037
6038 if ($titlealt == 'default') {
6039 $titlealt = $langs->trans('Error');
6040 }
6041
6042 return img_picto($titlealt, 'error.png');
6043}
6044
6052function img_next($titlealt = 'default', $moreatt = '')
6053{
6054 global $langs;
6055
6056 if ($titlealt == 'default') {
6057 $titlealt = $langs->trans('Next');
6058 }
6059
6060 //return img_picto($titlealt, 'next.png', $moreatt);
6061 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
6062}
6063
6071function img_previous($titlealt = 'default', $moreatt = '')
6072{
6073 global $langs;
6074
6075 if ($titlealt == 'default') {
6076 $titlealt = $langs->trans('Previous');
6077 }
6078
6079 //return img_picto($titlealt, 'previous.png', $moreatt);
6080 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
6081}
6082
6091function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6092{
6093 global $langs;
6094
6095 if ($titlealt == 'default') {
6096 $titlealt = $langs->trans('Down');
6097 }
6098
6099 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
6100}
6101
6110function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6111{
6112 global $langs;
6113
6114 if ($titlealt == 'default') {
6115 $titlealt = $langs->trans('Up');
6116 }
6117
6118 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
6119}
6120
6129function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6130{
6131 global $langs;
6132
6133 if ($titlealt == 'default') {
6134 $titlealt = $langs->trans('Left');
6135 }
6136
6137 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
6138}
6139
6148function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6149{
6150 global $langs;
6151
6152 if ($titlealt == 'default') {
6153 $titlealt = $langs->trans('Right');
6154 }
6155
6156 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
6157}
6158
6166function img_allow($allow, $titlealt = 'default')
6167{
6168 global $langs;
6169
6170 if ($titlealt == 'default') {
6171 $titlealt = $langs->trans('Active');
6172 }
6173
6174 if ($allow == 1) {
6175 return img_picto($titlealt, 'tick.png');
6176 }
6177
6178 return '-';
6179}
6180
6188function img_credit_card($brand, $morecss = null)
6189{
6190 if (is_null($morecss)) {
6191 $morecss = 'fa-2x';
6192 }
6193
6194 if ($brand == 'visa' || $brand == 'Visa') {
6195 $brand = 'cc-visa';
6196 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6197 $brand = 'cc-mastercard';
6198 } elseif ($brand == 'amex' || $brand == 'American Express') {
6199 $brand = 'cc-amex';
6200 } elseif ($brand == 'discover' || $brand == 'Discover') {
6201 $brand = 'cc-discover';
6202 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6203 $brand = 'cc-jcb';
6204 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6205 $brand = 'cc-diners-club';
6206 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6207 $brand = 'credit-card';
6208 }
6209
6210 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
6211}
6212
6221function img_mime($file, $titlealt = '', $morecss = '')
6222{
6223 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
6224
6225 $mimetype = dol_mimetype($file, '', 1);
6226 //$mimeimg = dol_mimetype($file, '', 2);
6227 $mimefa = dol_mimetype($file, '', 4);
6228
6229 if (empty($titlealt)) {
6230 $titlealt = 'Mime type: '.$mimetype;
6231 }
6232
6233 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6234 return '<i class="fa fa-'.$mimefa.' '.(preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ').($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.dolPrintHTMLForAttribute($titlealt).'"' : '').'></i>';
6235}
6236
6237
6245function img_search($titlealt = 'default', $other = '')
6246{
6247 global $langs;
6248
6249 if ($titlealt == 'default') {
6250 $titlealt = $langs->trans('Search');
6251 }
6252
6253 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
6254
6255 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
6256 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6257
6258 return $input;
6259}
6260
6268function img_searchclear($titlealt = 'default', $other = '')
6269{
6270 global $langs;
6271
6272 if ($titlealt == 'default') {
6273 $titlealt = $langs->trans('Search');
6274 }
6275
6276 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6277
6278 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
6279 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6280
6281 return $input;
6282}
6283
6296function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6297{
6298 global $conf, $langs;
6299
6300 if ($infoonimgalt) {
6301 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
6302 } else {
6303 if (empty($conf->use_javascript_ajax)) {
6304 $textfordropdown = '';
6305 }
6306
6307 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
6308 $fa = 'info-circle';
6309 if ($picto == 'warning') {
6310 $fa = 'exclamation-triangle';
6311 }
6312 $result = ($nodiv ? '' : '<div class="wordbreak '.$class.($morecss ? ' '.$morecss : '').($textfordropdown ? ' hidden' : '').'">').'<span class="fa fa-'.$fa.'" title="'.dol_escape_htmltag($admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).'"></span> ';
6313 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6314 $result .= ($nodiv ? '' : '</div>');
6315
6316 if ($textfordropdown) {
6317 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
6318 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
6319 jQuery(document).ready(function() {
6320 jQuery(".'.$class.'text").click(function() {
6321 console.log("toggle text");
6322 jQuery(".'.$class.'").toggle();
6323 });
6324 });
6325 </script>';
6326
6327 $result = $tmpresult.$result;
6328 }
6329 }
6330
6331 return $result;
6332}
6333
6334
6346function dol_print_error($db = null, $error = '', $errors = null)
6347{
6348 global $conf, $langs, $user, $argv;
6349 global $dolibarr_main_prod;
6350
6351 $out = '';
6352 $syslog = '';
6353
6354 // If error occurs before the $lang object was loaded
6355 if (!$langs) {
6356 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6357 $langs = new Translate('', $conf);
6358 $langs->load("main");
6359 }
6360
6361 // Load translation files required by the error messages
6362 $langs->loadLangs(array('main', 'errors'));
6363
6364 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6365 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6366 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6367 $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";
6368 }
6369 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6370
6371 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6372 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6373 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6374 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6375 }
6376 if ($user instanceof User) {
6377 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6378 }
6379 if (function_exists("phpversion")) {
6380 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6381 }
6382 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6383 if (function_exists("php_uname")) {
6384 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6385 }
6386 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6387 $out .= "<br>\n";
6388 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6389 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6390 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6391 $out .= "<br>\n";
6392 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6393 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6394 } else { // Mode CLI
6395 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6396 $syslog .= "pid=".dol_getmypid();
6397 }
6398
6399 if (!empty($conf->modules)) {
6400 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6401 }
6402
6403 if (is_object($db)) {
6404 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6405 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6406 $lastqueryerror = $db->lastqueryerror();
6407 if (!utf8_check($lastqueryerror)) {
6408 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6409 }
6410 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6411 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6412 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6413 $out .= "<br>\n";
6414 } else { // Mode CLI
6415 // No dol_escape_htmltag for output, we are in CLI mode
6416 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6417 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6418 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6419 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6420 }
6421 $syslog .= ", sql=".$db->lastquery();
6422 $syslog .= ", db_error=".$db->lasterror();
6423 }
6424
6425 if ($error || $errors) {
6426 // Merge all into $errors array
6427 if (is_array($error) && is_array($errors)) {
6428 $errors = array_merge($error, $errors);
6429 } elseif (is_array($error)) { // deprecated, use second parameters
6430 $errors = $error;
6431 } elseif (is_array($errors) && !empty($error)) {
6432 $errors = array_merge(array($error), $errors);
6433 } elseif (!empty($error)) {
6434 $errors = array_merge(array($error), array($errors));
6435 }
6436
6437 $langs->load("errors");
6438
6439 foreach ($errors as $msg) {
6440 if (empty($msg)) {
6441 continue;
6442 }
6443 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6444 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6445 } else { // Mode CLI
6446 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6447 }
6448 $syslog .= ", msg=".$msg;
6449 }
6450 }
6451 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6452 xdebug_print_function_stack();
6453 $out .= '<b>XDebug information:</b>'."<br>\n";
6454 $out .= 'File: '.xdebug_call_file()."<br>\n";
6455 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6456 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6457 $out .= "<br>\n";
6458 }
6459
6460 // Return a http header with error code if possible
6461 if (!headers_sent()) {
6462 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6463 top_httphead();
6464 }
6465 //http_response_code(500); // If we use 500, message is not output with some command line tools
6466 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
6467 }
6468
6469 if (empty($dolibarr_main_prod)) {
6470 print $out;
6471 } else {
6472 if (empty($langs->defaultlang)) {
6473 $langs->setDefaultLang();
6474 }
6475 $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.
6476 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6477 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";
6478 print $langs->trans("DolibarrHasDetectedError").'. ';
6479 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6480 if (!defined("MAIN_CORE_ERROR")) {
6481 define("MAIN_CORE_ERROR", 1);
6482 }
6483 }
6484
6485 dol_syslog("Error ".$syslog, LOG_ERR);
6486}
6487
6498function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6499{
6500 global $langs;
6501
6502 if (empty($email)) {
6503 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6504 }
6505
6506 $langs->load("errors");
6507 $now = dol_now();
6508
6509 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6510 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6511 if ($errormessage) {
6512 print '<br><br>'.$errormessage;
6513 }
6514 if (is_array($errormessages) && count($errormessages)) {
6515 foreach ($errormessages as $mesgtoshow) {
6516 print '<br><br>'.$mesgtoshow;
6517 }
6518 }
6519 print '</div></div>';
6520}
6521
6538function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6539{
6540 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6541}
6542
6561function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6562{
6563 global $langs, $form;
6564 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6565
6566 if ($moreattrib == 'class="right"') {
6567 $prefix .= 'right '; // For backward compatibility
6568 }
6569
6570 $sortorder = strtoupper((string) $sortorder);
6571 $out = '';
6572 $sortimg = '';
6573
6574 $tag = 'th';
6575 if ($thead == 2) {
6576 $tag = 'div';
6577 }
6578
6579 $tmpsortfield = explode(',', (string) $sortfield);
6580 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6581 $tmpfield = explode(',', $field);
6582 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6583
6584 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6585 $prefix = 'wrapcolumntitle '.$prefix;
6586 }
6587
6588 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6589 // If field is used as sort criteria we use a specific css class liste_titre_sel
6590 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6591 $liste_titre = 'liste_titre';
6592 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6593 $liste_titre = 'liste_titre_sel';
6594 }
6595
6596 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6597 //$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)).'"' : '');
6598 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dolPrintHTMLForAttribute($langs->trans($name)).'"' : '';
6599 $tagstart .= '>';
6600
6601 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6602 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6603 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6604 $options = preg_replace('/&+/i', '&', $options);
6605 if (!preg_match('/^&/', $options)) {
6606 $options = '&'.$options;
6607 }
6608
6609 $sortordertouseinlink = '';
6610 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6611 if (preg_match('/^DESC/i', $sortorder)) {
6612 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6613 } else { // We reverse the var $sortordertouseinlink
6614 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6615 }
6616 } else { // We are on field that is the first current sorting criteria
6617 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6618 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6619 } else {
6620 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6621 }
6622 }
6623 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6624 $out .= '<a class="reposition" href="'.$file.'?sortfield='.urlencode($field).'&sortorder='.urlencode($sortordertouseinlink).'&begin='.urlencode($begin).$options.'"';
6625 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
6626 $out .= '>';
6627 }
6628 if ($tooltip) {
6629 // You can also use 'TranslationString:keyfortooltiponclick:tooltipdirection' for a tooltip on click or to change tooltip position.
6630 if (strpos($tooltip, ':') !== false) {
6631 $tmptooltip = explode(':', $tooltip);
6632 } else {
6633 $tmptooltip = array($tooltip);
6634 }
6635 $out .= $form->textwithpicto($langs->trans((string) $name), $langs->trans($tmptooltip[0]), (empty($tmptooltip[2]) ? '1' : $tmptooltip[2]), 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6636 } else {
6637 $out .= $langs->trans((string) $name);
6638 }
6639
6640 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6641 $out .= '</a>';
6642 }
6643
6644 if (empty($thead) && $field) { // If this is a sort field
6645 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6646 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6647 $options = preg_replace('/&+/i', '&', $options);
6648 if (!preg_match('/^&/', $options)) {
6649 $options = '&'.$options;
6650 }
6651
6652 if (!$sortorder || ($field1 != $sortfield1)) {
6653 // Nothing
6654 } else {
6655 if (preg_match('/^DESC/', $sortorder)) {
6656 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6657 }
6658 if (preg_match('/^ASC/', $sortorder)) {
6659 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6660 }
6661 }
6662 }
6663
6664 $tagend = '</'.$tag.'>';
6665
6666 $out = $tagstart.$sortimg.$out.$tagend;
6667
6668 return $out;
6669}
6670
6679function print_titre($title)
6680{
6681 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6682
6683 print '<div class="titre">'.$title.'</div>';
6684}
6685
6697function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6698{
6699 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6700}
6701
6715function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6716{
6717 $return = '';
6718
6719 if ($picto == 'setup') {
6720 $picto = 'generic';
6721 }
6722
6723 $return .= "\n";
6724 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6725 $return .= '<tr class="toptitle">';
6726 if ($picto) {
6727 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6728 }
6729 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6730 $return .= '<div class="titre inline-block">';
6731 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6732 $return .= '</div>';
6733 $return .= '</td>';
6734 if (dol_strlen($morehtmlcenter)) {
6735 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6736 }
6737 if (dol_strlen($morehtmlright)) {
6738 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6739 }
6740 $return .= '</tr></table>'."\n";
6741
6742 return $return;
6743}
6744
6768function 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 = '')
6769{
6770 global $conf, $langs;
6771
6772 $savlimit = $limit;
6773 $savtotalnboflines = $totalnboflines;
6774 if (is_numeric($totalnboflines)) {
6775 $totalnboflines = abs($totalnboflines);
6776 }
6777
6778 // Detect if there is a subtitle
6779 $subtitle = '';
6780 $tmparray = preg_split('/<br>/i', $title, 2);
6781 if (!empty($tmparray[1])) {
6782 $title = $tmparray[0];
6783 $subtitle = $tmparray[1];
6784 }
6785
6786 $page = (int) $page;
6787
6788 if ($picto == 'setup') {
6789 $picto = 'title_setup.png';
6790 }
6791 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6792 $picto = 'title.gif';
6793 }
6794 if ($limit < 0) {
6795 $limit = $conf->liste_limit;
6796 }
6797
6798 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6799 $nextpage = 1;
6800 } else {
6801 $nextpage = 0;
6802 }
6803 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6804
6805 print "\n";
6806 print "<!-- Begin print_barre_liste -->\n";
6807 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6808 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6809
6810 // Left
6811
6812 if ($picto && $title) {
6813 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6814 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6815 print '</td>';
6816 }
6817
6818 print '<td class="nobordernopadding valignmiddle col-title">';
6819 print '<div class="titre inline-block">';
6820 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()
6821 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6822 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6823 }
6824 print '</div>';
6825 if (!empty($subtitle)) {
6826 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6827 }
6828 print '</td>';
6829
6830 // Center
6831 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6832 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6833 }
6834
6835 // Right
6836 print '<td class="nobordernopadding valignmiddle right col-right">';
6837 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6838 if ($sortfield) {
6839 $options .= "&sortfield=".urlencode($sortfield);
6840 }
6841 if ($sortorder) {
6842 $options .= "&sortorder=".urlencode($sortorder);
6843 }
6844 // Show navigation bar
6845 $pagelist = '';
6846 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6847 if ($totalnboflines) { // If we know total nb of lines
6848 // Define nb of extra page links before and after selected page + ... + first or last
6849 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6850
6851 if ($limit > 0) {
6852 $nbpages = ceil($totalnboflines / $limit);
6853 } else {
6854 $nbpages = 1;
6855 }
6856 $cpt = ($page - $maxnbofpage);
6857 if ($cpt < 0) {
6858 $cpt = 0;
6859 }
6860
6861 if ($cpt >= 1) {
6862 if (empty($pagenavastextinput)) {
6863 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6864 if ($cpt > 2) {
6865 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6866 } elseif ($cpt == 2) {
6867 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6868 }
6869 }
6870 }
6871
6872 do {
6873 if ($pagenavastextinput) {
6874 if ($cpt == $page) {
6875 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone heightofcombo" name="pageplusone" value="'.($page + 1).'"></li>';
6876 $pagelist .= '/';
6877 }
6878 } else {
6879 if ($cpt == $page) {
6880 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6881 } else {
6882 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6883 }
6884 }
6885 $cpt++;
6886 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6887
6888 if (empty($pagenavastextinput)) {
6889 if ($cpt < $nbpages) {
6890 if ($cpt < $nbpages - 2) {
6891 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6892 } elseif ($cpt == $nbpages - 2) {
6893 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6894 }
6895 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6896 }
6897 } else {
6898 //var_dump($page.' '.$cpt.' '.$nbpages);
6899 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6900 }
6901 } else {
6902 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6903 }
6904 }
6905
6906 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6907 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
6908 }
6909
6910 // js to autoselect page field on focus
6911 if ($pagenavastextinput) {
6912 print ajax_autoselect('.pageplusone');
6913 }
6914
6915 print '</td>';
6916 print '</tr>';
6917
6918 print '</table>'."\n";
6919
6920 // Center
6921 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6922 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6923 }
6924
6925 print "<!-- End title -->\n\n";
6926}
6927
6944function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6945{
6946 global $conf, $langs;
6947
6948 print '<div class="pagination"><ul>';
6949 if ($beforearrows) {
6950 print '<li class="paginationbeforearrows">';
6951 print $beforearrows;
6952 print '</li>';
6953 }
6954
6955 if (empty($hidenavigation)) {
6956 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6957 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6958 $pagesizechoices .= ',5000:5000';
6959 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6960 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6961 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6962 //$pagesizechoices .= ',2:2';
6963 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6964 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6965 }
6966
6967 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6968 print '<li class="pagination">';
6969 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.'">';
6970 print '<datalist id="limitlist">';
6971 } else {
6972 print '<li class="paginationcombolimit valignmiddle">';
6973 print '<select id="limit'.(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix).'" name="limit" class="flat selectlimit nopadding maxwidth75 center'.(is_numeric($selectlimitsuffix) ? '' : ' '.$selectlimitsuffix).'" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6974 }
6975 $tmpchoice = explode(',', $pagesizechoices);
6976 $tmpkey = $limit.':'.$limit;
6977 if (!in_array($tmpkey, $tmpchoice)) {
6978 $tmpchoice[$tmpkey] = $tmpkey;
6979 }
6980 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6981 if (!in_array($tmpkey, $tmpchoice)) {
6982 $tmpchoice[$tmpkey] = $tmpkey;
6983 }
6984 asort($tmpchoice, SORT_NUMERIC);
6985 foreach ($tmpchoice as $val) {
6986 $selected = '';
6987 $tmp = explode(':', $val);
6988 $key = $tmp[0];
6989 $val = $tmp[1];
6990 if ($key != '' && $val != '') {
6991 if ((int) $key == (int) $limit) {
6992 $selected = ' selected="selected"';
6993 }
6994 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6995 }
6996 }
6997 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6998 print '</datalist>';
6999 } else {
7000 print '</select>';
7001 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
7002 //print ajax_combobox("limit");
7003 }
7004
7005 if ($conf->use_javascript_ajax) {
7006 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
7007 <script>
7008 jQuery(document).ready(function () {
7009 jQuery(".selectlimit").change(function() {
7010 console.log("We change limit so we submit the form");
7011 $(this).parents(\'form:first\').submit();
7012 });
7013 });
7014 </script>
7015 ';
7016 }
7017 print '</li>';
7018 }
7019 if ($page > 0) {
7020 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>';
7021 }
7022 if ($betweenarrows) {
7023 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7024 print $betweenarrows;
7025 print '<!--</div>-->';
7026 }
7027 if ($nextpage > 0) {
7028 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>';
7029 }
7030 if ($afterarrows) {
7031 print '<li class="paginationafterarrows">';
7032 print $afterarrows;
7033 print '</li>';
7034 }
7035 }
7036 print '</ul></div>'."\n";
7037}
7038
7039
7051function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7052{
7053 $morelabel = '';
7054
7055 if (preg_match('/%/', $rate)) {
7056 $rate = str_replace('%', '', $rate);
7057 $addpercent = true;
7058 }
7059 $reg = array();
7060 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7061 $morelabel = ' ('.$reg[1].')';
7062 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
7063 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
7064 }
7065 if (preg_match('/\*/', $rate)) {
7066 $rate = str_replace('*', '', $rate);
7067 $info_bits |= 1;
7068 }
7069
7070 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7071 if (!preg_match('/\//', $rate)) {
7072 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
7073 } else {
7074 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7075 $ret = $rate.($addpercent ? '%' : '');
7076 }
7077 if (($info_bits & 1) && $usestarfornpr >= 0) {
7078 $ret .= ' *';
7079 }
7080 $ret .= $morelabel;
7081 return $ret;
7082}
7083
7084
7100function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7101{
7102 global $langs, $conf;
7103
7104 // Clean parameters
7105 if (empty($amount)) {
7106 $amount = 0; // To have a numeric value if amount not defined or = ''
7107 }
7108 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7109 if ($rounding == -1) {
7110 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7111 }
7112 $nbdecimal = $rounding;
7113
7114 if ($outlangs === 'none') {
7115 // Use international separators
7116 $dec = '.';
7117 $thousand = '';
7118 } else {
7119 // Output separators by default (french)
7120 $dec = ',';
7121 $thousand = ' ';
7122
7123 // If $outlangs not forced, we use use language
7124 if (!($outlangs instanceof Translate)) {
7125 $outlangs = $langs;
7126 }
7127
7128 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7129 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7130 }
7131 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7132 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7133 }
7134 if ($thousand == 'None') {
7135 $thousand = '';
7136 } elseif ($thousand == 'Space') {
7137 $thousand = ' ';
7138 }
7139 }
7140 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7141
7142 //print "amount=".$amount."-";
7143 $amount = str_replace(',', '.', $amount); // should be useless
7144 //print $amount."-";
7145 $data = explode('.', $amount);
7146 $decpart = isset($data[1]) ? $data[1] : '';
7147 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
7148 //print "decpart=".$decpart."<br>";
7149 $end = '';
7150
7151 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7152 if (dol_strlen($decpart) > $nbdecimal) {
7153 $nbdecimal = dol_strlen($decpart);
7154 }
7155
7156 // If nbdecimal is higher than max to show
7157 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7158 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7159 $nbdecimal = $nbdecimalmaxshown;
7160 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7161 // If output is truncated, we show ...
7162 $end = '...';
7163 }
7164 }
7165
7166 // If force rounding
7167 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7168 if ($forcerounding === 'MU') {
7169 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7170 } elseif ($forcerounding === 'MT') {
7171 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7172 } elseif ($forcerounding >= 0) {
7173 $nbdecimal = (int) $forcerounding;
7174 }
7175 }
7176
7177 // Format number
7178 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7179 // Add symbol of currency if requested
7180 $cursymbolbefore = $cursymbolafter = '';
7181 if ($currency_code && is_object($outlangs)) {
7182 if ($currency_code == 'auto') {
7183 $currency_code = $conf->currency;
7184 }
7185
7186 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7187 $listoflanguagesbefore = array('nl_NL');
7188 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7189 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7190 } else {
7191 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7192 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
7193 }
7194 }
7195 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
7196 if ($form) {
7197 $output = preg_replace('/\s/', '&nbsp;', $output);
7198 $output = preg_replace('/\'/', '&#039;', $output);
7199 }
7200
7201 return $output;
7202}
7203
7228function price2num($amount, $rounding = '', $option = 0)
7229{
7230 global $langs, $conf;
7231
7232 // Clean parameters
7233 if (is_null($amount)) {
7234 $amount = '';
7235 }
7236
7237 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7238 // Numbers must be '1234.56'
7239 // Decimal delimiter for PHP and database SQL requests must be '.'
7240 $dec = ',';
7241 $thousand = ' ';
7242 if (is_null($langs)) { // $langs is not defined, we use english values.
7243 $dec = '.';
7244 $thousand = ',';
7245 } else {
7246 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7247 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7248 }
7249 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7250 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7251 }
7252 }
7253 if ($thousand == 'None') {
7254 $thousand = '';
7255 } elseif ($thousand == 'Space') {
7256 $thousand = ' ';
7257 }
7258 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7259
7260 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7261 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7262 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7263 if (!is_numeric($amount)) {
7264 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7265 }
7266
7267 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
7268 $amount = str_replace($thousand, '', $amount);
7269 }
7270
7271 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7272 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7273 // So if number was already a good number, it is converted into local Dolibarr setup.
7274 if (is_numeric($amount)) {
7275 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7276 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7277 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7278 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7279 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7280 }
7281 //print "QQ".$amount."<br>\n";
7282
7283 // Now make replaceents (the main goal of function)
7284
7285 if ($thousand != ',' && $thousand != '.') {
7286 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7287
7288 // REGEX: Find the integral and decimal parts.
7289 //
7290 // We require that the decimal point only appears once in $amount.
7291 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7292 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7293 // It covers two cases:
7294 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7295 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7296 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7297 $matches = array();
7298 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7299 $intPart = $matches['int'];
7300 $decPart = $matches['dec'];
7301
7302 // Remove all commas and dots from intPart
7303 $intPart = str_replace(['.', ','], '', $intPart);
7304
7305 // Combine intPart and decPart with a dot
7306 $amount = $intPart . $dec . $decPart;
7307 }
7308 }
7309
7310 $amount = str_replace(' ', '', $amount); // To avoid spaces
7311 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7312 $amount = str_replace($dec, '.', $amount);
7313
7314 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7315 }
7316 //print ' XX'.$amount.' '.$rounding;
7317
7318 // Now, $amount is a real PHP float number. We make a rounding if required.
7319 if ($rounding) {
7320 $nbofdectoround = '';
7321 if ($rounding == 'MU') {
7322 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7323 } elseif ($rounding == 'MT') {
7324 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7325 } elseif ($rounding == 'MS') {
7326 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK') : 5;
7327 } elseif ($rounding == 'CU') {
7328 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7329 } elseif ($rounding == 'CT') {
7330 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7331 } elseif (is_numeric($rounding)) {
7332 $nbofdectoround = (int) $rounding;
7333 }
7334
7335 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7336 if (dol_strlen($nbofdectoround)) {
7337 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7338 } else {
7339 return 'ErrorBadParameterProvidedToFunction';
7340 }
7341 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7342
7343 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7344 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7345 if (is_numeric($amount)) {
7346 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7347 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7348 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7349 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7350 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7351 }
7352 //print "TT".$amount.'<br>';
7353
7354 // Always make replace because each math function (like round) replace
7355 // with local values and we want a number that has a SQL string format x.y
7356 if ($thousand != ',' && $thousand != '.') {
7357 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7358 }
7359
7360 $amount = str_replace(' ', '', $amount); // To avoid spaces
7361 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7362 $amount = str_replace($dec, '.', $amount);
7363
7364 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7365 }
7366
7367 return $amount;
7368}
7369
7382function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7383{
7384 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7385
7386 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7387 $dimension *= 1000000;
7388 $unit -= 6;
7389 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7390 $dimension *= 1000;
7391 $unit -= 3;
7392 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7393 $dimension /= 1000000;
7394 $unit += 6;
7395 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7396 $dimension /= 1000;
7397 $unit += 3;
7398 }
7399 // Special case when we want output unit into pound or ounce
7400 /* TODO
7401 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7402 {
7403 $dimension = // convert dimension from standard unit into ounce or pound
7404 $unit = $forceunitoutput;
7405 }
7406 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7407 {
7408 $dimension = // convert dimension from standard unit into ounce or pound
7409 $unit = $forceunitoutput;
7410 }*/
7411
7412 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7413 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7414 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
7415
7416 return $ret;
7417}
7418
7419
7432function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7433{
7434 global $db, $conf, $mysoc;
7435
7436 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7437 $thirdparty_seller = $mysoc;
7438 }
7439
7440 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);
7441
7442 $vatratecleaned = $vatrate;
7443 $reg = array();
7444 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7445 $vatratecleaned = trim($reg[1]);
7446 $vatratecode = $reg[2];
7447 }
7448
7449 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7450 {
7451 return 0;
7452 }*/
7453
7454 // Some test to guess with no need to make database access
7455 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7456 if ($local == 1) {
7457 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7458 return 0;
7459 }
7460 if ($thirdparty_seller->id == $mysoc->id) {
7461 if (!$thirdparty_buyer->localtax1_assuj) {
7462 return 0;
7463 }
7464 } else {
7465 if (!$thirdparty_seller->localtax1_assuj) {
7466 return 0;
7467 }
7468 }
7469 }
7470
7471 if ($local == 2) {
7472 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7473 if (!$mysoc->localtax2_assuj) {
7474 return 0; // If main vat is 0, IRPF may be different than 0.
7475 }
7476 if ($thirdparty_seller->id == $mysoc->id) {
7477 if (!$thirdparty_buyer->localtax2_assuj) {
7478 return 0;
7479 }
7480 } else {
7481 if (!$thirdparty_seller->localtax2_assuj) {
7482 return 0;
7483 }
7484 }
7485 }
7486 } else {
7487 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7488 return 0;
7489 }
7490 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7491 return 0;
7492 }
7493 }
7494
7495 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7496 if (in_array($mysoc->country_code, array('ES'))) {
7497 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7498 }
7499
7500 // Search local taxes
7501 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7502 if ($local == 1) {
7503 if ($thirdparty_seller != $mysoc) {
7504 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7505 return $thirdparty_seller->localtax1_value;
7506 }
7507 } else { // i am the seller
7508 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7509 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7510 }
7511 }
7512 }
7513 if ($local == 2) {
7514 if ($thirdparty_seller != $mysoc) {
7515 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7516 // TODO We should also return value defined on thirdparty only if defined
7517 return $thirdparty_seller->localtax2_value;
7518 }
7519 } else { // i am the seller
7520 if (in_array($mysoc->country_code, array('ES'))) {
7521 return $thirdparty_buyer->localtax2_value;
7522 } else {
7523 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7524 }
7525 }
7526 }
7527 }
7528
7529 // By default, search value of local tax on line of common tax
7530 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7531 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7532 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7533 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7534 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7535 if (!empty($vatratecode)) {
7536 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7537 } else {
7538 $sql .= " AND t.recuperableonly = '".$db->escape((string) $vatnpr)."'";
7539 }
7540
7541 $resql = $db->query($sql);
7542
7543 if ($resql) {
7544 $obj = $db->fetch_object($resql);
7545 if ($obj) {
7546 if ($local == 1) {
7547 return $obj->localtax1;
7548 } elseif ($local == 2) {
7549 return $obj->localtax2;
7550 }
7551 }
7552 }
7553
7554 return 0;
7555}
7556
7557
7566function isOnlyOneLocalTax($local)
7567{
7568 $tax = get_localtax_by_third($local);
7569
7570 $valors = explode(":", $tax);
7571
7572 if (count($valors) > 1) {
7573 return false;
7574 } else {
7575 return true;
7576 }
7577}
7578
7585function get_localtax_by_third($local)
7586{
7587 global $db, $mysoc;
7588
7589 $sql = " SELECT t.localtax".$local." as localtax";
7590 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7591 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7592 $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";
7593 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7594 $sql .= " AND t.localtax".$local."_type <> '0'";
7595 $sql .= " ORDER BY t.rowid DESC";
7596
7597 $resql = $db->query($sql);
7598 if ($resql) {
7599 $obj = $db->fetch_object($resql);
7600 if ($obj) {
7601 return $obj->localtax;
7602 } else {
7603 return '0';
7604 }
7605 }
7606
7607 return 'Error';
7608}
7609
7610
7622function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7623{
7624 global $db;
7625
7626 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7627
7628 // Search local taxes
7629 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7630 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7631 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7632 if ($firstparamisid) {
7633 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7634 } else {
7635 $vatratecleaned = $vatrate;
7636 $vatratecode = '';
7637 $reg = array();
7638 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7639 $vatratecleaned = $reg[1];
7640 $vatratecode = $reg[2];
7641 }
7642
7643 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7644 /*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 ??
7645 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7646 $sql .= " WHERE t.fk_pays = c.rowid";
7647 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7648 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7649 } else {
7650 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7651 }
7652 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7653 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7654 if ($vatratecode) {
7655 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7656 }
7657 }
7658
7659 $resql = $db->query($sql);
7660 if ($resql) {
7661 $obj = $db->fetch_object($resql);
7662 if ($obj) {
7663 return array(
7664 'rowid' => $obj->rowid,
7665 'code' => $obj->code,
7666 'rate' => $obj->rate,
7667 'localtax1' => $obj->localtax1,
7668 'localtax1_type' => $obj->localtax1_type,
7669 'localtax2' => $obj->localtax2,
7670 'localtax2_type' => $obj->localtax2_type,
7671 'npr' => $obj->npr,
7672 'accountancy_code_sell' => $obj->accountancy_code_sell,
7673 'accountancy_code_buy' => $obj->accountancy_code_buy
7674 );
7675 } else {
7676 return array();
7677 }
7678 } else {
7679 dol_print_error($db);
7680 }
7681
7682 return array();
7683}
7684
7701function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7702{
7703 global $db, $mysoc;
7704
7705 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7706
7707 // Search local taxes
7708 $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";
7709 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7710 if ($firstparamisid) {
7711 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7712 } else {
7713 $vatratecleaned = $vatrate;
7714 $vatratecode = '';
7715 $reg = array();
7716 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7717 $vatratecleaned = $reg[1];
7718 $vatratecode = $reg[2];
7719 }
7720
7721 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7722 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7723 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7724 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7725 } else {
7726 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7727 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7728 }
7729 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7730 if ($vatratecode) {
7731 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7732 }
7733 }
7734
7735 $resql = $db->query($sql);
7736 if ($resql) {
7737 $obj = $db->fetch_object($resql);
7738
7739 if ($obj) {
7740 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7741
7742 if ($local == 1) {
7743 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7744 } elseif ($local == 2) {
7745 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7746 } else {
7747 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);
7748 }
7749 }
7750 }
7751
7752 return array();
7753}
7754
7765function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7766{
7767 global $db, $mysoc;
7768
7769 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7770
7771 $ret = 0;
7772 $found = 0;
7773
7774 if ($idprod > 0) {
7775 // Load product
7776 $product = new Product($db);
7777 $product->fetch($idprod);
7778
7779 if (($mysoc->country_code == $thirdpartytouse->country_code)
7780 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7781 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7782 ) {
7783 // If country of thirdparty to consider is ours
7784 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7785 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
7786 if ($result > 0) {
7787 $ret = $product->vatrate_supplier;
7788 if ($product->default_vat_code_supplier) {
7789 $ret .= ' ('.$product->default_vat_code_supplier.')';
7790 }
7791 $found = 1;
7792 }
7793 }
7794 if (!$found) {
7795 $ret = $product->tva_tx; // Default sales vat of product
7796 if ($product->default_vat_code) {
7797 $ret .= ' ('.$product->default_vat_code.')';
7798 }
7799 $found = 1;
7800 }
7801 } else {
7802 // TODO Read default product vat according to product and an other countrycode.
7803 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7804 }
7805 }
7806
7807 if (!$found) {
7808 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7809 // 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).
7810 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7811 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7812 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7813 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7814 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7815 $sql .= $db->plimit(1);
7816
7817 $resql = $db->query($sql);
7818 if ($resql) {
7819 $obj = $db->fetch_object($resql);
7820 if ($obj) {
7821 $ret = $obj->vat_rate;
7822 if ($obj->default_vat_code) {
7823 $ret .= ' ('.$obj->default_vat_code.')';
7824 }
7825 }
7826 $db->free($resql);
7827 } else {
7828 dol_print_error($db);
7829 }
7830 } else {
7831 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7832 // '1.23'
7833 // or '1.23 (CODE)'
7834 $defaulttx = '';
7835 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7836 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7837 }
7838 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7839 $defaultcode = $reg[1];
7840 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7841 }*/
7842
7843 $ret = $defaulttx;
7844 }
7845 }
7846
7847 dol_syslog("get_product_vat_for_country: ret=".$ret);
7848 return $ret;
7849}
7850
7860function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7861{
7862 global $db, $mysoc;
7863
7864 if (!class_exists('Product')) {
7865 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7866 }
7867
7868 $ret = 0;
7869 $found = 0;
7870
7871 if ($idprod > 0) {
7872 // Load product
7873 $product = new Product($db);
7874 $result = $product->fetch($idprod);
7875
7876 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7877 /* Not defined yet, so we don't use this
7878 if ($local==1) $ret=$product->localtax1_tx;
7879 elseif ($local==2) $ret=$product->localtax2_tx;
7880 $found=1;
7881 */
7882 } else {
7883 // TODO Read default product vat according to product and another countrycode.
7884 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7885 }
7886 }
7887
7888 if (!$found) {
7889 // If vat of product for the country not found or not defined, we return higher vat of country.
7890 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7891 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7892 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7893 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7894 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7895 $sql .= $db->plimit(1);
7896
7897 $resql = $db->query($sql);
7898 if ($resql) {
7899 $obj = $db->fetch_object($resql);
7900 if ($obj) {
7901 if ($local == 1) {
7902 $ret = $obj->localtax1;
7903 } elseif ($local == 2) {
7904 $ret = $obj->localtax2;
7905 }
7906 }
7907 } else {
7908 dol_print_error($db);
7909 }
7910 }
7911
7912 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7913 return $ret;
7914}
7915
7933function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7934{
7935 global $mysoc, $db;
7936
7937 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7938
7939 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7940 $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;
7941
7942 if (empty($thirdparty_seller->country_code)) {
7943 $thirdparty_seller->country_code = $mysoc->country_code;
7944 }
7945 $seller_country_code = $thirdparty_seller->country_code;
7946 $seller_in_cee = isInEEC($thirdparty_seller);
7947
7948 if (empty($thirdparty_buyer->country_code)) {
7949 $thirdparty_buyer->country_code = $mysoc->country_code;
7950 }
7951 $buyer_country_code = $thirdparty_buyer->country_code;
7952 $buyer_in_cee = isInEEC($thirdparty_buyer);
7953
7954 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'));
7955
7956 // 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)
7957 // we use the buyer VAT.
7958 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7959 if ($seller_in_cee && $buyer_in_cee) {
7960 $isacompany = $thirdparty_buyer->isACompany();
7961 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7962 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7963 if (!isValidVATID($thirdparty_buyer)) {
7964 $isacompany = 0;
7965 }
7966 }
7967
7968 if (!$isacompany) {
7969 //print 'VATRULE 0';
7970 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7971 }
7972 }
7973 }
7974
7975 // If seller does not use VAT, default VAT is 0. End of rule.
7976 if (!$seller_use_vat) {
7977 //print 'VATRULE 1';
7978 return 0;
7979 }
7980
7981 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
7982 if (!empty($thirdparty_buyer->state_id)) {
7983 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
7984 $sql .= " FROM ".$db->prefix()."c_tva as t";
7985 $sql .= " INNER JOIN ".$db->prefix()."c_departements as d ON t.fk_department_buyer = d.rowid";
7986 $sql .= " WHERE d.rowid = ".((int) $thirdparty_buyer->state_id);
7987 $sql .= " AND t.active > 0";
7988 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7989 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7990
7991 $res = $db->query($sql);
7992 if ($res) {
7993 if ($db->num_rows($res)) {
7994 $obj = $db->fetch_object($res);
7995 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7996 }
7997 $db->free($res);
7998 }
7999 }
8000
8001 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
8002 if (($seller_country_code == $buyer_country_code)
8003 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
8004 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
8005 ) { // Warning ->country_code not always defined
8006 //print 'VATRULE 3';
8007 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8008
8009 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
8010 // Special case for india.
8011 //print 'VATRULE 3b';
8012 $reg = array();
8013 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
8014 // we must revert the C+S into I
8015 $tmpvat = str_replace("C+S", "I", $tmpvat);
8016 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8017 // we must revert the I into C+S
8018 $tmpvat = str_replace("I", "C+S", $tmpvat);
8019 }
8020 }
8021
8022 return $tmpvat;
8023 }
8024
8025 // 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.
8026 // 'VATRULE 4' - Not supported
8027
8028 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8029 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8030 if (($seller_in_cee && $buyer_in_cee)) {
8031 $isacompany = $thirdparty_buyer->isACompany();
8032 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
8033 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
8034 if (!isValidVATID($thirdparty_buyer)) {
8035 $isacompany = 0;
8036 }
8037 }
8038
8039 if (!$isacompany) {
8040 //print 'VATRULE 5';
8041 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8042 } else {
8043 //print 'VATRULE 6';
8044 return 0;
8045 }
8046 }
8047
8048 // 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
8049 // I don't see any use case that need this rule.
8050 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8051 $isacompany = $thirdparty_buyer->isACompany();
8052 if (!$isacompany) {
8053 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8054 //print 'VATRULE extra';
8055 }
8056 }
8057
8058 // Otherwise the VAT proposed by default=0. End of rule.
8059 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8060 //print 'VATRULE 7';
8061 return 0;
8062}
8063
8064
8075function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8076{
8077 global $db;
8078
8079 if ($idprodfournprice > 0) {
8080 if (!class_exists('ProductFournisseur')) {
8081 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8082 }
8083 $prodprice = new ProductFournisseur($db);
8084 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8085 return $prodprice->fourn_tva_npr;
8086 } elseif ($idprod > 0) {
8087 if (!class_exists('Product')) {
8088 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8089 }
8090 $prod = new Product($db);
8091 $prod->fetch($idprod);
8092 return $prod->tva_npr;
8093 }
8094
8095 return 0;
8096}
8097
8111function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8112{
8113 global $mysoc;
8114
8115 if (!is_object($thirdparty_seller)) {
8116 return -1;
8117 }
8118 if (!is_object($thirdparty_buyer)) {
8119 return -1;
8120 }
8121
8122 if (empty($thirdparty_seller->country_code)) {
8123 $thirdparty_seller->country_code = $mysoc->country_code;
8124 }
8125 $seller_country_code = $thirdparty_seller->country_code;
8126 //$seller_in_cee = isInEEC($thirdparty_seller);
8127
8128 if (empty($thirdparty_buyer->country_code)) {
8129 $thirdparty_buyer->country_code = $mysoc->country_code;
8130 }
8131 $buyer_country_code = $thirdparty_buyer->country_code;
8132 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8133
8134 if ($local == 1) { // Localtax 1
8135 if ($mysoc->country_code == 'ES') {
8136 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8137 return 0;
8138 }
8139 } else {
8140 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8141 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8142 return 0;
8143 }
8144 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8145 return 0;
8146 }
8147 }
8148 } elseif ($local == 2) { //I Localtax 2
8149 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8150 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8151 return 0;
8152 }
8153 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8154 return 0;
8155 }
8156 }
8157
8158 if ($seller_country_code == $buyer_country_code) {
8159 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8160 }
8161
8162 return 0;
8163}
8164
8173function yn($yesno, $format = 1, $color = 0)
8174{
8175 global $langs;
8176
8177 $result = 'unknown';
8178 $classname = '';
8179 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
8180 $result = $langs->trans('yes');
8181 if ($format == 1 || $format == 3) {
8182 $result = $langs->trans("Yes");
8183 }
8184 if ($format == 2) {
8185 $result = '<input type="checkbox" value="1" checked disabled>';
8186 }
8187 if ($format == 3) {
8188 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
8189 }
8190 if ($format == 4 || !is_numeric($format)) {
8191 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8192 }
8193
8194 $classname = 'ok';
8195 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
8196 $result = $langs->trans("no");
8197 if ($format == 1 || $format == 3) {
8198 $result = $langs->trans("No");
8199 }
8200 if ($format == 2) {
8201 $result = '<input type="checkbox" value="0" disabled>';
8202 }
8203 if ($format == 3) {
8204 $result = '<input type="checkbox" value="0" disabled> '.$result;
8205 }
8206 if ($format == 4 || !is_numeric($format)) {
8207 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8208 }
8209
8210 if ($color == 2) {
8211 $classname = 'ok';
8212 } else {
8213 $classname = 'error';
8214 }
8215 }
8216 if ($color) {
8217 return '<span class="'.$classname.'">'.$result.'</span>';
8218 }
8219 return $result;
8220}
8221
8240function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8241{
8242 if (empty($modulepart) && is_object($object)) {
8243 if (!empty($object->module)) {
8244 $modulepart = $object->module;
8245 } elseif (!empty($object->element)) {
8246 $modulepart = $object->element;
8247 }
8248 }
8249
8250 $path = '';
8251
8252 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8253 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8254 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8255 $arrayforoldpath['product'] = 2;
8256 }
8257
8258 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8259 $level = $arrayforoldpath[$modulepart];
8260 }
8261 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8262 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8263 if (empty($num) && is_object($object)) {
8264 $num = $object->id;
8265 }
8266 if (empty($alpha)) {
8267 $num = preg_replace('/([^0-9])/i', '', $num);
8268 } else {
8269 $num = preg_replace('/^.*\-/i', '', $num);
8270 }
8271 $num = substr("000".$num, -$level);
8272 if ($level == 1) {
8273 $path = substr($num, 0, 1);
8274 }
8275 if ($level == 2) {
8276 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
8277 }
8278 if ($level == 3) {
8279 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
8280 }
8281 } else {
8282 // We will enhance here a common way of forging path for document storage.
8283 // In a future, we may distribute directories on several levels depending on setup and object.
8284 // Here, $object->id, $object->ref and $modulepart are required.
8285 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
8286 // 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
8287 $path = dol_sanitizeFileName((string) $object->id);
8288 } else {
8289 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8290 }
8291 }
8292
8293 if (empty($withoutslash) && !empty($path)) {
8294 $path .= '/';
8295 }
8296
8297 return $path;
8298}
8299
8308function dol_mkdir($dir, $dataroot = '', $newmask = '')
8309{
8310 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
8311
8312 $dir = dol_sanitizePathName($dir, '_', 0);
8313
8314 $dir_osencoded = dol_osencode($dir);
8315 if (@is_dir($dir_osencoded)) {
8316 return 0;
8317 }
8318
8319 $nberr = 0;
8320 $nbcreated = 0;
8321
8322 $ccdir = '';
8323 if (!empty($dataroot)) {
8324 // Remove data root from loop
8325 $dir = str_replace($dataroot.'/', '', $dir);
8326 $ccdir = $dataroot.'/';
8327 }
8328
8329 $cdir = explode("/", $dir);
8330 $num = count($cdir);
8331 for ($i = 0; $i < $num; $i++) {
8332 if ($i > 0) {
8333 $ccdir .= '/'.$cdir[$i];
8334 } else {
8335 $ccdir .= $cdir[$i];
8336 }
8337 $regs = array();
8338 if (preg_match("/^.:$/", $ccdir, $regs)) {
8339 continue; // If the Windows path is incomplete, continue with next directory
8340 }
8341
8342 // Attention, is_dir() can fail event if the directory exists
8343 // (i.e. according the open_basedir configuration)
8344 if ($ccdir) {
8345 $ccdir_osencoded = dol_osencode($ccdir);
8346 if (!@is_dir($ccdir_osencoded)) {
8347 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8348
8349 umask(0);
8350 $dirmaskdec = octdec((string) $newmask);
8351 if (empty($newmask)) {
8352 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8353 }
8354 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8355 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8356 // If the is_dir has returned a false information, we arrive here
8357 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' (no permission to write into parent or directory already exists).", LOG_WARNING);
8358 $nberr++;
8359 } else {
8360 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
8361 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8362 $nbcreated++;
8363 }
8364 } else {
8365 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8366 }
8367 }
8368 }
8369 return ($nberr ? -$nberr : $nbcreated);
8370}
8371
8372
8380function dolChmod($filepath, $newmask = '')
8381{
8382 if (!empty($newmask)) {
8383 @chmod($filepath, octdec($newmask));
8384 } elseif (getDolGlobalString('MAIN_UMASK')) {
8385 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8386 }
8387}
8388
8389
8395function picto_required()
8396{
8397 return '<span class="fieldrequired">*</span>';
8398}
8399
8400
8417function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8418{
8419 if (is_null($stringtoclean)) {
8420 return '';
8421 }
8422
8423 if ($removelinefeed == 2) {
8424 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8425 }
8426 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8427
8428 // 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)
8429 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8430
8431 $temp = str_replace('< ', '__ltspace__', $temp);
8432 $temp = str_replace('<:', '__lttwopoints__', $temp);
8433
8434 if ($strip_tags) {
8435 $temp = strip_tags($temp);
8436 } else {
8437 // 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).
8438 $pattern = "/<[^<>]+>/";
8439 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8440 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8441 // pass 2 - $temp after pass 2: 0000-021
8442 $tempbis = $temp;
8443 do {
8444 $temp = $tempbis;
8445 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8446 $tempbis = preg_replace($pattern, '', $tempbis);
8447 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8448 } while ($tempbis != $temp);
8449
8450 $temp = $tempbis;
8451
8452 // 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).
8453 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8454 }
8455
8456 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8457
8458 // Remove also carriage returns
8459 if ($removelinefeed == 1) {
8460 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8461 }
8462
8463 // And double spaces
8464 if ($removedoublespaces) {
8465 while (strpos($temp, " ") !== false) {
8466 $temp = str_replace(" ", " ", $temp);
8467 }
8468 }
8469
8470 $temp = str_replace('__ltspace__', '< ', $temp);
8471 $temp = str_replace('__lttwopoints__', '<:', $temp);
8472
8473 return trim($temp);
8474}
8475
8495function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
8496{
8497 if (empty($allowed_tags)) {
8498 $allowed_tags = array(
8499 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8500 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8501 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8502 );
8503 }
8504 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8505 if ($allowiframe) {
8506 if (!in_array('iframe', $allowed_tags)) {
8507 $allowed_tags[] = "iframe";
8508 }
8509 }
8510 if ($allowlink) {
8511 if (!in_array('link', $allowed_tags)) {
8512 $allowed_tags[] = "link";
8513 }
8514 }
8515 if ($allowscript) {
8516 if (!in_array('script', $allowed_tags)) {
8517 $allowed_tags[] = "script";
8518 }
8519 }
8520 if ($allowstyle) {
8521 if (!in_array('style', $allowed_tags)) {
8522 $allowed_tags[] = "style";
8523 }
8524 }
8525
8526 $allowed_tags_string = implode("><", $allowed_tags);
8527 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8528
8529 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8530
8531 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8532
8533 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8534 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8535
8536 if ($allowphp) {
8537 $allowed_tags[] = "commentphp";
8538 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
8539 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
8540 }
8541
8542 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8543 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8544
8545 // Remove all HTML tags
8546 $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
8547
8548 if ($cleanalsosomestyles) { // Clean for remaining html tags
8549 $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
8550 }
8551 if ($removeclassattribute) { // Clean for remaining html tags
8552 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8553 }
8554
8555 // Remove 'javascript:' that we should not find into a text
8556 // 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)).
8557 if ($cleanalsojavascript) {
8558 $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);
8559 }
8560
8561 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8562
8563 if ($allowphp) {
8564 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
8565 }
8566
8567 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8568
8569
8570 return $temp;
8571}
8572
8573
8586function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8587{
8588 if (is_null($allowed_attributes)) {
8589 $allowed_attributes = array(
8590 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8591 // HTML5
8592 "header", "footer", "nav", "section", "menu", "menuitem"
8593 );
8594 }
8595 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8596 if (!in_array("content", $allowed_attributes)) {
8597 $allowed_attributes[] = "content";
8598 }
8599 if (!in_array("http-equiv", $allowed_attributes)) {
8600 $allowed_attributes[] = "http-equiv";
8601 }
8602
8603 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8604 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8605 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8606
8607 // Warning: loadHTML does not support HTML5 on old libxml versions.
8608 $dom = new DOMDocument('', 'UTF-8');
8609 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8610 $savwarning = error_reporting();
8611 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8612 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8613 error_reporting($savwarning);
8614
8615 if ($dom instanceof DOMDocument) {
8616 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8617 $el = $els->item($i);
8618 if (!$el instanceof DOMElement) {
8619 continue;
8620 }
8621 $attrs = $el->attributes;
8622 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8623 //var_dump($attrs->item($ii));
8624 if (!empty($attrs->item($ii)->name)) {
8625 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8626 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8627 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8628 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8629 // If attribute is 'style'
8630 $valuetoclean = $attrs->item($ii)->value;
8631
8632 if (isset($valuetoclean)) {
8633 do {
8634 $oldvaluetoclean = $valuetoclean;
8635 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8636 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8637 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8638 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8639 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8640 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8641 }
8642
8643 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8644 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8645 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8646 } while ($oldvaluetoclean != $valuetoclean);
8647 }
8648
8649 $attrs->item($ii)->value = $valuetoclean;
8650 }
8651 }
8652 }
8653 }
8654 }
8655
8656 $dom->encoding = 'UTF-8';
8657
8658 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8659 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8660
8661 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8662 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8663 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8664
8665 return trim($return);
8666 } else {
8667 return $stringtoclean;
8668 }
8669}
8670
8682function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8683{
8684 $temp = $stringtoclean;
8685 foreach ($disallowed_tags as $tagtoremove) {
8686 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8687 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8688 }
8689
8690 if ($cleanalsosomestyles) {
8691 $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
8692 }
8693
8694 return $temp;
8695}
8696
8697
8707function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8708{
8709 if ($nboflines == 1) {
8710 if (dol_textishtml($text)) {
8711 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8712 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8713 } else {
8714 if (isset($text)) {
8715 $firstline = preg_replace('/[\n\r].*/', '', $text);
8716 } else {
8717 $firstline = '';
8718 }
8719 }
8720 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8721 } else {
8722 $ishtml = 0;
8723 if (dol_textishtml($text)) {
8724 $text = preg_replace('/\n/', '', $text);
8725 $ishtml = 1;
8726 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8727 } else {
8728 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8729 }
8730
8731 $text = strtr($text, $repTable);
8732 if ($charset == 'UTF-8') {
8733 $pattern = '/(<br[^>]*>)/Uu';
8734 } else {
8735 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8736 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8737 }
8738 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8739
8740 $firstline = '';
8741 $i = 0;
8742 $countline = 0;
8743 $lastaddediscontent = 1;
8744 while ($countline < $nboflines && isset($a[$i])) {
8745 if (preg_match('/<br[^>]*>/', $a[$i])) {
8746 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8747 $firstline .= ($ishtml ? "<br>\n" : "\n");
8748 // Is it a br for a new line of after a printed line ?
8749 if (!$lastaddediscontent) {
8750 $countline++;
8751 }
8752 $lastaddediscontent = 0;
8753 }
8754 } else {
8755 $firstline .= $a[$i];
8756 $lastaddediscontent = 1;
8757 $countline++;
8758 }
8759 $i++;
8760 }
8761
8762 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8763 //unset($a);
8764 $ret = $firstline.($adddots ? '...' : '');
8765 //exit;
8766 return $ret;
8767 }
8768}
8769
8770
8782function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8783{
8784 if (is_null($stringtoencode)) {
8785 return '';
8786 }
8787
8788 if (!$nl2brmode) {
8789 return nl2br($stringtoencode, $forxml);
8790 } else {
8791 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8792 return $ret;
8793 }
8794}
8795
8805function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8806{
8807 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8808 // TODO using sandbox on inline html content is not possible yet with current browsers
8809 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8810 //$s .= $stringtoencode;
8811 //$s .= '</body></html></iframe>';
8812 return $stringtoencode;
8813 } else {
8814 $out = $stringtoencode;
8815
8816 // First clean HTML content
8817 do {
8818 $oldstringtoclean = $out;
8819
8820 $outishtml = 0;
8821 if (dol_textishtml($out)) {
8822 $outishtml = 1;
8823 }
8824
8825 // HTML sanitizer by DOMDocument
8826 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8827 try {
8828 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8829 if (LIBXML_VERSION < 20900) {
8830 // Avoid load of external entities (security problem).
8831 // Required only if LIBXML_VERSION < 20900
8832 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8833 libxml_disable_entity_loader(true);
8834 }
8835
8836 $dom = new DOMDocument();
8837 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
8838 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8839 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8840 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
8841 // I don't know what the xml encoding is the trick for
8842 if ($outishtml) {
8843 //$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>';
8844 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8845 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8846 } else {
8847 //$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>';
8848 $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>';
8849 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
8850 }
8851
8852 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8853
8854 $dom->encoding = 'UTF-8';
8855
8856 $out = trim($dom->saveHTML());
8857
8858 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8859 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8860 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8861 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8862 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8863 // $out = preg_replace('/<\/div>$/', '', $out);
8864 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8865
8866 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
8867 $out = str_replace('<br>', '', $out);
8868 }
8869 } catch (Exception $e) {
8870 // If error, invalid HTML string with no way to clean it
8871 //print $e->getMessage();
8872 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8873 }
8874 }
8875
8876 // HTML sanitizer by Tidy
8877 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8878 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
8879 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
8880 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8881 try {
8882 //var_dump($out);
8883
8884 // Try cleaning using tidy
8885 if (extension_loaded('tidy') && class_exists("tidy")) {
8886 //print "aaa".$out."\n";
8887
8888 // See options at https://tidy.sourceforge.net/docs/quickref.html
8889 $config = array(
8890 'clean' => false,
8891 // 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;
8892 'quote-marks' => false,
8893 'doctype' => 'strict',
8894 'show-body-only' => true,
8895 "indent-attributes" => false,
8896 "vertical-space" => false,
8897 //'ident' => false, // Not always supported
8898 "wrap" => 0,
8899 'preserve-entities' => true
8900 // HTML5 tags
8901 //'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',
8902 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8903 //'new-empty-tags' => 'command embed keygen source track wbr',
8904 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8905 );
8906
8907 // Tidy
8908 $tidy = new tidy();
8909 $out = $tidy->repairString($out, $config, 'utf8');
8910
8911 //print "xxx".$out;exit;
8912 }
8913
8914 //var_dump($out);
8915 } catch (Exception $e) {
8916 // If error, invalid HTML string with no way to clean it
8917 //print $e->getMessage();
8918 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8919 }
8920 }
8921
8922 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8923 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8924
8925 // Clean some html entities that are useless so text is cleaner
8926 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8927
8928 // Ckeditor uses the numeric entity for apostrophe, so we force it to
8929 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
8930 $out = preg_replace('/&#39;/i', '&apos;', $out);
8931
8932 // 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).
8933 // 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
8934 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8935 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
8936 $out = preg_replace_callback(
8937 '/&#(x?[0-9][0-9a-f]+;?)/i',
8942 static function ($m) {
8943 return realCharForNumericEntities($m);
8944 },
8945 $out
8946 );
8947 }
8948
8949 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8950 $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'.
8951
8952 // Keep only some html tags and remove also some 'javascript:' strings
8953 if ($check == 'restricthtmlallowlinkscript') {
8954 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
8955 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8956 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8957 } elseif ($check == 'restricthtmlallowiframe') {
8958 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
8959 } else {
8960 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8961 }
8962
8963 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8964 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8966 }
8967
8968 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
8969 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
8970 $out = preg_replace('/&apos;/i', "&#39;", $out);
8971
8972 // Now remove js
8973 // 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
8974 $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)>
8975 $out = preg_replace('/on(abort|after|animation|auxclick|before|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', '', $out);
8976 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8977 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8978 $out = preg_replace('/on(paste|pause|play|playing|progress|ratechange|reset|resize|scroll|search|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', '', $out);
8979 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8980 // More not into the previous list
8981 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8982 } while ($oldstringtoclean != $out);
8983
8984 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8985 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8986 // 'url(' to avoid inline style like background: url(http...
8987 // '<link' to avoid <link href="http...">
8988 $reg = array();
8989 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8990 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8991 $nblinks = count($reg[0]);
8992 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8993 $out = 'ErrorTooManyLinksIntoHTMLString';
8994 }
8995
8996 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8997 if ($nblinks > 0) {
8998 $out = 'ErrorHTMLLinksNotAllowed';
8999 }
9000 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
9001 $nblinks = 0;
9002 // Loop on each url in src= and url(
9003 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
9004
9005 $matches = array();
9006 if (preg_match_all($pattern, $out, $matches)) {
9007 // URLs are into $matches[1]
9008 $urls = $matches[1];
9009
9010 // Affiche les URLs
9011 foreach ($urls as $url) {
9012 $nblinks++;
9013 echo "Found url = ".$url . "\n";
9014 }
9015 if ($nblinks > 0) {
9016 $out = 'ErrorHTMLExternalLinksNotAllowed';
9017 }
9018 }
9019 }
9020
9021 return $out;
9022 }
9023}
9024
9045function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9046{
9047 if (is_null($stringtoencode)) {
9048 return '';
9049 }
9050
9051 $newstring = $stringtoencode;
9052 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9053 $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.
9054 if ($removelasteolbr) {
9055 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9056 }
9057 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9058 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9059 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9060 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9061 } else {
9062 if ($removelasteolbr) {
9063 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9064 }
9065 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9066 }
9067 // Other substitutions that htmlentities does not do
9068 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9069 return $newstring;
9070}
9071
9079function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9080{
9081 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9082 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9083 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
9084 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
9085 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9086 return $ret;
9087}
9088
9095function dol_htmlcleanlastbr($stringtodecode)
9096{
9097 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9098 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
9099 return $ret;
9100}
9101
9111function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9112{
9113 $newstring = $a;
9114 if ($keepsomeentities) {
9115 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9116 }
9117 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9118 if ($keepsomeentities) {
9119 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9120 }
9121 return $newstring;
9122}
9123
9135function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9136{
9137 return htmlentities($string, $flags, $encoding, $double_encode);
9138}
9139
9151function dol_string_is_good_iso($s, $clean = 0)
9152{
9153 $len = dol_strlen($s);
9154 $out = '';
9155 $ok = 1;
9156 for ($scursor = 0; $scursor < $len; $scursor++) {
9157 $ordchar = ord($s[$scursor]);
9158 //print $scursor.'-'.$ordchar.'<br>';
9159 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9160 $ok = 0;
9161 break;
9162 } elseif ($ordchar > 126 && $ordchar < 160) {
9163 $ok = 0;
9164 break;
9165 } elseif ($clean) {
9166 $out .= $s[$scursor];
9167 }
9168 }
9169 if ($clean) {
9170 return $out;
9171 }
9172 return $ok;
9173}
9174
9183function dol_nboflines($s, $maxchar = 0)
9184{
9185 if ($s == '') {
9186 return 0;
9187 }
9188 $arraystring = explode("\n", $s);
9189 $nb = count($arraystring);
9190
9191 return $nb;
9192}
9193
9194
9204function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9205{
9206 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9207 if (dol_textishtml($text)) {
9208 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9209 }
9210
9211 $text = strtr($text, $repTable);
9212 if ($charset == 'UTF-8') {
9213 $pattern = '/(<br[^>]*>)/Uu';
9214 } else {
9215 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9216 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9217 }
9218 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9219
9220 $nblines = (int) floor((count($a) + 1) / 2);
9221 // count possible auto line breaks
9222 if ($maxlinesize) {
9223 foreach ($a as $line) {
9224 if (dol_strlen($line) > $maxlinesize) {
9225 //$line_dec = html_entity_decode(strip_tags($line));
9226 $line_dec = html_entity_decode($line);
9227 if (dol_strlen($line_dec) > $maxlinesize) {
9228 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9229 $nblines += substr_count($line_dec, '\n');
9230 }
9231 }
9232 }
9233 }
9234
9235 unset($a);
9236 return $nblines;
9237}
9238
9247function dol_textishtml($msg, $option = 0)
9248{
9249 if (is_null($msg)) {
9250 return false;
9251 }
9252
9253 if ($option == 1) {
9254 if (preg_match('/<(html|link|script)/i', $msg)) {
9255 return true;
9256 } elseif (preg_match('/<body/i', $msg)) {
9257 return true;
9258 } elseif (preg_match('/<\/textarea/i', $msg)) {
9259 return true;
9260 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9261 return true;
9262 } elseif (preg_match('/<br/i', $msg)) {
9263 return true;
9264 }
9265 return false;
9266 } else {
9267 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9268 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9269 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9270 return true;
9271 } elseif (preg_match('/<\/textarea/i', $msg)) {
9272 return true;
9273 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9274 return true;
9275 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9276 return true;
9277 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9278 return true;
9279 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9280 return true;
9281 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9282 return true; // must accept <img src="http://example.com/aaa.png" />
9283 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9284 return true; // must accept <a href="http://example.com/aaa.png" />
9285 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9286 return true;
9287 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9288 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9289 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9290 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9291 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9292 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
9293 return true; // Html entities numbers in hexa
9294 }
9295
9296 return false;
9297 }
9298}
9299
9314function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
9315{
9316 if (!empty($invert)) {
9317 $tmp = $text1;
9318 $text1 = $text2;
9319 $text2 = $tmp;
9320 }
9321
9322 $ret = '';
9323 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
9324 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
9325 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
9326 return $ret;
9327}
9328
9329
9330
9344function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
9345{
9346 global $db, $conf, $mysoc, $user, $extrafields;
9347
9348 $substitutionarray = array();
9349
9350 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
9351 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
9352 // this will include signature content first and then replace var found into content of signature
9353 //var_dump($onlykey);
9354 $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()
9355 $usersignature = $user->signature;
9356 $substitutionarray = array_merge($substitutionarray, array(
9357 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
9358 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
9359 ));
9360
9361 if (is_object($user) && ($user instanceof User)) {
9362 $substitutionarray = array_merge($substitutionarray, array(
9363 '__USER_ID__' => (string) $user->id,
9364 '__USER_LOGIN__' => (string) $user->login,
9365 '__USER_EMAIL__' => (string) $user->email,
9366 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
9367 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
9368 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
9369 '__USER_FAX__' => (string) $user->office_fax,
9370 '__USER_LASTNAME__' => (string) $user->lastname,
9371 '__USER_FIRSTNAME__' => (string) $user->firstname,
9372 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
9373 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
9374 '__USER_JOB__' => (string) $user->job,
9375 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
9376 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
9377 ));
9378 }
9379 }
9380 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
9381 $substitutionarray = array_merge($substitutionarray, array(
9382 '__MYCOMPANY_NAME__' => $mysoc->name,
9383 '__MYCOMPANY_EMAIL__' => $mysoc->email,
9384 '__MYCOMPANY_URL__' => $mysoc->url,
9385 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
9386 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
9387 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
9388 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
9389 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
9390 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
9391 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
9392 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
9393 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
9394 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
9395 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
9396 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
9397 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
9398 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
9399 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
9400 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
9401 '__MYCOMPANY_ZIP__' => $mysoc->zip,
9402 '__MYCOMPANY_TOWN__' => $mysoc->town,
9403 '__MYCOMPANY_STATE__' => $mysoc->state,
9404 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
9405 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
9406 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
9407 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
9408 ));
9409 }
9410
9411 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
9412 if ($onlykey) {
9413 $substitutionarray['__ID__'] = '__ID__';
9414 $substitutionarray['__REF__'] = '__REF__';
9415 $substitutionarray['__NEWREF__'] = '__NEWREF__';
9416 $substitutionarray['__LABEL__'] = '__LABEL__';
9417 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
9418 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
9419 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
9420 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
9421 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
9422
9423 if (isModEnabled("societe")) { // Most objects are concerned
9424 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
9425 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
9426 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
9427 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
9428 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
9429 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
9430 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
9431 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
9432 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
9433 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
9434 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
9435 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
9436 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
9437 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
9438 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
9439 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
9440 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
9441 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
9442 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
9443 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
9444 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
9445 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
9446 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
9447 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
9448 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
9449 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
9450 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
9451 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
9452 }
9453 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9454 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9455 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9456 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9457 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9458 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9459 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9460 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9461 }
9462 // add substitution variables for ticket
9463 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9464 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9465 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9466 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9467 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9468 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9469 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9470 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9471 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9472 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9473 }
9474
9475 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9476 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9477 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9478 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9479 }
9480 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9481 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9482 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9483 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9484 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9485 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9486 }
9487 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9488 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9489 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9490 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9491 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9492 }
9493 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9494 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9495 }
9496 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9497 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9498 }
9499 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9500 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9501 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9502 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9503 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9504 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9505 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9506
9507 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9508 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9509 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9510 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9511 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9512
9513 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9514 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9515 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9516 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9517 }
9518 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9519 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9520 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9521 }
9522 } else {
9523 '@phan-var-force Adherent|Delivery $object';
9525 $substitutionarray['__ID__'] = $object->id;
9526 $substitutionarray['__REF__'] = $object->ref;
9527 $substitutionarray['__NEWREF__'] = $object->newref;
9528 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9529 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9530 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9531 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9532 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9533
9534 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9535 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9536 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9537
9538 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
9539 $date_delivery = null;
9540 if (property_exists($object, 'date_delivery')) {
9541 $date_delivery = $object->date_delivery;
9542 } elseif (property_exists($object, 'delivery_date')) {
9543 $date_delivery = $object->delivery_date;
9544 }
9545 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9546 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
9547 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
9548 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
9549 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
9550 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
9551 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
9552 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
9553 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
9554
9555 // For backward compatibility (deprecated)
9556 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9557 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9558
9559 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9560 $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 : '')) : '');
9561 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9562
9563 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9564 '@phan-var-force Adherent $object';
9566 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9567
9568 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9569 if (method_exists($object, 'getCivilityLabel')) {
9570 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9571 }
9572 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9573 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9574 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9575 if (method_exists($object, 'getFullName')) {
9576 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9577 }
9578 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9579 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9580 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9581 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9582 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9583 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9584 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9585 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9586 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9587 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9588 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9589 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9590 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9591 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9592 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9593 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9594
9595 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9596 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9597 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9598 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9599 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9600 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9601 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9602 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9603 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9604 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9605 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9606 }
9607
9608 if (is_object($object) && $object->element == 'societe') {
9610 '@phan-var-force Societe $object';
9611 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
9612 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
9613 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
9614 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
9615 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
9616 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
9617 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
9618 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
9619 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
9620 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
9621 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
9622 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
9623 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
9624 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
9625 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
9626 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ? $object->country_id : '');
9627 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
9628 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
9629 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
9630 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
9631 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
9632 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
9633 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
9634 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
9635 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
9636 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
9637 } elseif (is_object($object) && is_object($object->thirdparty)) {
9638 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
9639 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
9640 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
9641 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
9642 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
9643 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
9644 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
9645 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
9646 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
9647 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
9648 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
9649 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
9650 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
9651 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ? $object->thirdparty->country_id : '');
9652 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
9653 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
9654 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
9655 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
9656 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
9657 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
9658 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
9659 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
9660 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
9661 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
9662 }
9663
9664 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9665 '@phan-var-force RecruitmentCandidature $object';
9667 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9668 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9669 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9670 }
9671 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9672 '@phan-var-force ConferenceOrBoothAttendee $object';
9674 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9675 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9676 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9677 }
9678
9679 if (is_object($object) && $object->element == 'project') {
9680 '@phan-var-force Project $object';
9682 $substitutionarray['__PROJECT_ID__'] = $object->id;
9683 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9684 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9685 } elseif (is_object($object)) {
9686 $project = null;
9687 if (!empty($object->project)) {
9688 $project = $object->project;
9689 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9690 $project = $object->projet;
9691 }
9692 if (!is_null($project) && is_object($project)) {
9693 $substitutionarray['__PROJECT_ID__'] = $project->id;
9694 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9695 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9696 } else {
9697 // can substitute variables for project : uses lazy load in "make_substitutions" method
9698 $project_id = 0;
9699 if (!empty($object->fk_project) && $object->fk_project > 0) {
9700 $project_id = $object->fk_project;
9701 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9702 $project_id = $object->fk_project;
9703 }
9704 if ($project_id > 0) {
9705 // path:class:method:id
9706 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9707 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9708 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9709 }
9710 }
9711 }
9712
9713 if (is_object($object) && $object->element == 'facture') {
9714 '@phan-var-force Facture $object';
9716 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9717 }
9718 if (is_object($object) && $object->element == 'shipping') {
9719 '@phan-var-force Expedition $object';
9721 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9722 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9723 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9724 }
9725 if (is_object($object) && $object->element == 'reception') {
9726 '@phan-var-force Reception $object';
9728 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9729 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9730 }
9731
9732 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9733 '@phan-var-force Contrat $object';
9735 $dateplannedstart = '';
9736 $datenextexpiration = '';
9737 foreach ($object->lines as $line) {
9738 if ($line->date_start > $dateplannedstart) {
9739 $dateplannedstart = $line->date_start;
9740 }
9741 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9742 $datenextexpiration = $line->date_end;
9743 }
9744 }
9745 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9746 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9747 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9748
9749 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9750 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9751 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9752 }
9753 // add substitution variables for ticket
9754 if (is_object($object) && $object->element == 'ticket') {
9755 '@phan-var-force Ticket $object';
9757 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9758 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9759 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9760 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9761 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9762 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9763 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9764 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9765 $userstat = new User($db);
9766 if ($object->fk_user_assign > 0) {
9767 $userstat->fetch($object->fk_user_assign);
9768 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9769 }
9770
9771 if ($object->fk_user_create > 0) {
9772 $userstat->fetch($object->fk_user_create);
9773 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9774 }
9775 }
9776
9777 // Create dynamic tags for __EXTRAFIELD_FIELD__
9778 if ($object->table_element && $object->id > 0) {
9779 if (!is_object($extrafields)) {
9780 $extrafields = new ExtraFields($db);
9781 }
9782 $extrafields->fetch_name_optionals_label($object->table_element, true);
9783
9784 if ($object->fetch_optionals() > 0) {
9785 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9786 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9787 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9788 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9789 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9790 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9791 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9792 $datetime = $object->array_options['options_'.$key];
9793 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9794 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9795 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9796 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9797 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9798 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9799 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9800 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9801 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9802 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9803 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9804 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9805 }
9806 }
9807 }
9808 }
9809 }
9810
9811 // Complete substitution array with the url to make online payment
9812 if (empty($substitutionarray['__REF__'])) {
9813 $paymenturl = '';
9814 } else {
9815 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9816 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9817 $outputlangs->loadLangs(array('paypal', 'other'));
9818
9819 $amounttouse = 0;
9820 $typeforonlinepayment = 'free';
9821 if (is_object($object) && $object->element == 'commande') {
9822 $typeforonlinepayment = 'order';
9823 }
9824 if (is_object($object) && $object->element == 'facture') {
9825 $typeforonlinepayment = 'invoice';
9826 }
9827 if (is_object($object) && $object->element == 'member') {
9828 $typeforonlinepayment = 'member';
9829 if (!empty($object->last_subscription_amount)) {
9830 $amounttouse = $object->last_subscription_amount;
9831 }
9832 }
9833 if (is_object($object) && $object->element == 'contrat') {
9834 $typeforonlinepayment = 'contract';
9835 }
9836 if (is_object($object) && $object->element == 'fichinter') {
9837 $typeforonlinepayment = 'ficheinter';
9838 }
9839
9840 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9841 $paymenturl = $url;
9842 }
9843
9844 if ($object->id > 0) {
9845 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9846 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9847
9848 // Show structured communication
9849 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9850 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9851 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9852 }
9853
9854 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9855 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9856 } else {
9857 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9858 }
9859 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9860 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9861 } else {
9862 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9863 }
9864 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9865 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9866 } else {
9867 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9868 }
9869 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9870 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9871 } else {
9872 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9873 }
9874 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9875 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9876 } else {
9877 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9878 }
9879 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9880 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9881 } else {
9882 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9883 }
9884
9885 if (is_object($object) && $object->element == 'propal') {
9886 '@phan-var-force Propal $object';
9888 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9889 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9890 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9891 }
9892 if (is_object($object) && $object->element == 'commande') {
9893 '@phan-var-force Commande $object';
9895 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9896 }
9897 if (is_object($object) && $object->element == 'facture') {
9898 '@phan-var-force Facture $object';
9900 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9901 }
9902 if (is_object($object) && $object->element == 'contrat') {
9903 '@phan-var-force Contrat $object';
9905 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9906 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9907 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9908 }
9909 if (is_object($object) && $object->element == 'fichinter') {
9910 '@phan-var-force Fichinter $object';
9912 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9913 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9914 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9915 }
9916 if (is_object($object) && $object->element == 'supplier_proposal') {
9917 '@phan-var-force SupplierProposal $object';
9919 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9920 }
9921 if (is_object($object) && $object->element == 'invoice_supplier') {
9922 '@phan-var-force FactureFournisseur $object';
9924 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9925 }
9926 if (is_object($object) && $object->element == 'shipping') {
9927 '@phan-var-force Expedition $object';
9929 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9930 }
9931 }
9932
9933 if (is_object($object) && $object->element == 'action') {
9934 '@phan-var-force ActionComm $object';
9936 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9937 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9938 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9939 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9940 }
9941 }
9942 }
9943 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9944 '@phan-var-force Facture|FactureRec $object';
9946 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9947
9948 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9949 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9950 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9951 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9952
9953 $already_payed_all = 0;
9954 if (is_object($object) && ($object instanceof Facture)) {
9955 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9956 }
9957
9958 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9959 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9960 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9961
9962 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9963 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9964 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9965
9966 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9967
9968 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9969 $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)) : '';
9970 $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)) : '';
9971
9972 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9973 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9974 }
9975 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9976 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9977 }
9978
9979 // Amount keys formatted in a currency
9980 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9981 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9982 $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) : '';
9983 $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)) : '';
9984 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9985 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9986 }
9987 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9988 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9989 }
9990 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9991 if ($onlykey != 2) {
9992 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9993 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9994 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9995 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9996 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9997 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9998 }
9999 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
10000 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
10001 }
10002 }
10003
10004 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
10005 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
10006 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
10007 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
10008 // TODO Add other keys for foreign multicurrency
10009
10010 // For backward compatibility
10011 if ($onlykey != 2) {
10012 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
10013 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
10014 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10015 }
10016 }
10017
10018
10019 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10020 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
10021
10022 $now = dol_now();
10023
10024 $tmp = dol_getdate($now, true);
10025 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10026 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10027 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10028 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10029
10030 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
10031
10032 $substitutionarray = array_merge($substitutionarray, array(
10033 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10034 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10035 '__DAY__' => (string) $tmp['mday'],
10036 '__DAY_TEXT__' => $daytext, // Monday
10037 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10038 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10039 '__MONTH__' => (string) $tmp['mon'],
10040 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
10041 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
10042 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
10043 '__YEAR__' => (string) $tmp['year'],
10044 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10045 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10046 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10047 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10048 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp3['month'])),
10049 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp3['month'])),
10050 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp3['month'])),
10051 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10052 '__NEXT_DAY__' => (string) $tmp4['day'],
10053 '__NEXT_MONTH__' => (string) $tmp5['month'],
10054 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
10055 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
10056 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
10057 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10058 ));
10059 }
10060
10061 if (isModEnabled('multicompany')) {
10062 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10063 }
10064 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10065 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10066 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10067 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
10068 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10069 }
10070
10071 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10072
10073 return $substitutionarray;
10074}
10075
10092function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10093{
10094 global $conf, $db, $langs;
10095
10096 if (!is_array($substitutionarray)) {
10097 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10098 }
10099
10100 if (empty($outputlangs)) {
10101 $outputlangs = $langs;
10102 }
10103
10104 // Is initial text HTML or simple text ?
10105 $msgishtml = 0;
10106 if (dol_textishtml($text, 1)) {
10107 $msgishtml = 1;
10108 }
10109
10110 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10111 if (is_object($outputlangs)) {
10112 $reg = array();
10113 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10114 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10115 $tmp = explode('|', $reg[1]);
10116 if (!empty($tmp[1])) {
10117 $outputlangs->load($tmp[1]);
10118 }
10119
10120 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10121
10122 if (empty($converttextinhtmlifnecessary)) {
10123 // convert $newval into HTML is necessary
10124 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10125 } else {
10126 if (! $msgishtml) {
10127 $valueishtml = dol_textishtml($value, 1);
10128 //var_dump("valueishtml=".$valueishtml);
10129
10130 if ($valueishtml) {
10131 $text = dol_htmlentitiesbr($text);
10132 $msgishtml = 1;
10133 }
10134 } else {
10135 $value = dol_nl2br((string) $value);
10136 }
10137
10138 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
10139 }
10140 }
10141 }
10142
10143 // Make substitution for constant keys.
10144 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10145 $reg = array();
10146 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10147 $keyfound = $reg[1];
10148 if (isASecretKey($keyfound)) {
10149 $value = '*****forbidden*****';
10150 } else {
10151 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
10152 }
10153
10154 if (empty($converttextinhtmlifnecessary)) {
10155 // convert $newval into HTML is necessary
10156 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10157 } else {
10158 if (! $msgishtml) {
10159 $valueishtml = dol_textishtml($value, 1);
10160
10161 if ($valueishtml) {
10162 $text = dol_htmlentitiesbr($text);
10163 $msgishtml = 1;
10164 }
10165 } else {
10166 $value = dol_nl2br((string) $value);
10167 }
10168
10169 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
10170 }
10171 }
10172
10173 // Make substitution for array $substitutionarray
10174 foreach ($substitutionarray as $key => $value) {
10175 if (!isset($value)) {
10176 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10177 }
10178
10179 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
10180 $value = ''; // Protection
10181 }
10182
10183 if (empty($converttextinhtmlifnecessary)) {
10184 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10185 } else {
10186 if (! $msgishtml) {
10187 $valueishtml = dol_textishtml($value, 1);
10188
10189 if ($valueishtml) {
10190 $text = dol_htmlentitiesbr($text);
10191 $msgishtml = 1;
10192 }
10193 } else {
10194 $value = dol_nl2br((string) $value);
10195 }
10196 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10197 }
10198 }
10199
10200 // TODO Implement the lazyload substitution
10201 /*
10202 add a loop to scan $substitutionarray:
10203 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.
10204 If no, we don't need to make replacement, so we do nothing.
10205 If yes, we can make the substitution:
10206
10207 include_once $path;
10208 $tmpobj = new $class($db);
10209 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10210 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10211 */
10212 $memory_object_list = array();
10213 foreach ($substitutionarray as $key => $value) {
10214 $lazy_load_arr = array();
10215 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10216 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10217 $key_to_substitute = $lazy_load_arr[1];
10218 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10219 $param_arr = explode(':', (string) $value);
10220 // path:class:method:id
10221 if (count($param_arr) == 4) {
10222 $path = $param_arr[0];
10223 $class = $param_arr[1];
10224 $method = $param_arr[2];
10225 $id = (int) $param_arr[3];
10226
10227 // load class file and init object list in memory
10228 if (!isset($memory_object_list[$class])) {
10229 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10230 require_once DOL_DOCUMENT_ROOT . $path;
10231 if (class_exists($class)) {
10232 $memory_object_list[$class] = array(
10233 'list' => array(),
10234 );
10235 }
10236 }
10237 }
10238
10239 // fetch object and set substitution
10240 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
10241 if (method_exists($class, $method)) {
10242 if (!isset($memory_object_list[$class]['list'][$id])) {
10243 $tmpobj = new $class($db);
10244 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10245 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
10246 $memory_object_list[$class]['list'][$id] = $tmpobj;
10247 } else {
10248 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10249 $tmpobj = $memory_object_list[$class]['list'][$id];
10250 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10251 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
10252 }
10253
10254 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
10255 }
10256 }
10257 }
10258 }
10259 }
10260 }
10261 }
10262
10263 return $text;
10264}
10265
10278function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
10279{
10280 global $conf, $user;
10281
10282 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
10283
10284 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
10285
10286 // Check if there is external substitution to do, requested by plugins
10287 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
10288
10289 foreach ($dirsubstitutions as $reldir) {
10290 $dir = dol_buildpath($reldir, 0);
10291
10292 // Check if directory exists
10293 if (!dol_is_dir($dir)) {
10294 continue;
10295 }
10296
10297 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
10298 foreach ($substitfiles as $substitfile) {
10299 $reg = array();
10300 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
10301 $module = $reg[1];
10302
10303 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
10304 // Include the user's functions file
10305 require_once $dir.$substitfile['name'];
10306 // Call the user's function, and only if it is defined
10307 $function_name = $module."_".$callfunc;
10308 if (function_exists($function_name)) {
10309 $function_name($substitutionarray, $outputlangs, $object, $parameters);
10310 }
10311 }
10312 }
10313 }
10314 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
10315 // to list all tags in odt template
10316 $tags = '';
10317 foreach ($substitutionarray as $key => $value) {
10318 $tags .= '{'.$key.'} => '.$value."\n";
10319 }
10320 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
10321 }
10322}
10323
10333function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
10334{
10335 print get_date_range($date_start, $date_end, $format, $outputlangs);
10336}
10337
10348function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
10349{
10350 global $langs;
10351
10352 $out = '';
10353
10354 if (!is_object($outputlangs)) {
10355 $outputlangs = $langs;
10356 }
10357
10358 if ($date_start && $date_end) {
10359 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10360 }
10361 if ($date_start && !$date_end) {
10362 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10363 }
10364 if (!$date_start && $date_end) {
10365 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10366 }
10367
10368 return $out;
10369}
10370
10379function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
10380{
10381 global $conf;
10382
10383 $ret = '';
10384 // If order not defined, we use the setup
10385 if ($nameorder < 0) {
10386 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
10387 }
10388 if ($nameorder == 1) {
10389 $ret .= $firstname;
10390 if ($firstname && $lastname) {
10391 $ret .= ' ';
10392 }
10393 $ret .= $lastname;
10394 } elseif ($nameorder == 2 || $nameorder == 3) {
10395 $ret .= $firstname;
10396 if (empty($ret) && $nameorder == 3) {
10397 $ret .= $lastname;
10398 }
10399 } else { // 0, 4 or 5
10400 $ret .= $lastname;
10401 if (empty($ret) && $nameorder == 5) {
10402 $ret .= $firstname;
10403 }
10404 if ($nameorder == 0) {
10405 if ($firstname && $lastname) {
10406 $ret .= ' ';
10407 }
10408 $ret .= $firstname;
10409 }
10410 }
10411 return $ret;
10412}
10413
10414
10427function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
10428{
10429 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
10430 if (!is_array($mesgs)) {
10431 $mesgs = trim((string) $mesgs);
10432 // If mesgs is a not an empty string
10433 if ($mesgs) {
10434 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
10435 return;
10436 }
10437 if ($attop) {
10438 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10439 } else {
10440 $_SESSION['dol_events'][$style][] = $mesgs;
10441 }
10442 }
10443 } else {
10444 // If mesgs is an array
10445 foreach ($mesgs as $mesg) {
10446 $mesg = trim((string) $mesg);
10447 if ($mesg) {
10448 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
10449 return;
10450 }
10451 if ($attop) {
10452 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10453 } else {
10454 $_SESSION['dol_events'][$style][] = $mesg;
10455 }
10456 }
10457 }
10458 }
10459}
10460
10474function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
10475{
10476 if (empty($mesg) && empty($mesgs)) {
10477 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
10478 } else {
10479 if ($messagekey) {
10480 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
10481 // TODO
10482 $mesg .= '';
10483 }
10484 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
10485 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
10486 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
10487 }
10488 if (empty($mesgs)) {
10489 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
10490 } else {
10491 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10492 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10493 }
10494 setEventMessage($mesgs, $style, $noduplicate, $attop);
10495 }
10496 }
10497 }
10498}
10499
10509function dol_htmloutput_events($disabledoutputofmessages = 0)
10510{
10511 // Show mesgs
10512 if (isset($_SESSION['dol_events']['mesgs'])) {
10513 if (empty($disabledoutputofmessages)) {
10514 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10515 }
10516 unset($_SESSION['dol_events']['mesgs']);
10517 }
10518 // Show errors
10519 if (isset($_SESSION['dol_events']['errors'])) {
10520 if (empty($disabledoutputofmessages)) {
10521 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10522 }
10523 unset($_SESSION['dol_events']['errors']);
10524 }
10525
10526 // Show warnings
10527 if (isset($_SESSION['dol_events']['warnings'])) {
10528 if (empty($disabledoutputofmessages)) {
10529 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10530 }
10531 unset($_SESSION['dol_events']['warnings']);
10532 }
10533}
10534
10549function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10550{
10551 global $conf, $langs;
10552
10553 $ret = 0;
10554 $return = '';
10555 $out = '';
10556 $divstart = $divend = '';
10557
10558 // If inline message with no format, we add it.
10559 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || defined('DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10560 $divstart = '<div class="'.$style.' clearboth">';
10561 $divend = '</div>';
10562 }
10563
10564 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10565 $langs->load("errors");
10566 $out .= $divstart;
10567 if (is_array($mesgarray) && count($mesgarray)) {
10568 foreach ($mesgarray as $message) {
10569 $ret++;
10570 $out .= $langs->trans($message);
10571 if ($ret < count($mesgarray)) {
10572 $out .= "<br>\n";
10573 }
10574 }
10575 }
10576 if ($mesgstring) {
10577 $ret++;
10578 $out .= $langs->trans($mesgstring);
10579 }
10580 $out .= $divend;
10581 }
10582
10583 if ($out) {
10584 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10585 $return = '<script nonce="'.getNonce().'">
10586 $(document).ready(function() {
10587 /* jnotify(message, preset of message type, keepmessage) */
10588 $.jnotify("'.dol_escape_js($out).'", "'.($style == "ok" ? 3000 : $style).'", '.($style == "ok" ? "false" : "true").',{ remove: function (){} } );
10589 });
10590 </script>';
10591 } else {
10592 $return = $out;
10593 }
10594 }
10595
10596 return $return;
10597}
10598
10610function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10611{
10612 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10613}
10614
10628function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10629{
10630 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10631 return;
10632 }
10633
10634 $iserror = 0;
10635 $iswarning = 0;
10636 if (is_array($mesgarray)) {
10637 foreach ($mesgarray as $val) {
10638 if ($val && preg_match('/class="error"/i', $val)) {
10639 $iserror++;
10640 break;
10641 }
10642 if ($val && preg_match('/class="warning"/i', $val)) {
10643 $iswarning++;
10644 break;
10645 }
10646 }
10647 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10648 $iserror++;
10649 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10650 $iswarning++;
10651 }
10652 if ($style == 'error') {
10653 $iserror++;
10654 }
10655 if ($style == 'warning') {
10656 $iswarning++;
10657 }
10658
10659 if ($iserror || $iswarning) {
10660 // Remove div from texts
10661 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10662 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10663 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10664 // Remove div from texts array
10665 if (is_array($mesgarray)) {
10666 $newmesgarray = array();
10667 foreach ($mesgarray as $val) {
10668 if (is_string($val)) {
10669 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10670 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10671 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10672 $newmesgarray[] = $tmpmesgstring;
10673 } else {
10674 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10675 }
10676 }
10677 $mesgarray = $newmesgarray;
10678 }
10679 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10680 } else {
10681 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10682 }
10683}
10684
10696function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10697{
10698 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10699}
10700
10721function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10722{
10723 // Clean parameters
10724 $order = strtolower($order);
10725
10726 if (is_array($array)) {
10727 $sizearray = count($array);
10728 if ($sizearray > 0) {
10729 $temp = array();
10730 foreach (array_keys($array) as $key) {
10731 if (is_object($array[$key])) {
10732 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10733 } else {
10734 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10735 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10736 }
10737 if ($natsort == -1) {
10738 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10739 }
10740 }
10741
10742 if (empty($natsort) || $natsort == -1) {
10743 if ($order == 'asc') {
10744 asort($temp);
10745 } else {
10746 arsort($temp);
10747 }
10748 } else {
10749 if ($case_sensitive) {
10750 natsort($temp);
10751 } else {
10752 natcasesort($temp); // natecasesort is not sensible to case
10753 }
10754 if ($order != 'asc') {
10755 $temp = array_reverse($temp, true);
10756 }
10757 }
10758
10759 $sorted = array();
10760
10761 foreach (array_keys($temp) as $key) {
10762 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10763 }
10764
10765 return $sorted;
10766 }
10767 }
10768 return $array;
10769}
10770
10771
10779function utf8_check($str)
10780{
10781 $str = (string) $str; // Sometimes string is an int.
10782
10783 // We must use here a binary strlen function (so not dol_strlen)
10784 $strLength = strlen($str);
10785 for ($i = 0; $i < $strLength; $i++) {
10786 if (ord($str[$i]) < 0x80) {
10787 continue; // 0bbbbbbb
10788 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10789 $n = 1; // 110bbbbb
10790 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10791 $n = 2; // 1110bbbb
10792 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10793 $n = 3; // 11110bbb
10794 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10795 $n = 4; // 111110bb
10796 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10797 $n = 5; // 1111110b
10798 } else {
10799 return false; // Does not match any model
10800 }
10801 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10802 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10803 return false;
10804 }
10805 }
10806 }
10807 return true;
10808}
10809
10817function utf8_valid($str)
10818{
10819 /* 2 other methods to test if string is utf8
10820 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10821 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10822 */
10823 return preg_match('//u', $str) ? true : false;
10824}
10825
10826
10833function ascii_check($str)
10834{
10835 if (function_exists('mb_check_encoding')) {
10836 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10837 if (!mb_check_encoding($str, 'ASCII')) {
10838 return false;
10839 }
10840 } else {
10841 if (preg_match('/[^\x00-\x7f]/', $str)) {
10842 return false; // Contains a byte > 7f
10843 }
10844 }
10845
10846 return true;
10847}
10848
10849
10857function dol_osencode($str)
10858{
10859 $tmp = ini_get("unicode.filesystem_encoding");
10860 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10861 $tmp = 'iso-8859-1'; // By default for windows
10862 }
10863 if (empty($tmp)) {
10864 $tmp = 'utf-8'; // By default for other
10865 }
10866 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10867 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10868 }
10869
10870 if ($tmp == 'iso-8859-1') {
10871 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10872 }
10873 return $str;
10874}
10875
10876
10892function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10893{
10894 global $conf;
10895
10896 // If key empty
10897 if ($key == '') {
10898 return 0;
10899 }
10900
10901 // Check in cache
10902 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10903 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10904 }
10905
10906 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10907
10908 $sql = "SELECT ".$fieldid." as valuetoget";
10909 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10910 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10911 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10912 } else {
10913 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10914 }
10915 if (!empty($entityfilter)) {
10916 $sql .= " AND entity IN (".getEntity($tablename).")";
10917 }
10918 if ($filters) {
10919 $sql .= $filters;
10920 }
10921
10922 $resql = $db->query($sql);
10923 if ($resql) {
10924 $obj = $db->fetch_object($resql);
10925 $valuetoget = '';
10926 if ($obj) {
10927 $valuetoget = $obj->valuetoget;
10928 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10929 } else {
10930 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10931 }
10932 $db->free($resql);
10933
10934 return $valuetoget;
10935 } else {
10936 return -1;
10937 }
10938}
10939
10949function isStringVarMatching($var, $regextext, $matchrule = 1)
10950{
10951 if ($matchrule == 1) {
10952 if ($var == 'mainmenu') {
10953 global $mainmenu;
10954 return (preg_match('/^'.$regextext.'/', $mainmenu));
10955 } elseif ($var == 'leftmenu') {
10956 global $leftmenu;
10957 return (preg_match('/^'.$regextext.'/', $leftmenu));
10958 } else {
10959 return 'This variable is not accessible with dol_eval';
10960 }
10961 } else {
10962 return 'This value for matchrule is not implemented';
10963 }
10964}
10965
10966
10976function verifCond($strToEvaluate, $onlysimplestring = '1')
10977{
10978 //print $strToEvaluate."<br>\n";
10979 $rights = true;
10980 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10981 //var_dump($strToEvaluate);
10982 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10983 $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
10984 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10985 //var_dump($rights);
10986 }
10987 return $rights;
10988}
10989
11004function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11005{
11006 if ($returnvalue != 1) {
11007 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
11008 }
11009
11010 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
11011 return dol_eval_new($s);
11012 } else {
11013 return dol_eval_standard($s, $returnvalue, $hideerrors, $onlysimplestring);
11014 }
11015}
11016
11026function dol_eval_new($s)
11027{
11028 // Only this global variables can be read by eval function and returned to caller
11029 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11030 $db, $langs, $user, $website, $websitepage,
11031 $action, $mainmenu, $leftmenu,
11032 $mysoc,
11033 $objectoffield, // To allow the use of $objectoffield in computed fields
11034
11035 // Old variables used
11036 $object,
11037 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11038
11039 // PHP < 7.4.0
11040 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11041 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11042
11043 // PHP < 8.0.0
11044 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11045 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11046 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11047 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11048 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11049
11050 // PHP < 8.1.0
11051 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11052 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11053 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11054 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11055
11056 // PHP < 8.4.0
11057 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11058 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11059 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11060
11061 $prohibited_token_ids = [
11062 /*
11063 * Prohibited int tokens
11064 */
11065
11066 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11067 'T_ABSTRACT', 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', 'T_ATTRIBUTE',
11068 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11069 'T_BAD_CHARACTER',
11070 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11071 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11072 'T_CALLABLE', 'T_CATCH', 'T_CLASS', 'T_CLOSE_TAG', 'T_CONST',
11073 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11074 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11075 'T_DECLARE', 'T_DIR',
11076 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11077 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11078 'T_ECHO', 'T_ENDDECLARE', 'T_ENUM', 'T_EVAL', 'T_EXIT', 'T_EXTENDS',
11079 // 'T_FOR', 'T_FOREACH',
11080 'T_FILE', 'T_FINAL', 'T_FINALLY', 'T_FN', 'T_FUNCTION', 'T_FUNC_C',
11081 'T_GLOBAL', 'T_GOTO',
11082 'T_HALT_COMPILER',
11083 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11084 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11085 'T_IMPLEMENTS', 'T_INCLUDE', 'T_INCLUDE_ONCE', 'T_INSTEADOF', 'T_INTERFACE',
11086 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11087 'T_LINE',
11088 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11089 'T_METHOD_C',
11090 // 'T_NEW',
11091 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11092 'T_NAMESPACE',
11093 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11094 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11095 'T_OPEN_TAG', 'T_OPEN_TAG_WITH_ECHO',
11096 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11097 'T_PRINT', 'T_PRIVATE', 'T_PROTECTED', 'T_PUBLIC',
11098 // 'T_PROPERTY_C',
11099 'T_READONLY', 'T_REQUIRE', 'T_REQUIRE_ONCE', 'T_RETURN',
11100 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11101 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11102 'T_STATIC',
11103 'T_THROW', 'T_TRAIT', 'T_TRAIT_C', 'T_TRY',
11104 'T_UNSET', 'T_UNSET_CAST', 'T_USE',
11105 // 'T_VARIABLE',
11106 'T_VAR',
11107 // 'T_WHILE', 'T_WHITESPACE',
11108 // 'T_XOR_EQUAL',
11109 // 'T_YIELD', 'T_YIELD_FROM',
11110
11111 /*
11112 * Prohibited string tokens
11113 */
11114 ';', '`',
11115 ];
11116
11117 $prohibited_variables = [
11118 '$_COOKIE', '$_ENV', '$_FILES', '$GLOBALS', '$_GET', '$_POST', '$_REQUEST', '$_SERVER', '$_SESSION',
11119 ];
11120
11121 $prohibited_functions = [
11122 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11123 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11124 'override_function', 'session_id', 'session_create_id', 'session_regenerate_id',
11125 'call_user_func', 'call_user_func_array', // PREVENT calling forbidden functions
11126 'exec', 'passthru', 'shell_exec', 'system', 'proc_open', 'popen',
11127 'dol_eval', 'dol_eval_new', 'dol_eval_standard', 'dol_contctdesc', 'executeCLI', 'verifCond', 'GETPOST', // Native Dolibarr functions
11128 'create_function', 'assert', 'mb_ereg_replace', 'mb_eregi_replace', // function with eval capabilities
11129 'dol_compress_dir', 'dol_decode', 'dol_delete_file', 'dol_delete_dir', 'dol_delete_dir_recursive', 'dol_copy', 'archiveOrBackupFile', // more dolibarr functions
11130 'fopen', 'file_put_contents', 'fputs', 'fputscsv', 'fwrite', 'fpassthru', 'mkdir', 'rmdir', 'symlink', 'touch', 'unlink', 'umask', // PHP functions related to file operations
11131 'invoke', 'invokeArgs', // Method of ReflectionFunction to execute a function
11132 'filter_input', 'filter_input_array', 'GETPOST', // PREVENT CODE INJECTION
11133 ];
11134
11135 $prohibited_token_arrangements = [
11136 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11137 ' T_VARIABLE ( ', ' " ( ', ' \' ( ', ' T_CONSTANT_ENCAPSED_STRING ( ', ' ) ( ',
11138 ];
11139
11140 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
11141
11142 $tokens_arrangement = ' ';
11143
11144 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
11145 if (is_array($tokens[$i])) {
11146 $token_id = $tokens[$i][0];
11147 $token_value = $tokens[$i][1];
11148 $token_name = token_name($tokens[$i][0]);
11149 } else {
11150 $token_id = $tokens[$i];
11151 $token_value = $tokens[$i];
11152 $token_name = $tokens[$i];
11153 }
11154
11155 // Ignore whitespaces
11156 if (T_WHITESPACE === $token_id) {
11157 continue;
11158 }
11159
11160 // Keep history to check arrangements
11161 $tokens_arrangement .= "{$token_name} ";
11162
11163 // Prohibited Variables
11164 if (T_VARIABLE === $token_id
11165 && in_array($token_value, $prohibited_variables, true)
11166 ) {
11167 return "« {$token_value} » is prohibited in « {$s} »";
11168 }
11169
11170 // Prohibited Functions
11171 if (T_STRING === $token_id
11172 && in_array($token_value, $prohibited_functions, true)
11173 ) {
11174 return "« {$token_value} » is prohibited in « {$s} »";
11175 }
11176 }
11177
11178 // Prohibited Token IDs
11179 $maxi = count($prohibited_token_ids);
11180 for ($i = 0; $i < $maxi; ++$i) {
11181 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
11182 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
11183 }
11184 }
11185
11186 // Prohibited token arrangements
11187 $maxi = count($prohibited_token_arrangements);
11188 for ($i = 0; $i < $maxi; ++$i) {
11189 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
11190 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
11191 }
11192 }
11193
11194 // Return result
11195 try {
11196 return @eval("return {$s};") ?? '';
11197 } catch (Throwable $ex) {
11198 return "Exception during evaluation: ".$s." - ".$ex->getMessage();
11199 }
11200}
11201
11216function dol_eval_standard($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11217{
11218 // Only this global variables can be read by eval function and returned to caller
11219 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11220 global $db, $langs, $user, $website, $websitepage;
11221 global $action, $mainmenu, $leftmenu;
11222 global $mysoc;
11223 global $objectoffield; // To allow the use of $objectoffield in computed fields
11224
11225 // Old variables used (deprecated)
11226 global $object;
11227 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11228
11229 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
11230 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
11231 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
11232 }
11233 if (!is_scalar($s)) {
11234 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
11235 }
11236
11237 try {
11238 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
11239 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
11240 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
11241 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
11242 // We must accept with 2: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) <= 99) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found"
11243
11244 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
11245 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
11246 if ($onlysimplestring == '2') {
11247 $specialcharsallowed .= '<[]';
11248 }
11249 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
11250 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
11251 }
11252 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
11253 if ($returnvalue) {
11254 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s;
11255 } else {
11256 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING);
11257 return '';
11258 }
11259 }
11260
11261 // Check if we found a ? without a space before and after
11262 $tmps = str_replace(' ? ', '__XXX__', $s);
11263 if (strpos($tmps, '?') !== false) {
11264 if ($returnvalue) {
11265 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s;
11266 } else {
11267 dol_syslog('Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s, LOG_WARNING);
11268 return '';
11269 }
11270 }
11271
11272 // Check if there is a < or <= without spaces before/after
11273 if (preg_match('/<=?[^\s]/', $s)) {
11274 if ($returnvalue) {
11275 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s;
11276 } else {
11277 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING);
11278 return '';
11279 }
11280 }
11281
11282 // Check if there is dynamic call (first we use black list patterns)
11283 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
11284 if ($returnvalue) {
11285 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;
11286 } else {
11287 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a call using "$abc(" or "$abc (" instead of using the direct name of the function): '.$s, LOG_WARNING);
11288 return '';
11289 }
11290 }
11291
11292 // Now we check if we try dynamic call
11293 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
11294 $savescheck = '';
11295 $scheck = $s;
11296 while ($scheck && $savescheck != $scheck) {
11297 $savescheck = $scheck;
11298 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
11299 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
11300 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
11301 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11302 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11303 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
11304 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
11305 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
11306 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
11307 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
11308 }
11309 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
11310
11311 // Now test if it remains 1 open parenthesis.
11312 if (strpos($scheck, '(') !== false) {
11313 if ($returnvalue) {
11314 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
11315 } else {
11316 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s, LOG_WARNING);
11317 return '';
11318 }
11319 }
11320
11321 // TODO
11322 // We can exclude $ char that are not in dol_eval global, so that are not:
11323 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object, $obj, ...,
11324 }
11325 if ($s === 'Array') {
11326 if ($returnvalue) {
11327 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
11328 } else {
11329 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
11330 return '';
11331 }
11332 }
11333
11334 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
11335 if ($returnvalue) {
11336 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
11337 } else {
11338 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
11339 return '';
11340 }
11341 }
11342
11343 if (strpos($s, '`') !== false) {
11344 if ($returnvalue) {
11345 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
11346 } else {
11347 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
11348 return '';
11349 }
11350 }
11351
11352 // Disallow also concat
11353 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
11354 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
11355 if ($returnvalue) {
11356 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
11357 } else {
11358 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
11359 return '';
11360 }
11361 }
11362 }
11363
11364 // We block use of php exec or php file functions
11365 $forbiddenphpstrings = array('$$', '$_', '}[', ')(');
11366 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction'));
11367
11368 // We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all)
11369 // 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
11370 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
11371 $forbiddenphpfunctions = array();
11372 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
11373 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
11374 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
11375
11376 $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"));
11377 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
11378 $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"));
11379 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
11380 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
11381
11382 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
11383
11384 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
11385 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
11386 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "dol_concatdesc", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
11387 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
11388 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
11389 $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
11390 $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"));
11391 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
11392 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) { // We disabllow all function that allow to obfuscate the real name of a function
11393 // @phpcs:ignore
11394 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive
11395 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concatdesc")); // native dolibarr functions
11396 }
11397
11398 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
11399
11400 $forbiddenphpregex = 'global\s*\$';
11401 $forbiddenphpregex .= '|';
11402 $forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
11403
11404 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
11405
11406 do {
11407 $oldstringtoclean = $s;
11408 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
11409 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
11410 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
11411 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
11412 } while ($oldstringtoclean != $s);
11413
11414
11415 if (strpos($s, '__forbiddenstring__') !== false) {
11416 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
11417 if ($returnvalue) {
11418 return 'Bad string syntax to evaluate: '.$s;
11419 } else {
11420 dol_syslog('Bad string syntax to evaluate: '.$s);
11421 return '';
11422 }
11423 }
11424
11425 //print $s."<br>\n";
11426 if ($returnvalue) {
11427 ob_start(); // An evaluation has no reason to output data
11428 $isObBufferActive = true;
11429 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
11430 $tmpo = ob_get_clean();
11431 $isObBufferActive = false;
11432 if ($tmpo) {
11433 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
11434 }
11435 return $tmps;
11436 } else {
11437 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
11438 if ($hideerrors) {
11439 @eval($s);
11440 } else {
11441 eval($s);
11442 }
11443 return '';
11444 }
11445 } catch (Error $e) {
11446 if ($isObBufferActive) {
11447 // Clean up buffer which was left behind due to exception.
11448 $tmpo = ob_get_clean();
11449 $isObBufferActive = false;
11450 }
11451 $error = 'dol_eval try/catch error : ';
11452 $error .= $e->getMessage();
11453 dol_syslog($error, LOG_WARNING);
11454 if ($returnvalue) {
11455 return 'Exception during evaluation: '.$s;
11456 } else {
11457 return '';
11458 }
11459 }
11460}
11461
11469function dol_validElement($element)
11470{
11471 return (trim($element) != '');
11472}
11473
11482function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
11483{
11484 if (empty($codelang)) {
11485 return '';
11486 }
11487
11488 if ($codelang == 'auto') {
11489 return '<span class="fa fa-language"></span>';
11490 }
11491
11492 $langtocountryflag = array(
11493 'ar_AR' => '',
11494 'ca_ES' => 'catalonia',
11495 'da_DA' => 'dk',
11496 'fr_CA' => 'mq',
11497 'sv_SV' => 'se',
11498 'sw_SW' => 'unknown',
11499 'AQ' => 'unknown',
11500 'CW' => 'unknown',
11501 'IM' => 'unknown',
11502 'JE' => 'unknown',
11503 'MF' => 'unknown',
11504 'BL' => 'unknown',
11505 'SX' => 'unknown'
11506 );
11507
11508 if (isset($langtocountryflag[$codelang])) {
11509 $flagImage = $langtocountryflag[$codelang];
11510 } else {
11511 $tmparray = explode('_', $codelang);
11512 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
11513 }
11514
11515 $morecss = '';
11516 $reg = array();
11517 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
11518 $morecss = $reg[1];
11519 $moreatt = "";
11520 }
11521
11522 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
11523 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
11524}
11525
11533function getLanguageCodeFromCountryCode($countrycode)
11534{
11535 global $mysoc;
11536
11537 if (empty($countrycode)) {
11538 return null;
11539 }
11540
11541 if (strtoupper($countrycode) == 'MQ') {
11542 return 'fr_CA';
11543 }
11544 if (strtoupper($countrycode) == 'SE') {
11545 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
11546 }
11547 if (strtoupper($countrycode) == 'CH') {
11548 if ($mysoc->country_code == 'FR') {
11549 return 'fr_CH';
11550 }
11551 if ($mysoc->country_code == 'DE') {
11552 return 'de_CH';
11553 }
11554 if ($mysoc->country_code == 'IT') {
11555 return 'it_CH';
11556 }
11557 }
11558
11559 // Locale list taken from:
11560 // http://stackoverflow.com/questions/3191664/
11561 // list-of-all-locales-and-their-short-codes
11562 $locales = array(
11563 'af-ZA',
11564 'am-ET',
11565 'ar-AE',
11566 'ar-BH',
11567 'ar-DZ',
11568 'ar-EG',
11569 'ar-IQ',
11570 'ar-JO',
11571 'ar-KW',
11572 'ar-LB',
11573 'ar-LY',
11574 'ar-MA',
11575 'ar-OM',
11576 'ar-QA',
11577 'ar-SA',
11578 'ar-SY',
11579 'ar-TN',
11580 'ar-YE',
11581 //'as-IN', // Moved after en-IN
11582 'ba-RU',
11583 'be-BY',
11584 'bg-BG',
11585 'bn-BD',
11586 //'bn-IN', // Moved after en-IN
11587 'bo-CN',
11588 'br-FR',
11589 'ca-ES',
11590 'co-FR',
11591 'cs-CZ',
11592 'cy-GB',
11593 'da-DK',
11594 'de-AT',
11595 'de-CH',
11596 'de-DE',
11597 'de-LI',
11598 'de-LU',
11599 'dv-MV',
11600 'el-GR',
11601 'en-AU',
11602 'en-BZ',
11603 'en-CA',
11604 'en-GB',
11605 'en-IE',
11606 'en-IN',
11607 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
11608 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
11609 'en-JM',
11610 'en-MY',
11611 'en-NZ',
11612 'en-PH',
11613 'en-SG',
11614 'en-TT',
11615 'en-US',
11616 'en-ZA',
11617 'en-ZW',
11618 'es-AR',
11619 'es-BO',
11620 'es-CL',
11621 'es-CO',
11622 'es-CR',
11623 'es-DO',
11624 'es-EC',
11625 'es-ES',
11626 'es-GT',
11627 'es-HN',
11628 'es-MX',
11629 'es-NI',
11630 'es-PA',
11631 'es-PE',
11632 'es-PR',
11633 'es-PY',
11634 'es-SV',
11635 'es-US',
11636 'es-UY',
11637 'es-VE',
11638 'et-EE',
11639 'eu-ES',
11640 'fa-IR',
11641 'fi-FI',
11642 'fo-FO',
11643 'fr-BE',
11644 'fr-CA',
11645 'fr-CH',
11646 'fr-FR',
11647 'fr-LU',
11648 'fr-MC',
11649 'fy-NL',
11650 'ga-IE',
11651 'gd-GB',
11652 'gl-ES',
11653 'gu-IN',
11654 'he-IL',
11655 'hi-IN',
11656 'hr-BA',
11657 'hr-HR',
11658 'hu-HU',
11659 'hy-AM',
11660 'id-ID',
11661 'ig-NG',
11662 'ii-CN',
11663 'is-IS',
11664 'it-CH',
11665 'it-IT',
11666 'ja-JP',
11667 'ka-GE',
11668 'kk-KZ',
11669 'kl-GL',
11670 'km-KH',
11671 'kn-IN',
11672 'ko-KR',
11673 'ky-KG',
11674 'lb-LU',
11675 'lo-LA',
11676 'lt-LT',
11677 'lv-LV',
11678 'mi-NZ',
11679 'mk-MK',
11680 'ml-IN',
11681 'mn-MN',
11682 'mr-IN',
11683 'ms-BN',
11684 'ms-MY',
11685 'mt-MT',
11686 'nb-NO',
11687 'ne-NP',
11688 'nl-BE',
11689 'nl-NL',
11690 'nn-NO',
11691 'oc-FR',
11692 'or-IN',
11693 'pa-IN',
11694 'pl-PL',
11695 'ps-AF',
11696 'pt-BR',
11697 'pt-PT',
11698 'rm-CH',
11699 'ro-MD',
11700 'ro-RO',
11701 'ru-RU',
11702 'rw-RW',
11703 'sa-IN',
11704 'se-FI',
11705 'se-NO',
11706 'se-SE',
11707 'si-LK',
11708 'sk-SK',
11709 'sl-SI',
11710 'sq-AL',
11711 'sv-FI',
11712 'sv-SE',
11713 'sw-KE',
11714 'ta-IN',
11715 'te-IN',
11716 'th-TH',
11717 'tk-TM',
11718 'tn-ZA',
11719 'tr-TR',
11720 'tt-RU',
11721 'ug-CN',
11722 'uk-UA',
11723 'ur-PK',
11724 'vi-VN',
11725 'wo-SN',
11726 'xh-ZA',
11727 'yo-NG',
11728 'zh-CN',
11729 'zh-HK',
11730 'zh-MO',
11731 'zh-SG',
11732 'zh-TW',
11733 'zu-ZA',
11734 );
11735
11736 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
11737 if (in_array($buildprimarykeytotest, $locales)) {
11738 return strtolower($countrycode).'_'.strtoupper($countrycode);
11739 }
11740
11741 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
11742 foreach ($locales as $locale) {
11743 $locale_language = locale_get_primary_language($locale);
11744 $locale_region = locale_get_region($locale);
11745 if (strtoupper($countrycode) == $locale_region) {
11746 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
11747 return strtolower($locale_language).'_'.strtoupper($locale_region);
11748 }
11749 }
11750 } else {
11751 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
11752 }
11753
11754 return null;
11755}
11756
11787function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11788{
11789 global $hookmanager, $db;
11790
11791 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11792 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11793 $values = explode(':', $value);
11794
11795 $reg = array();
11796 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11797 $newtab = array();
11798 $postab = $h;
11799 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11800 $str = $values[1];
11801 $posstart = strpos($str, '(');
11802 if ($posstart > 0) {
11803 $posend = strpos($str, ')');
11804 if ($posstart > 0) {
11805 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11806 if (is_numeric($res1)) {
11807 $postab = (int) $res1;
11808 $values[1] = '+' . substr($str, $posend + 1);
11809 }
11810 }
11811 }
11812 if (count($values) == 6) {
11813 // new declaration with permissions:
11814 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11815 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11816 if ($values[0] != $type) {
11817 continue;
11818 }
11819
11820 if (verifCond($values[4], '2')) {
11821 if ($values[3]) {
11822 if ($filterorigmodule) { // If a filter of module origin has been requested
11823 if (strpos($values[3], '@')) { // This is an external module
11824 if ($filterorigmodule != 'external') {
11825 continue;
11826 }
11827 } else { // This looks a core module
11828 if ($filterorigmodule != 'core') {
11829 continue;
11830 }
11831 }
11832 }
11833 $langs->load($values[3]);
11834 }
11835 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11836 // If label is "SUBSTITUION_..."
11837 $substitutionarray = array();
11838 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11839 $label = make_substitutions($reg[1], $substitutionarray);
11840 } else {
11841 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11842 $labeltemp = explode(',', $values[2]);
11843 $label = $langs->trans($labeltemp[0]);
11844
11845 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11846 dol_include_once($labeltemp[2]);
11847 $classtoload = $labeltemp[1];
11848 if (class_exists($classtoload)) {
11849 $obj = new $classtoload($db);
11850 $function = $labeltemp[3];
11851 if ($obj && $function && method_exists($obj, $function)) {
11852 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11853 $nbrec = $obj->$function($object->id, $obj);
11854 if (!empty($nbrec)) {
11855 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11856 }
11857 }
11858 }
11859 }
11860 }
11861
11862 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11863 $newtab[1] = $label;
11864 $newtab[2] = str_replace('+', '', $values[1]);
11865 $h++;
11866 } else {
11867 continue;
11868 }
11869 } elseif (count($values) == 5) { // case deprecated
11870 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11871
11872 if ($values[0] != $type) {
11873 continue;
11874 }
11875 if ($values[3]) {
11876 if ($filterorigmodule) { // If a filter of module origin has been requested
11877 if (strpos($values[3], '@')) { // This is an external module
11878 if ($filterorigmodule != 'external') {
11879 continue;
11880 }
11881 } else { // This looks a core module
11882 if ($filterorigmodule != 'core') {
11883 continue;
11884 }
11885 }
11886 }
11887 $langs->load($values[3]);
11888 }
11889 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11890 $substitutionarray = array();
11891 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11892 $label = make_substitutions($reg[1], $substitutionarray);
11893 } else {
11894 $label = $langs->trans($values[2]);
11895 }
11896
11897 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11898 $newtab[1] = $label;
11899 $newtab[2] = str_replace('+', '', $values[1]);
11900 $h++;
11901 }
11902 // set tab at its position
11903 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11904 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11905 if ($values[0] != $type) {
11906 continue;
11907 }
11908 $tabname = str_replace('-', '', $values[1]);
11909 foreach ($head as $key => $val) {
11910 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11911 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11912 if ($head[$key][2] == $tabname && $condition) {
11913 unset($head[$key]);
11914 break;
11915 }
11916 }
11917 }
11918 }
11919 }
11920
11921 // No need to make a return $head. Var is modified as a reference
11922 if (!empty($hookmanager)) {
11923 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11924 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
11925 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11926 if ($reshook > 0) { // Hook ask to replace completely the array
11927 $head = $hookmanager->resArray;
11928 } else { // Hook
11929 $head = array_merge($head, $hookmanager->resArray);
11930 }
11931 $h = count($head);
11932 }
11933}
11934
11946function printCommonFooter($zone = 'private')
11947{
11948 global $conf, $hookmanager, $user, $langs;
11949 global $debugbar;
11950 global $action;
11951 global $micro_start_time;
11952
11953 if ($zone == 'private') {
11954 print "\n".'<!-- Common footer for private page -->'."\n";
11955 } else {
11956 print "\n".'<!-- Common footer for public page -->'."\n";
11957 }
11958
11959 // A div to store page_y POST parameter so we can read it using javascript
11960 print "\n<!-- A div to store page_y POST parameter -->\n";
11961 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11962
11963 $parameters = array();
11964 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11965 if (empty($reshook)) {
11966 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11967 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11968 }
11969
11970 print "\n";
11971 if (!empty($conf->use_javascript_ajax)) {
11972 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11973 print '<script>'."\n";
11974 print 'jQuery(document).ready(function() {'."\n";
11975
11976 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11977 print "\n";
11978 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11979 print 'jQuery("li.menuhider").click(function(event) {';
11980 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11981 print ' console.log("We click on .menuhider");'."\n";
11982 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11983 print '});'."\n";
11984 }
11985
11986 // Management of focus and mandatory for fields
11987 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"])))) {
11988 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11989 $relativepathstring = $_SERVER["PHP_SELF"];
11990 // Clean $relativepathstring
11991 if (constant('DOL_URL_ROOT')) {
11992 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11993 }
11994 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11995 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11996 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11997
11998 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11999 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
12000 $qualified = 0;
12001 if ($defkey != '_noquery_') {
12002 $tmpqueryarraytohave = explode('&', $defkey);
12003 $foundintru = 0;
12004 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12005 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12006 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12007 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12008 $foundintru = 1;
12009 }
12010 }
12011 if (!$foundintru) {
12012 $qualified = 1;
12013 }
12014 //var_dump($defkey.'-'.$qualified);
12015 } else {
12016 $qualified = 1;
12017 }
12018
12019 if ($qualified) {
12020 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
12021 foreach ($defval as $paramkey => $paramval) {
12022 // Set focus on field
12023 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
12024 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
12025 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
12026 }
12027 }
12028 }
12029 }
12030 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12031 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12032 $qualified = 0;
12033 if ($defkey != '_noquery_') {
12034 $tmpqueryarraytohave = explode('&', $defkey);
12035 $foundintru = 0;
12036 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12037 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12038 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12039 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12040 $foundintru = 1;
12041 }
12042 }
12043 if (!$foundintru) {
12044 $qualified = 1;
12045 }
12046 //var_dump($defkey.'-'.$qualified);
12047 } else {
12048 $qualified = 1;
12049 }
12050
12051 if ($qualified) {
12052 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
12053
12054 foreach ($defval as $paramkey => $paramval) {
12055 // Solution 1: Add handler on submit to check if mandatory fields are empty
12056 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12057 print "form.on('submit', function(event) {
12058 var submitter = \$(this).find(':submit:focus').get(0);
12059 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12060
12061 if (buttonName == 'cancel') {
12062 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12063 return true;
12064 }
12065
12066 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');
12067
12068 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12069 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12070
12071 if (tmptypefield == 'textarea') {
12072 // We must instead check the content of ckeditor
12073 var tmpeditor = (typeof CKEDITOR !== 'undefined') ? CKEDITOR.instances['".dol_escape_js($paramkey)."'] : null;
12074 if (tmpeditor) {
12075 tmpvalue = tmpeditor.getData();
12076 console.log('For textarea tmpvalue is '+tmpvalue);
12077 }
12078 }
12079
12080 let tmpvalueisempty = false;
12081 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1 || tmpvalue === '-1') {
12082 tmpvalueisempty = true;
12083 }
12084 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
12085 tmpvalueisempty = true;
12086 }
12087 if (tmpvalueisempty && buttonName !== 'cancel') {
12088 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
12089 event.preventDefault(); // Stop submission of form to allow custom code to decide.
12090 event.stopPropagation(); // Stop other handlers.
12091
12092 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
12093
12094 return false;
12095 }
12096 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
12097 return true;
12098 });
12099 \n";
12100
12101 // Solution 2: Add property 'required' on input
12102 // so browser will check value and try to focus on it when submitting the form.
12103 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
12104 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12105 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12106 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
12107 //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";
12108 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
12109 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
12110 // Add 'field required' class on closest td for all input elements : input, textarea and select
12111 //print '}, 500);'; // 500 milliseconds delay
12112
12113 // Now set the class "fieldrequired"
12114 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
12115 }
12116
12117 // If we submit using the cancel button, we remove the required attributes
12118 print 'jQuery("input[name=\'cancel\']").click(function() {
12119 console.log("We click on cancel button so removed all required attribute");
12120 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
12121 });'."\n";
12122 }
12123 }
12124 }
12125 }
12126
12127 print '});'."\n";
12128
12129 // End of tuning
12130 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
12131 print "\n";
12132 print "/* JS CODE TO ENABLE to add memory info */\n";
12133 print 'window.console && console.log("';
12134 if (getDolGlobalString('MEMCACHED_SERVER')) {
12135 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
12136 }
12137 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
12138 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
12139 $micro_end_time = microtime(true);
12140 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
12141 }
12142
12143 if (function_exists("memory_get_usage")) {
12144 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
12145 }
12146 if (function_exists("memory_get_peak_usage")) {
12147 print ' - Real mem peak: '.memory_get_peak_usage(true);
12148 }
12149 if (function_exists("zend_loader_file_encoded")) {
12150 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
12151 }
12152 print '");'."\n";
12153 }
12154
12155 print "\n".'</script>'."\n";
12156
12157 // Google Analytics
12158 // TODO Add a hook here
12159 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
12160 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
12161 foreach ($tmptagarray as $tmptag) {
12162 print "\n";
12163 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
12164 print '
12165 <!-- Global site tag (gtag.js) - Google Analytics -->
12166 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
12167 <script>
12168 window.dataLayer = window.dataLayer || [];
12169 function gtag(){dataLayer.push(arguments);}
12170 gtag(\'js\', new Date());
12171
12172 gtag(\'config\', \''.trim($tmptag).'\');
12173 </script>';
12174 print "\n";
12175 }
12176 }
12177 }
12178
12179 // Add Xdebug coverage of code
12180 if (defined('XDEBUGCOVERAGE')) {
12181 print_r(xdebug_get_code_coverage());
12182 }
12183
12184 // Add DebugBar data
12185 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
12186 if (isset($debugbar['time'])) {
12187 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12188 $debugbar['time']->stopMeasure('pageaftermaster');
12189 }
12190 print '<!-- Output debugbar data -->'."\n";
12191 $renderer = $debugbar->getJavascriptRenderer();
12192 print $renderer->render();
12193 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
12194 print "\n";
12195 print "<!-- Start of log output\n";
12196 //print '<div class="hidden">'."\n";
12197 foreach ($conf->logbuffer as $logline) {
12198 print $logline."<br>\n";
12199 }
12200 //print '</div>'."\n";
12201 print "End of log output -->\n";
12202 }
12203 }
12204}
12205
12215function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
12216{
12217 if (is_null($string)) {
12218 return array();
12219 }
12220
12221 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
12222 // This is a regex string
12223 $newdelimiter = $delimiter;
12224 } else {
12225 // This is a simple string
12226 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
12227 $newdelimiter = preg_quote($delimiter, '/');
12228 }
12229
12230 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
12231 $ka = array();
12232 foreach ($a as $s) { // each part
12233 if ($s) {
12234 if ($pos = strpos($s, $kv)) { // key/value delimiter
12235 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
12236 } else { // key delimiter not found
12237 $ka[] = trim($s);
12238 }
12239 }
12240 }
12241 return $ka;
12242 }
12243
12244 return array();
12245}
12246
12254function dolExplodeKeepIfQuotes($input)
12255{
12256 // Use regexp to capture words and section in quotes
12257 $matches = array();
12258 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
12259
12260 // Merge result and delete empty values
12261
12262 $result = array_map(
12269 static function ($a, $b, $c) {
12270 if ($a !== '') {
12271 return $a;
12272 }
12273 if ($b !== '') {
12274 return $b;
12275 }
12276 if ($c !== '') {
12277 return $c;
12278 }
12279 return '';
12280 },
12281 $matches[1],
12282 $matches[2],
12283 $matches[3]
12284 );
12285 return array_values(array_filter(
12286 $result,
12293 static function ($val) {
12294 return $val !== '';
12295 }
12296 ));
12297}
12298
12299
12306function dol_set_focus($selector)
12307{
12308 print "\n".'<!-- Set focus onto a specific field -->'."\n";
12309 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
12310}
12311
12312
12320function dol_getmypid()
12321{
12322 if (!function_exists('getmypid')) {
12323 return mt_rand(99900000, 99965535);
12324 } else {
12325 return getmypid(); // May be a number on 64 bits (depending on OS)
12326 }
12327}
12328
12350function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
12351{
12352 global $db, $langs;
12353
12354 $value = trim($value);
12355
12356 if ($mode == 0) {
12357 $value = preg_replace('/\*/', '%', $value); // Replace * with %
12358 }
12359 if ($mode == 1) {
12360 $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
12361 }
12362
12363 $value = preg_replace('/\s*\|\s*/', '|', $value);
12364
12365 // Split criteria on ' ' but not if we are inside quotes.
12366 // For mode 3, the split is done later on the , only and not on the ' '.
12367 if ($mode != -3 && $mode != 3) {
12368 $crits = dolExplodeKeepIfQuotes($value);
12369 } else {
12370 $crits = array($value);
12371 }
12372
12373 $res = '';
12374 if (!is_array($fields)) {
12375 $fields = array($fields);
12376 }
12377 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
12378 foreach ($crits as $crit) { // Loop on each AND criteria
12379 $crit = trim($crit);
12380 $i2 = 0; // count the nb of valid criteria added for this this first criteria
12381 $newres = '';
12382
12383 foreach ($fields as $field) {
12384 if ($mode == 1) {
12385 $tmpcrits = explode('|', $crit);
12386 $i3 = 0; // count the nb of valid criteria added for this current field
12387 foreach ($tmpcrits as $tmpcrit) {
12388 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12389 continue;
12390 }
12391 $tmpcrit = trim($tmpcrit);
12392
12393 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12394
12395 $operator = '=';
12396 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
12397
12398 $reg = array();
12399 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
12400 if (!empty($reg[1])) {
12401 $operator = $reg[1];
12402 }
12403 if ($newcrit != '') {
12404 $numnewcrit = price2num($newcrit);
12405 if (is_numeric($numnewcrit)) {
12406 $newres .= $db->sanitize($field).' '.$operator.' '.((float) $numnewcrit); // should be a numeric
12407 } else {
12408 $newres .= '1 = 2'; // force false, we received a corrupted data
12409 }
12410 $i3++; // a criteria was added to string
12411 }
12412 }
12413 $i2++; // a criteria for 1 more field was added to string
12414 } elseif ($mode == 2 || $mode == -2) {
12415 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
12416 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -2 ? 'NOT ' : '');
12417 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
12418 if ($mode == -2) {
12419 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12420 }
12421 $i2++; // a criteria for 1 more field was added to string
12422 } elseif ($mode == 3 || $mode == -3) {
12423 $tmparray = explode(',', $crit);
12424 if (count($tmparray)) {
12425 $listofcodes = '';
12426 foreach ($tmparray as $val) {
12427 $val = trim($val);
12428 if ($val) {
12429 $listofcodes .= ($listofcodes ? ',' : '');
12430 $listofcodes .= "'".$db->escape($val)."'";
12431 }
12432 }
12433 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
12434 $i2++; // a criteria for 1 more field was added to string
12435 }
12436 if ($mode == -3) {
12437 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12438 }
12439 } elseif ($mode == 4) {
12440 $tmparray = explode(',', $crit);
12441 if (count($tmparray)) {
12442 $listofcodes = '';
12443 foreach ($tmparray as $val) {
12444 $val = trim($val);
12445 if ($val) {
12446 $newres .= ($i2 > 0 ? " OR (" : "(").$db->sanitize($field)." LIKE '".$db->escape($val).",%'";
12447 $newres .= ' OR '.$db->sanitize($field)." = '".$db->escape($val)."'";
12448 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val)."'";
12449 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val).",%'";
12450 $newres .= ')';
12451 $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)
12452 }
12453 }
12454 }
12455 } else { // $mode=0
12456 $tmpcrits = explode('|', $crit);
12457 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
12458 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
12459 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12460 continue;
12461 }
12462 $tmpcrit = trim($tmpcrit);
12463
12464 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
12465 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
12466 } else {
12467 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12468 }
12469
12470 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
12471 $newres .= $db->sanitize($field)." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
12472 } else {
12473 $tmpcrit2 = $tmpcrit;
12474 $tmpbefore = '%';
12475 $tmpafter = '%';
12476 $tmps = '';
12477
12478 if (preg_match('/^!/', $tmpcrit)) {
12479 $tmps .= $db->sanitize($field)." NOT LIKE "; // ! as exclude character
12480 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
12481 } else {
12482 $tmps .= $db->sanitize($field)." LIKE ";
12483 }
12484 $tmps .= "'";
12485
12486 if (preg_match('/^[\^\$]/', $tmpcrit)) {
12487 $tmpbefore = '';
12488 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
12489 }
12490 if (preg_match('/[\^\$]$/', $tmpcrit)) {
12491 $tmpafter = '';
12492 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
12493 }
12494
12495 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12496 $tmps = "(".$tmps;
12497 }
12498 $newres .= $tmps;
12499 $newres .= $tmpbefore;
12500 $newres .= $db->escape($tmpcrit2);
12501 $newres .= $tmpafter;
12502 $newres .= "'";
12503 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12504 $newres .= " OR ".$field." IS NULL)";
12505 }
12506 }
12507
12508 $i3++;
12509 }
12510
12511 $i2++; // a criteria for 1 more field was added to string
12512 }
12513 }
12514 if ($newres) {
12515 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
12516 }
12517 $i1++;
12518 }
12519 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
12520
12521 return $res;
12522}
12523
12530function showDirectDownloadLink($object)
12531{
12532 global $langs;
12533
12534 $out = '';
12535 $url = $object->getLastMainDocLink($object->element);
12536
12537 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
12538 if ($url) {
12539 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
12540 $out .= ajax_autoselect("directdownloadlink", '');
12541 } else {
12542 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
12543 }
12544
12545 return $out;
12546}
12547
12556function getImageFileNameForSize($file, $extName, $extImgTarget = '')
12557{
12558 $dirName = dirname($file);
12559 if ($dirName == '.') {
12560 $dirName = '';
12561 }
12562
12563 if (!in_array($extName, array('', '_small', '_mini'))) {
12564 return 'Bad parameter extName';
12565 }
12566
12567 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
12568 $fileName = basename($fileName);
12569
12570 if (empty($extImgTarget)) {
12571 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
12572 }
12573 if (empty($extImgTarget)) {
12574 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
12575 }
12576 if (empty($extImgTarget)) {
12577 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
12578 }
12579 if (empty($extImgTarget)) {
12580 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
12581 }
12582 if (empty($extImgTarget)) {
12583 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
12584 }
12585 if (empty($extImgTarget)) {
12586 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
12587 }
12588
12589 if (!$extImgTarget) {
12590 return $file;
12591 }
12592
12593 $subdir = '';
12594 if ($extName) {
12595 $subdir = 'thumbs/';
12596 }
12597
12598 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
12599}
12600
12601
12611function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
12612{
12613 global $conf, $langs;
12614
12615 if (empty($conf->use_javascript_ajax)) {
12616 return '';
12617 }
12618
12619 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
12620
12621 if ($alldata == 1) {
12622 if ($isAllowedForPreview) {
12623 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));
12624 } else {
12625 return array();
12626 }
12627 }
12628
12629 // old behavior, return a string
12630 if ($isAllowedForPreview) {
12631 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
12632 $title = $langs->transnoentities("Preview");
12633 //$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().
12634 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
12635
12636 // 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,
12637 // 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.
12638 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
12639 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
12640 } else {
12641 return '';
12642 }
12643}
12644
12651function getLabelSpecialCode($idcode)
12652{
12653 global $langs;
12654
12655 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
12656 if ($idcode > 10) {
12657 return 'Module ID '.$idcode;
12658 }
12659 if (!empty($arrayspecialines[$idcode])) {
12660 return $langs->trans($arrayspecialines[$idcode]);
12661 }
12662 return '';
12663}
12664
12673function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
12674{
12675 global $langs;
12676 $out = '<script nonce="'.getNonce().'">
12677 jQuery(document).ready(function () {
12678 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
12679 });
12680 </script>';
12681 if ($addlink) {
12682 if ($textonlink === 'image') {
12683 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
12684 } else {
12685 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
12686 }
12687 }
12688 return $out;
12689}
12690
12698function dolIsAllowedForPreview($file)
12699{
12700 // Check .noexe extension in filename
12701 if (preg_match('/\.noexe$/i', $file)) {
12702 return 0;
12703 }
12704
12705 // Check mime types
12706 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
12707 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
12708 $mime_preview[] = 'svg+xml';
12709 }
12710 //$mime_preview[]='vnd.oasis.opendocument.presentation';
12711 //$mime_preview[]='archive';
12712 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
12713 if ($num_mime !== false) {
12714 return 1;
12715 }
12716
12717 // By default, not allowed for preview
12718 return 0;
12719}
12720
12721
12731function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
12732{
12733 $mime = $default;
12734 $imgmime = 'other.png';
12735 $famime = 'file-o';
12736 $srclang = '';
12737
12738 $tmpfile = preg_replace('/\.noexe$/', '', $file);
12739
12740 // Plain text files
12741 if (preg_match('/\.txt$/i', $tmpfile)) {
12742 $mime = 'text/plain';
12743 $imgmime = 'text.png';
12744 $famime = 'file-alt';
12745 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
12746 $mime = 'text/richtext';
12747 $imgmime = 'text.png';
12748 $famime = 'file-alt';
12749 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
12750 $mime = 'text/csv';
12751 $imgmime = 'text.png';
12752 $famime = 'file-csv';
12753 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
12754 $mime = 'text/tab-separated-values';
12755 $imgmime = 'text.png';
12756 $famime = 'file-alt';
12757 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
12758 $mime = 'text/plain';
12759 $imgmime = 'text.png';
12760 $famime = 'file-alt';
12761 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
12762 $mime = 'text/plain';
12763 $imgmime = 'text.png';
12764 $srclang = 'ini';
12765 $famime = 'file-alt';
12766 } elseif (preg_match('/\.md$/i', $tmpfile)) {
12767 $mime = 'text/plain';
12768 $imgmime = 'text.png';
12769 $srclang = 'md';
12770 $famime = 'file-alt';
12771 } elseif (preg_match('/\.css$/i', $tmpfile)) {
12772 $mime = 'text/css';
12773 $imgmime = 'css.png';
12774 $srclang = 'css';
12775 $famime = 'file-alt';
12776 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
12777 $mime = 'text/plain';
12778 $imgmime = 'text.png';
12779 $srclang = 'lang';
12780 $famime = 'file-alt';
12781 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
12782 $mime = 'text/plain';
12783 $imgmime = 'text.png';
12784 $famime = 'file-alt';
12785 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
12786 $mime = 'text/html';
12787 $imgmime = 'html.png';
12788 $srclang = 'html';
12789 $famime = 'file-alt';
12790 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
12791 $mime = 'text/xml';
12792 $imgmime = 'other.png';
12793 $srclang = 'xml';
12794 $famime = 'file-alt';
12795 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
12796 $mime = 'text/xml';
12797 $imgmime = 'other.png';
12798 $srclang = 'xaml';
12799 $famime = 'file-alt';
12800 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
12801 $mime = 'text/plain';
12802 $imgmime = 'text.png';
12803 $srclang = 'bas';
12804 $famime = 'file-code';
12805 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
12806 $mime = 'text/plain';
12807 $imgmime = 'text.png';
12808 $srclang = 'c';
12809 $famime = 'file-code';
12810 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
12811 $mime = 'text/plain';
12812 $imgmime = 'text.png';
12813 $srclang = 'cpp';
12814 $famime = 'file-code';
12815 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
12816 $mime = 'text/plain';
12817 $imgmime = 'text.png';
12818 $srclang = 'cs';
12819 $famime = 'file-code';
12820 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
12821 $mime = 'text/plain';
12822 $imgmime = 'text.png';
12823 $srclang = 'h';
12824 $famime = 'file-code';
12825 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
12826 $mime = 'text/plain';
12827 $imgmime = 'text.png';
12828 $srclang = 'java';
12829 $famime = 'file-code';
12830 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
12831 $mime = 'text/plain';
12832 $imgmime = 'php.png';
12833 $srclang = 'php';
12834 $famime = 'file-code';
12835 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12836 $mime = 'text/plain';
12837 $imgmime = 'php.png';
12838 $srclang = 'php';
12839 $famime = 'file-code';
12840 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12841 $mime = 'text/plain';
12842 $imgmime = 'pl.png';
12843 $srclang = 'perl';
12844 $famime = 'file-code';
12845 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12846 $mime = 'text/plain';
12847 $imgmime = 'text.png';
12848 $srclang = 'sql';
12849 $famime = 'file-code';
12850 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12851 $mime = 'text/x-javascript';
12852 $imgmime = 'jscript.png';
12853 $srclang = 'js';
12854 $famime = 'file-code';
12855 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12856 $mime = 'application/vnd.oasis.opendocument.presentation';
12857 $imgmime = 'ooffice.png';
12858 $famime = 'file-powerpoint';
12859 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12860 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12861 $imgmime = 'ooffice.png';
12862 $famime = 'file-excel';
12863 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12864 $mime = 'application/vnd.oasis.opendocument.text';
12865 $imgmime = 'ooffice.png';
12866 $famime = 'file-word';
12867 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12868 $mime = 'application/msaccess';
12869 $imgmime = 'mdb.png';
12870 $famime = 'file';
12871 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12872 $mime = 'application/msword';
12873 $imgmime = 'doc.png';
12874 $famime = 'file-word';
12875 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12876 $mime = 'application/msword';
12877 $imgmime = 'doc.png';
12878 $famime = 'file-word';
12879 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12880 $mime = 'application/vnd.ms-excel';
12881 $imgmime = 'xls.png';
12882 $famime = 'file-excel';
12883 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12884 $mime = 'application/vnd.ms-excel';
12885 $imgmime = 'xls.png';
12886 $famime = 'file-excel';
12887 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12888 $mime = 'application/vnd.ms-excel';
12889 $imgmime = 'xls.png';
12890 $famime = 'file-excel';
12891 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12892 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12893 $imgmime = 'xls.png';
12894 $famime = 'file-excel';
12895 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12896 $mime = 'application/vnd.ms-powerpoint';
12897 $imgmime = 'ppt.png';
12898 $famime = 'file-powerpoint';
12899 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12900 $mime = 'application/x-mspowerpoint';
12901 $imgmime = 'ppt.png';
12902 $famime = 'file-powerpoint';
12903 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12904 $mime = 'application/pdf';
12905 $imgmime = 'pdf.png';
12906 $famime = 'file-pdf';
12907 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12908 $mime = 'text/x-bat';
12909 $imgmime = 'script.png';
12910 $srclang = 'dos';
12911 $famime = 'file-code';
12912 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12913 $mime = 'text/x-sh';
12914 $imgmime = 'script.png';
12915 $srclang = 'bash';
12916 $famime = 'file-code';
12917 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12918 $mime = 'text/x-ksh';
12919 $imgmime = 'script.png';
12920 $srclang = 'bash';
12921 $famime = 'file-code';
12922 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12923 $mime = 'text/x-bash';
12924 $imgmime = 'script.png';
12925 $srclang = 'bash';
12926 $famime = 'file-code';
12927 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12928 $mime = 'image/x-icon';
12929 $imgmime = 'image.png';
12930 $famime = 'file-image';
12931 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12932 $mime = 'image/jpeg';
12933 $imgmime = 'image.png';
12934 $famime = 'file-image';
12935 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12936 $mime = 'image/png';
12937 $imgmime = 'image.png';
12938 $famime = 'file-image';
12939 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12940 $mime = 'image/gif';
12941 $imgmime = 'image.png';
12942 $famime = 'file-image';
12943 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12944 $mime = 'image/bmp';
12945 $imgmime = 'image.png';
12946 $famime = 'file-image';
12947 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12948 $mime = 'image/tiff';
12949 $imgmime = 'image.png';
12950 $famime = 'file-image';
12951 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12952 $mime = 'image/svg+xml';
12953 $imgmime = 'image.png';
12954 $famime = 'file-image';
12955 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12956 $mime = 'image/webp';
12957 $imgmime = 'image.png';
12958 $famime = 'file-image';
12959 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12960 $mime = 'text/calendar';
12961 $imgmime = 'other.png';
12962 $famime = 'file-alt';
12963 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12964 $mime = 'text/calendar';
12965 $imgmime = 'other.png';
12966 $famime = 'file-alt';
12967 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12968 $mime = 'application/x-bittorrent';
12969 $imgmime = 'other.png';
12970 $famime = 'file-o';
12971 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12972 $mime = 'audio';
12973 $imgmime = 'audio.png';
12974 $famime = 'file-audio';
12975 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12976 $mime = 'video/mp4';
12977 $imgmime = 'video.png';
12978 $famime = 'file-video';
12979 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12980 $mime = 'video/ogg';
12981 $imgmime = 'video.png';
12982 $famime = 'file-video';
12983 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12984 $mime = 'video/webm';
12985 $imgmime = 'video.png';
12986 $famime = 'file-video';
12987 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12988 $mime = 'video/x-msvideo';
12989 $imgmime = 'video.png';
12990 $famime = 'file-video';
12991 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12992 $mime = 'video/divx';
12993 $imgmime = 'video.png';
12994 $famime = 'file-video';
12995 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12996 $mime = 'video/xvid';
12997 $imgmime = 'video.png';
12998 $famime = 'file-video';
12999 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
13000 $mime = 'video';
13001 $imgmime = 'video.png';
13002 $famime = 'file-video';
13003 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
13004 // application/xxx where zzz is zip, ...
13005 $mime = 'archive';
13006 $imgmime = 'archive.png';
13007 $famime = 'file-archive';
13008 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
13009 $mime = 'application/octet-stream';
13010 $imgmime = 'other.png';
13011 $famime = 'file-o';
13012 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
13013 $mime = 'library';
13014 $imgmime = 'library.png';
13015 $famime = 'file-o';
13016 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13017 $mime = 'error';
13018 $imgmime = 'error.png';
13019 $famime = 'file-alt';
13020 }
13021
13022 if ($famime == 'file-o') {
13023 // file-o seems to not work in fontawesome 5
13024 $famime = 'file';
13025 }
13026
13027 // Return mimetype string
13028 switch ((int) $mode) {
13029 case 1:
13030 $tmp = explode('/', $mime);
13031 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13032 case 2:
13033 return $imgmime;
13034 case 3:
13035 return $srclang;
13036 case 4:
13037 return $famime;
13038 }
13039 return $mime;
13040}
13041
13053function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
13054{
13055 global $conf, $db;
13056
13057 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
13058
13059 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
13060
13061 if (is_null($dictvalues)) {
13062 $dictvalues = array();
13063
13064 $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
13065 if ($checkentity) {
13066 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
13067 }
13068
13069 $resql = $db->query($sql);
13070 if ($resql) {
13071 while ($obj = $db->fetch_object($resql)) {
13072 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
13073 }
13074 } else {
13075 dol_print_error($db);
13076 }
13077
13078 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
13079 }
13080
13081 if (!empty($dictvalues[$id])) {
13082 // Found
13083 $tmp = $dictvalues[$id];
13084 return (property_exists($tmp, $field) ? $tmp->$field : '');
13085 } else {
13086 // Not found
13087 return '';
13088 }
13089}
13090
13097function colorIsLight($stringcolor)
13098{
13099 $stringcolor = str_replace('#', '', $stringcolor);
13100 $res = -1;
13101 if (!empty($stringcolor)) {
13102 $res = 0;
13103 $tmp = explode(',', $stringcolor);
13104 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
13105 $r = $tmp[0];
13106 $g = $tmp[1];
13107 $b = $tmp[2];
13108 } else {
13109 $hexr = $stringcolor[0].$stringcolor[1];
13110 $hexg = $stringcolor[2].$stringcolor[3];
13111 $hexb = $stringcolor[4].$stringcolor[5];
13112 $r = hexdec($hexr);
13113 $g = hexdec($hexg);
13114 $b = hexdec($hexb);
13115 }
13116 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
13117 if ($bright > 0.6) {
13118 $res = 1;
13119 }
13120 }
13121 return $res;
13122}
13123
13132function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
13133{
13134 global $conf;
13135
13136 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
13137 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
13138 if (empty($menuentry['enabled'])) {
13139 return 0; // Entry disabled by condition
13140 }
13141 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
13142 $tmploops = explode('|', $menuentry['module']);
13143 $found = 0;
13144 foreach ($tmploops as $tmploop) {
13145 if (in_array($tmploop, $listofmodulesforexternal)) {
13146 $found++;
13147 break;
13148 }
13149 }
13150 if (!$found) {
13151 return 0; // Entry is for menus all excluded to external users
13152 }
13153 }
13154 if (!$menuentry['perms'] && $type_user) {
13155 return 0; // No permissions and user is external
13156 }
13157 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
13158 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
13159 }
13160 if (!$menuentry['perms']) {
13161 return 2; // No permissions and user is external
13162 }
13163 return 1;
13164}
13165
13173function roundUpToNextMultiple($n, $x = 5)
13174{
13175 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
13176 return (int) $result;
13177}
13178
13190function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
13191{
13192 $csstouse = 'badge';
13193 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
13194 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
13195 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
13196
13197 $attr = array(
13198 'class' => $csstouse
13199 );
13200
13201 if (empty($html)) {
13202 $html = $label;
13203 }
13204
13205 if (!empty($url)) {
13206 $attr['href'] = $url;
13207 }
13208
13209 if ($mode === 'dot') {
13210 $attr['class'] .= ' classfortooltip';
13211 $attr['title'] = $html;
13212 $attr['aria-label'] = $label;
13213 $html = '';
13214 }
13215
13216 // Override attr
13217 if (!empty($params['attr']) && is_array($params['attr'])) {
13218 foreach ($params['attr'] as $key => $value) {
13219 if ($key == 'class') {
13220 $attr['class'] .= ' '.$value;
13221 } elseif ($key == 'classOverride') {
13222 $attr['class'] = $value;
13223 } else {
13224 $attr[$key] = $value;
13225 }
13226 }
13227 }
13228
13229 // TODO: add hook
13230
13231 // escape all attribute
13232 $attr = array_map('dolPrintHTMLForAttribute', $attr);
13233
13234 $TCompiledAttr = array();
13235 foreach ($attr as $key => $value) {
13236 $TCompiledAttr[] = $key.'="'.$value.'"';
13237 }
13238
13239 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
13240
13241 $tag = !empty($url) ? 'a' : 'span';
13242
13243 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
13244}
13245
13246
13259function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
13260{
13261 global $conf;
13262
13263 $return = '';
13264 $dolGetBadgeParams = array();
13265
13266 if (!empty($params['badgeParams'])) {
13267 $dolGetBadgeParams = $params['badgeParams'];
13268 }
13269
13270 // TODO : add a hook
13271 if ($displayMode == 0) {
13272 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
13273 } elseif ($displayMode == 1) {
13274 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13275 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
13276 // Use status with images (for backward compatibility)
13277 $return = '';
13278 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
13279 $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>' : '');
13280
13281 // For small screen, we always use the short label instead of long label.
13282 if (!empty($conf->dol_optimize_smallscreen)) {
13283 if ($displayMode == 0) {
13284 $displayMode = 1;
13285 } elseif ($displayMode == 4) {
13286 $displayMode = 2;
13287 } elseif ($displayMode == 6) {
13288 $displayMode = 5;
13289 }
13290 }
13291
13292 // For backward compatibility. Image's filename are still in French, so we use this array to convert
13293 $statusImg = array(
13294 'status0' => 'statut0',
13295 'status1' => 'statut1',
13296 'status2' => 'statut2',
13297 'status3' => 'statut3',
13298 'status4' => 'statut4',
13299 'status5' => 'statut5',
13300 'status6' => 'statut6',
13301 'status7' => 'statut7',
13302 'status8' => 'statut8',
13303 'status9' => 'statut9'
13304 );
13305
13306 if (!empty($statusImg[$statusType])) {
13307 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
13308 } else {
13309 $htmlImg = img_picto($statusLabel, $statusType);
13310 }
13311
13312 if ($displayMode === 2) {
13313 $return = $htmlImg.' '.$htmlLabelShort;
13314 } elseif ($displayMode === 3) {
13315 $return = $htmlImg;
13316 } elseif ($displayMode === 4) {
13317 $return = $htmlImg.' '.$htmlLabel;
13318 } elseif ($displayMode === 5) {
13319 $return = $htmlLabelShort.' '.$htmlImg;
13320 } else { // $displayMode >= 6
13321 $return = $htmlLabel.' '.$htmlImg;
13322 }
13323 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
13324 // Use new badge
13325 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13326
13327 $dolGetBadgeParams['attr']['class'] = 'badge-status';
13328 if (empty($dolGetBadgeParams['attr']['title'])) {
13329 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
13330 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
13331 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
13332 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
13333 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
13334 }
13335
13336 if ($displayMode == 3) {
13337 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
13338 } elseif ($displayMode === 5) {
13339 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
13340 } else {
13341 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
13342 }
13343 }
13344
13345 return $return;
13346}
13347
13348
13384function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
13385{
13386 global $hookmanager, $action, $object, $langs;
13387
13388 // If $url is an array, we must build a dropdown button or recursively iterate over each value
13389 if (is_array($url)) {
13390 // Loop on $url array to remove entries of disabled modules
13391 foreach ($url as $key => $subbutton) {
13392 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
13393 unset($url[$key]);
13394 }
13395 }
13396
13397 $out = '';
13398
13399 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
13400 foreach ($url as $button) {
13401 if (!empty($button['lang'])) {
13402 $langs->load($button['lang']);
13403 }
13404 $label = $langs->trans($button['label']);
13405 $text = $button['text'] ?? '';
13406 $actionType = $button['actionType'] ?? '';
13407 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
13408 $id = $button['id'] ?? '';
13409 $userRight = $button['perm'] ?? 1;
13410 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
13411
13412 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
13413 }
13414 return $out;
13415 }
13416
13417 if (count($url) > 1) {
13418 $out .= '<div class="dropdown inline-block dropdown-holder">';
13419 $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>';
13420 $out .= '<div class="dropdown-content">';
13421 foreach ($url as $subbutton) {
13422 if (!empty($subbutton['lang'])) {
13423 $langs->load($subbutton['lang']);
13424 }
13425
13426 if (!empty($subbutton['urlraw'])) {
13427 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13428 } else {
13429 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13430 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13431 }
13432
13433 $subbuttonparam = array();
13434 if (!empty($subbutton['attr'])) {
13435 $subbuttonparam['attr'] = $subbutton['attr'];
13436 }
13437 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
13438
13439 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
13440 }
13441 $out .= "</div>";
13442 $out .= "</div>";
13443 } else {
13444 foreach ($url as $subbutton) { // Should loop on 1 record only
13445 if (!empty($subbutton['lang'])) {
13446 $langs->load($subbutton['lang']);
13447 }
13448
13449 if (!empty($subbutton['urlraw'])) {
13450 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13451 } else {
13452 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13453 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13454 }
13455
13456 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
13457 }
13458 }
13459
13460 return $out;
13461 }
13462
13463 // Here, $url is a simple link
13464
13465 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
13466 $class = "dropdown-item";
13467 } else {
13468 $class = 'butAction';
13469 if ($actionType == 'danger' || $actionType == 'delete') {
13470 $class = 'butActionDelete';
13471 if (!empty($url) && strpos($url, 'token=') === false) {
13472 $url .= '&token='.newToken();
13473 }
13474 }
13475 }
13476 $attr = array(
13477 'class' => $class,
13478 'href' => empty($url) ? '' : $url,
13479 'title' => $label
13480 );
13481
13482 if (empty($text)) {
13483 $text = $label;
13484 $attr['title'] = ''; // if html not set, leave label on title is redundant
13485 } else {
13486 $attr['title'] = $label;
13487 $attr['aria-label'] = $label;
13488 }
13489
13490 if (empty($userRight) || $userRight < 0) {
13491 $attr['class'] = 'butActionRefused';
13492 $attr['href'] = '';
13493 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
13494 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '').(empty($userRight) ? $langs->trans('NotEnoughPermissions') : '');
13495 }
13496
13497 if (!empty($id)) {
13498 $attr['id'] = $id;
13499 }
13500
13501 // Override attr
13502 if (!empty($params['attr']) && is_array($params['attr'])) {
13503 foreach ($params['attr'] as $key => $value) {
13504 if ($key == 'class') {
13505 $attr['class'] .= ' '.$value;
13506 } elseif ($key == 'classOverride') {
13507 $attr['class'] = $value;
13508 } else {
13509 $attr[$key] = $value;
13510 }
13511 }
13512 }
13513
13514 // automatic add tooltip when title is detected
13515 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13516 $attr['class'] .= ' classfortooltip';
13517 }
13518
13519 // Js Confirm button
13520 if ($userRight && !empty($params['confirm'])) {
13521 if (!is_array($params['confirm'])) {
13522 $params['confirm'] = array();
13523 }
13524
13525 if (empty($params['confirm']['url'])) {
13526 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
13527 }
13528
13529 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
13530 $attr['data-confirm-url'] = $params['confirm']['url'];
13531 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
13532 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
13533 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
13534 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
13535 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
13536 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
13537
13538 $attr['class'] .= ' butActionConfirm';
13539 }
13540
13541 if (isset($attr['href']) && empty($attr['href'])) {
13542 unset($attr['href']);
13543 }
13544
13545 $TCompiledAttr = array();
13546 foreach ($attr as $key => $value) {
13547 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
13548 // Not recommended
13549 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13550 } elseif ($key == 'href') {
13551 $value = dolPrintHTMLForAttributeUrl($value);
13552 } else {
13553 $value = dolPrintHTMLForAttribute($value);
13554 }
13555
13556 $TCompiledAttr[] = $key.'="'.$value.'"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13557 }
13558
13559 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
13560
13561 $tag = !empty($attr['href']) ? 'a' : 'span';
13562
13563
13564 $parameters = array(
13565 'TCompiledAttr' => $TCompiledAttr, // array
13566 'compiledAttributes' => $compiledAttributes, // string
13567 'attr' => $attr,
13568 'tag' => $tag,
13569 'label' => $label,
13570 'html' => $text,
13571 'actionType' => $actionType,
13572 'url' => $url,
13573 'id' => $id,
13574 'userRight' => $userRight,
13575 'params' => $params
13576 );
13577
13578 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
13579 if ($reshook < 0) {
13580 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
13581 }
13582
13583 if (empty($reshook)) {
13584 if (dol_textishtml($text)) { // If content already HTML encoded
13585 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
13586 } else {
13587 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
13588 }
13589 } else {
13590 return $hookmanager->resPrint;
13591 }
13592}
13593
13594
13603function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
13604{
13605 if (empty($url)) {
13606 return '';
13607 }
13608
13609 $parsedUrl = parse_url($url);
13610 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
13611 return $url;
13612 }
13613
13614 if (!empty($parsedUrl['query'])) {
13615 // Use parse_str() function to parse the string passed via URL
13616 parse_str($parsedUrl['query'], $urlQuery);
13617 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
13618 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
13619 }
13620 }
13621
13622 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
13623 $url = DOL_URL_ROOT.$url;
13624 }
13625
13626 return $url;
13627}
13628
13629
13636function dolGetButtonTitleSeparator($moreClass = "")
13637{
13638 return '<span class="button-title-separator '.$moreClass.'" ></span>';
13639}
13640
13647function getFieldErrorIcon($fieldValidationErrorMsg)
13648{
13649 $out = '';
13650 if (!empty($fieldValidationErrorMsg)) {
13651 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
13652 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
13653 $out .= '</span>';
13654 }
13655
13656 return $out;
13657}
13658
13671function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
13672{
13673 global $langs, $user;
13674
13675 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
13676 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
13677 return '';
13678 }
13679 // Fix old picto fa-th-list to use fa-grid-vertical instead
13680 if ($iconClass == 'fa fa-th-list imgforviewmode') {
13681 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
13682 }
13683
13684 $class = 'btnTitle';
13685 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
13686 $class .= ' btnTitlePlus';
13687 }
13688 $useclassfortooltip = 1;
13689
13690 if (!empty($params['morecss'])) {
13691 $class .= ' '.$params['morecss'];
13692 }
13693
13694 $attr = array(
13695 'class' => $class,
13696 'href' => empty($url) ? '' : $url
13697 );
13698
13699 if (!empty($helpText)) {
13700 $attr['title'] = $helpText;
13701 } elseif ($label) { // empty($attr['title']) &&
13702 $attr['title'] = $label;
13703 $useclassfortooltip = 0;
13704 }
13705
13706 if ($status == 2) {
13707 $attr['class'] .= ' btnTitleSelected';
13708 } elseif ($status <= 0) {
13709 $attr['class'] .= ' refused';
13710
13711 $attr['href'] = '';
13712
13713 if ($status == -1) { // disable
13714 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
13715 } elseif ($status == 0) { // Not enough permissions
13716 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
13717 }
13718 }
13719
13720 if (!empty($attr['title']) && $useclassfortooltip) {
13721 $attr['class'] .= ' classfortooltip';
13722 }
13723
13724 if (!empty($id)) {
13725 $attr['id'] = $id;
13726 }
13727
13728 // Override attr
13729 if (!empty($params['attr']) && is_array($params['attr'])) {
13730 foreach ($params['attr'] as $key => $value) {
13731 if ($key == 'class') {
13732 $attr['class'] .= ' '.$value;
13733 } elseif ($key == 'classOverride') {
13734 $attr['class'] = $value;
13735 } else {
13736 $attr[$key] = $value;
13737 }
13738 }
13739 }
13740
13741 if (isset($attr['href']) && empty($attr['href'])) {
13742 unset($attr['href']);
13743 }
13744
13745 // TODO : add a hook
13746
13747 // Generate attributes with escapement
13748 $TCompiledAttr = array();
13749 foreach ($attr as $key => $value) {
13750 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
13751 }
13752
13753 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
13754
13755 $tag = (empty($attr['href']) ? 'span' : 'a');
13756
13757 $button = '<'.$tag.' '.$compiledAttributes.'>';
13758 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
13759 if (!empty($params['forcenohideoftext'])) {
13760 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
13761 }
13762 $button .= '</'.$tag.'>';
13763
13764 return $button;
13765}
13766
13776function getElementProperties($elementType)
13777{
13778 global $conf, $db, $hookmanager;
13779
13780 $regs = array();
13781
13782 //$element_type='facture';
13783
13784 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
13785
13786 // Parse element/subelement
13787 $module = $elementType;
13788 $element = $elementType;
13789 $subelement = $elementType;
13790 $table_element = $elementType;
13791
13792 // If we ask a resource form external module (instead of default path)
13793 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
13794 $element = $subelement = $regs[1];
13795 $module = $regs[2];
13796 }
13797
13798 // If we ask a resource for a string with an element and a subelement
13799 // Example 'project_task'
13800 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
13801 $module = $element = $regs[1];
13802 $subelement = $regs[2];
13803 }
13804
13805 // Object lines will use parent classpath and module ref
13806 if (substr($elementType, -3) == 'det') {
13807 $module = preg_replace('/det$/', '', $element);
13808 $subelement = preg_replace('/det$/', '', $subelement);
13809 $classpath = $module.'/class';
13810 $classfile = $module;
13811 $classname = preg_replace('/det$/', 'Line', $element);
13812 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
13813 $classname = preg_replace('/det$/', 'Ligne', $element);
13814 }
13815 }
13816 // For compatibility and to work with non standard path
13817 if ($elementType == "action" || $elementType == "actioncomm") {
13818 $classpath = 'comm/action/class';
13819 $subelement = 'Actioncomm';
13820 $module = 'agenda';
13821 $table_element = 'actioncomm';
13822 } elseif ($elementType == 'cronjob') {
13823 $classpath = 'cron/class';
13824 $module = 'cron';
13825 $table_element = 'cron';
13826 } elseif ($elementType == 'adherent_type') {
13827 $classpath = 'adherents/class';
13828 $classfile = 'adherent_type';
13829 $module = 'adherent';
13830 $subelement = 'adherent_type';
13831 $classname = 'AdherentType';
13832 $table_element = 'adherent_type';
13833 } elseif ($elementType == 'bank_account') {
13834 $classpath = 'compta/bank/class';
13835 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13836 $classfile = 'account';
13837 $classname = 'Account';
13838 } elseif ($elementType == 'bank_line') {
13839 $classpath = 'compta/bank/class';
13840 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13841 $classfile = 'account';
13842 $classname = 'AccountLine';
13843 } elseif ($elementType == 'category') {
13844 $classpath = 'categories/class';
13845 $module = 'categorie';
13846 $subelement = 'categorie';
13847 $table_element = 'categorie';
13848 } elseif ($elementType == 'contact') {
13849 $classpath = 'contact/class';
13850 $classfile = 'contact';
13851 $module = 'societe';
13852 $subelement = 'contact';
13853 $table_element = 'socpeople';
13854 } elseif ($elementType == 'inventory') {
13855 $module = 'product';
13856 $classpath = 'product/inventory/class';
13857 } elseif ($elementType == 'inventoryline') {
13858 $module = 'product';
13859 $classpath = 'product/inventory/class';
13860 $table_element = 'inventorydet';
13861 $parent_element = 'inventory';
13862 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
13863 $module = 'stock';
13864 $classpath = 'product/stock/class';
13865 $classfile = 'entrepot';
13866 $classname = 'Entrepot';
13867 $table_element = 'entrepot';
13868 } elseif ($elementType == 'project') {
13869 $classpath = 'projet/class';
13870 $module = 'projet';
13871 $table_element = 'projet';
13872 } elseif ($elementType == 'project_task') {
13873 $classpath = 'projet/class';
13874 $module = 'projet';
13875 $subelement = 'task';
13876 $table_element = 'projet_task';
13877 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13878 $classpath = 'compta/facture/class';
13879 $module = 'facture';
13880 $subelement = 'facture';
13881 $table_element = 'facture';
13882 } elseif ($elementType == 'facturedet') {
13883 $classpath = 'compta/facture/class';
13884 $classfile = 'facture';
13885 $classname = 'FactureLigne';
13886 $module = 'facture';
13887 $table_element = 'facturedet';
13888 $parent_element = 'facture';
13889 } elseif ($elementType == 'facturerec') {
13890 $classpath = 'compta/facture/class';
13891 $classfile = 'facture-rec';
13892 $module = 'facture';
13893 $classname = 'FactureRec';
13894 } elseif ($elementType == 'commande' || $elementType == 'order') {
13895 $classpath = 'commande/class';
13896 $module = 'commande';
13897 $subelement = 'commande';
13898 $table_element = 'commande';
13899 } elseif ($elementType == 'commandedet') {
13900 $classpath = 'commande/class';
13901 $classfile = 'commande';
13902 $classname = 'OrderLine';
13903 $module = 'commande';
13904 $table_element = 'commandedet';
13905 $parent_element = 'commande';
13906 } elseif ($elementType == 'propal') {
13907 $classpath = 'comm/propal/class';
13908 $table_element = 'propal';
13909 } elseif ($elementType == 'propaldet') {
13910 $classpath = 'comm/propal/class';
13911 $classfile = 'propal';
13912 $subelement = 'propaleligne';
13913 $module = 'propal';
13914 $table_element = 'propaldet';
13915 $parent_element = 'propal';
13916 } elseif ($elementType == 'shipping') {
13917 $classpath = 'expedition/class';
13918 $classfile = 'expedition';
13919 $classname = 'Expedition';
13920 $module = 'expedition';
13921 $table_element = 'expedition';
13922 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13923 $classpath = 'expedition/class';
13924 $classfile = 'expedition';
13925 $classname = 'ExpeditionLigne';
13926 $module = 'expedition';
13927 $table_element = 'expeditiondet';
13928 $parent_element = 'expedition';
13929 } elseif ($elementType == 'delivery_note') {
13930 $classpath = 'delivery/class';
13931 $subelement = 'delivery';
13932 $module = 'expedition';
13933 } elseif ($elementType == 'delivery') {
13934 $classpath = 'delivery/class';
13935 $subelement = 'delivery';
13936 $module = 'expedition';
13937 } elseif ($elementType == 'deliverydet') {
13938 // @todo
13939 } elseif ($elementType == 'supplier_proposal') {
13940 $classpath = 'supplier_proposal/class';
13941 $module = 'supplier_proposal';
13942 $element = 'supplierproposal';
13943 $classfile = 'supplier_proposal';
13944 $subelement = 'supplierproposal';
13945 } elseif ($elementType == 'supplier_proposaldet') {
13946 $classpath = 'supplier_proposal/class';
13947 $module = 'supplier_proposal';
13948 $classfile = 'supplier_proposal';
13949 $classname = 'SupplierProposalLine';
13950 $table_element = 'supplier_proposaldet';
13951 $parent_element = 'supplier_proposal';
13952 } elseif ($elementType == 'contract') {
13953 $classpath = 'contrat/class';
13954 $module = 'contrat';
13955 $subelement = 'contrat';
13956 $table_element = 'contract';
13957 } elseif ($elementType == 'contratdet') {
13958 $classpath = 'contrat/class';
13959 $module = 'contrat';
13960 $table_element = 'contratdet';
13961 $parent_element = 'contrat';
13962 } elseif ($elementType == 'mailing') {
13963 $classpath = 'comm/mailing/class';
13964 $module = 'mailing';
13965 $classfile = 'mailing';
13966 $classname = 'Mailing';
13967 $subelement = '';
13968 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13969 $classpath = 'adherents/class';
13970 $module = 'adherent';
13971 $subelement = 'adherent';
13972 $table_element = 'adherent';
13973 } elseif ($elementType == 'subscription') {
13974 $classpath = 'adherents/class';
13975 $classfile = 'subscription';
13976 $module = 'adherent';
13977 $subelement = 'subscription';
13978 $classname = 'Subscription';
13979 $table_element = 'subscription';
13980 } elseif ($elementType == 'usergroup') {
13981 $classpath = 'user/class';
13982 $module = 'user';
13983 } elseif ($elementType == 'mo') {
13984 $classpath = 'mrp/class';
13985 $classfile = 'mo';
13986 $classname = 'Mo';
13987 $module = 'mrp';
13988 $subelement = '';
13989 $table_element = 'mrp_mo';
13990 } elseif ($elementType == 'mrp_production') {
13991 $classpath = 'mrp/class';
13992 $classfile = 'mo';
13993 $classname = 'MoLine';
13994 $module = 'mrp';
13995 $subelement = '';
13996 $table_element = 'mrp_production';
13997 $parent_element = 'mo';
13998 } elseif ($elementType == 'cabinetmed_cons') {
13999 $classpath = 'cabinetmed/class';
14000 $module = 'cabinetmed';
14001 $subelement = 'cabinetmedcons';
14002 $table_element = 'cabinetmedcons';
14003 } elseif ($elementType == 'fichinter') {
14004 $classpath = 'fichinter/class';
14005 $module = 'ficheinter';
14006 $subelement = 'fichinter';
14007 $table_element = 'fichinter';
14008 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
14009 $classpath = 'resource/class';
14010 $module = 'resource';
14011 $subelement = 'dolresource';
14012 $table_element = 'resource';
14013 } elseif ($elementType == 'opensurvey_sondage') {
14014 $classpath = 'opensurvey/class';
14015 $module = 'opensurvey';
14016 $subelement = 'opensurveysondage';
14017 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' ||$elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
14018 $classpath = 'fourn/class';
14019 $module = 'fournisseur';
14020 $classfile = 'fournisseur.commande';
14021 $element = 'order_supplier';
14022 $subelement = '';
14023 $classname = 'CommandeFournisseur';
14024 $table_element = 'commande_fournisseur';
14025 } elseif ($elementType == 'commande_fournisseurdet') {
14026 $classpath = 'fourn/class';
14027 $module = 'fournisseur';
14028 $classfile = 'fournisseur.commande';
14029 $element = 'commande_fournisseurdet';
14030 $subelement = '';
14031 $classname = 'CommandeFournisseurLigne';
14032 $table_element = 'commande_fournisseurdet';
14033 $parent_element = 'commande_fournisseur';
14034 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
14035 $classpath = 'fourn/class';
14036 $module = 'fournisseur';
14037 $classfile = 'fournisseur.facture';
14038 $element = 'invoice_supplier';
14039 $subelement = '';
14040 $classname = 'FactureFournisseur';
14041 $table_element = 'facture_fourn';
14042 } elseif ($elementType == 'facture_fourn_det') {
14043 $classpath = 'fourn/class';
14044 $module = 'fournisseur';
14045 $classfile = 'fournisseur.facture';
14046 $element = 'facture_fourn_det';
14047 $subelement = '';
14048 $classname = 'SupplierInvoiceLine';
14049 $table_element = 'facture_fourn_det';
14050 $parent_element = 'invoice_supplier';
14051 } elseif ($elementType == "service") {
14052 $classpath = 'product/class';
14053 $subelement = 'product';
14054 $table_element = 'product';
14055 } elseif ($elementType == 'salary') {
14056 $classpath = 'salaries/class';
14057 $module = 'salaries';
14058 } elseif ($elementType == 'payment_salary') {
14059 $classpath = 'salaries/class';
14060 $classfile = 'paymentsalary';
14061 $classname = 'PaymentSalary';
14062 $module = 'salaries';
14063 } elseif ($elementType == 'productlot') {
14064 $module = 'productbatch';
14065 $classpath = 'product/stock/class';
14066 $classfile = 'productlot';
14067 $classname = 'Productlot';
14068 $element = 'productlot';
14069 $subelement = '';
14070 $table_element = 'product_lot';
14071 } elseif ($elementType == 'societeaccount') {
14072 $classpath = 'societe/class';
14073 $classfile = 'societeaccount';
14074 $classname = 'SocieteAccount';
14075 $module = 'societe';
14076 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
14077 $classpath = 'website/class';
14078 $classfile = 'websitepage';
14079 $classname = 'Websitepage';
14080 $module = 'website';
14081 $subelement = 'websitepage';
14082 $table_element = 'website_page';
14083 } elseif ($elementType == 'fiscalyear') {
14084 $classpath = 'core/class';
14085 $module = 'accounting';
14086 $subelement = 'fiscalyear';
14087 } elseif ($elementType == 'chargesociales') {
14088 $classpath = 'compta/sociales/class';
14089 $module = 'tax';
14090 $table_element = 'chargesociales';
14091 } elseif ($elementType == 'tva') {
14092 $classpath = 'compta/tva/class';
14093 $module = 'tax';
14094 $subdir = '/vat';
14095 $table_element = 'tva';
14096 } elseif ($elementType == 'emailsenderprofile') {
14097 $module = '';
14098 $classpath = 'core/class';
14099 $classfile = 'emailsenderprofile';
14100 $classname = 'EmailSenderProfile';
14101 $table_element = 'c_email_senderprofile';
14102 $subelement = '';
14103 } elseif ($elementType == 'conferenceorboothattendee') {
14104 $classpath = 'eventorganization/class';
14105 $classfile = 'conferenceorboothattendee';
14106 $classname = 'ConferenceOrBoothAttendee';
14107 $module = 'eventorganization';
14108 } elseif ($elementType == 'conferenceorbooth') {
14109 $classpath = 'eventorganization/class';
14110 $classfile = 'conferenceorbooth';
14111 $classname = 'ConferenceOrBooth';
14112 $module = 'eventorganization';
14113 } elseif ($elementType == 'ccountry') {
14114 $module = '';
14115 $classpath = 'core/class';
14116 $classfile = 'ccountry';
14117 $classname = 'Ccountry';
14118 $table_element = 'c_country';
14119 $subelement = '';
14120 } elseif ($elementType == 'ecmfiles') {
14121 $module = 'ecm';
14122 $classpath = 'ecm/class';
14123 $classfile = 'ecmfiles';
14124 $classname = 'Ecmfiles';
14125 $table_element = 'ecmfiles';
14126 $subelement = '';
14127 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
14128 $module = 'knowledgemanagement';
14129 $classpath = 'knowledgemanagement/class';
14130 $classfile = 'knowledgerecord';
14131 $classname = 'KnowledgeRecord';
14132 $table_element = 'knowledgemanagement_knowledgerecord';
14133 $subelement = '';
14134 } elseif ($elementType == 'customer') {
14135 $module = 'societe';
14136 $classpath = 'societe/class';
14137 $classfile = 'client';
14138 $classname = 'Client';
14139 $table_element = 'societe';
14140 $subelement = '';
14141 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
14142 $module = 'societe';
14143 $classpath = 'fourn/class';
14144 $classfile = 'fournisseur';
14145 $classname = 'Fournisseur';
14146 $table_element = 'societe';
14147 $subelement = '';
14148 }
14149
14150
14151 if (empty($classfile)) {
14152 $classfile = strtolower($subelement);
14153 }
14154 if (empty($classname)) {
14155 $classname = ucfirst($subelement);
14156 }
14157 if (empty($classpath)) {
14158 $classpath = $module.'/class';
14159 }
14160
14161 //print 'getElementProperties subdir='.$subdir;
14162
14163 // Set dir_output
14164 if ($module && isset($conf->$module)) { // The generic case
14165 if (!empty($conf->$module->multidir_output[$conf->entity])) {
14166 $dir_output = $conf->$module->multidir_output[$conf->entity];
14167 } elseif (!empty($conf->$module->output[$conf->entity])) {
14168 $dir_output = $conf->$module->output[$conf->entity];
14169 } elseif (!empty($conf->$module->dir_output)) {
14170 $dir_output = $conf->$module->dir_output;
14171 }
14172 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
14173 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
14174 } elseif (!empty($conf->$module->temp[$conf->entity])) {
14175 $dir_temp = $conf->$module->temp[$conf->entity];
14176 } elseif (!empty($conf->$module->dir_temp)) {
14177 $dir_temp = $conf->$module->dir_temp;
14178 }
14179 }
14180
14181 // Overwrite value for special cases
14182 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
14183 $dir_output = $conf->fournisseur->commande->dir_output;
14184 $dir_temp = $conf->fournisseur->commande->dir_temp;
14185 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
14186 $dir_output = $conf->fournisseur->facture->dir_output;
14187 $dir_temp = $conf->fournisseur->facture->dir_temp;
14188 }
14189 $dir_output .= $subdir;
14190 $dir_temp .= $subdir;
14191
14192 $elementProperties = array(
14193 'module' => $module,
14194 'element' => $element,
14195 'table_element' => $table_element,
14196 'subelement' => $subelement,
14197 'classpath' => $classpath,
14198 'classfile' => $classfile,
14199 'classname' => $classname,
14200 'dir_output' => $dir_output,
14201 'dir_temp' => $dir_temp,
14202 'parent_element' => $parent_element,
14203 );
14204
14205
14206 // Add hook
14207 if (!is_object($hookmanager)) {
14208 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14209 $hookmanager = new HookManager($db);
14210 }
14211 $hookmanager->initHooks(array('elementproperties'));
14212
14213
14214 // Hook params
14215 $parameters = array(
14216 'elementType' => $elementType,
14217 'elementProperties' => $elementProperties
14218 );
14219
14220 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
14221
14222 if ($reshook) {
14223 $elementProperties = $hookmanager->resArray;
14224 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
14225 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
14226 }
14227
14228 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
14229 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
14230 unset($hookmanager->contextarray[$key]);
14231 }
14232
14233 return $elementProperties;
14234}
14235
14248function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
14249{
14250 global $db, $conf;
14251
14252 $ret = 0;
14253
14254 $element_prop = getElementProperties($element_type);
14255
14256 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
14257 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
14258 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
14259 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
14260 // of service and we will return properties of a product.
14261 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
14262 } elseif ($element_prop['module'] == 'societeaccount') {
14263 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
14264 } else {
14265 $ismodenabled = isModEnabled($element_prop['module']);
14266 }
14267 //var_dump('element_type='.$element_type);
14268 //var_dump($element_prop);
14269 //var_dump($element_prop['module'].' '.$ismodenabled);
14270 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
14271 if ($useCache === 1 && $element_id > 0
14272 && !empty($conf->cache['fetchObjectByElement'][$element_type])
14273 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14274 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14275 ) {
14276 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
14277 }
14278
14279 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14280
14281 if (class_exists($element_prop['classname'])) {
14282 $className = $element_prop['classname'];
14283 $objecttmp = new $className($db);
14284 '@phan-var-force CommonObject $objecttmp';
14285
14286 if ($element_id > 0 || !empty($element_ref)) {
14287 $ret = $objecttmp->fetch($element_id, $element_ref);
14288 if ($ret >= 0) {
14289 if (empty($objecttmp->module)) {
14290 $objecttmp->module = $element_prop['module'];
14291 }
14292
14293 if ($useCache > 0) {
14294 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
14295 $conf->cache['fetchObjectByElement'][$element_type] = [];
14296 }
14297
14298 // Manage cache limit
14299 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
14300 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
14301 }
14302
14303 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
14304 }
14305
14306 return $objecttmp;
14307 }
14308 } else {
14309 return $objecttmp; // returned an object without fetch
14310 }
14311 } else {
14312 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14313 return -1;
14314 }
14315 }
14316
14317 return $ret;
14318}
14319
14325function getExecutableContent()
14326{
14327 $arrayofregexextension = array(
14328 'htm', 'html', 'shtml', 'js', 'phar', 'php', 'php3', 'php4', 'php5', 'phtml', 'pht', 'pl', 'py', 'cgi', 'ksh', 'sh', 'shtml',
14329 'bash', 'bat', 'cmd', 'wpk', 'exe', 'dmg', 'appimage'
14330 );
14331
14332 return $arrayofregexextension;
14333}
14334
14341function isAFileWithExecutableContent($filename)
14342{
14343 $arrayofregexextension = getExecutableContent();
14344
14345 foreach ($arrayofregexextension as $fileextension) {
14346 if (preg_match('/\.'.preg_quote($fileextension, '/').'$/i', $filename)) {
14347 return true;
14348 }
14349 }
14350
14351 return false;
14352}
14353
14361function newToken()
14362{
14363 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
14364}
14365
14373function currentToken()
14374{
14375 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
14376}
14377
14383function getNonce()
14384{
14385 global $conf;
14386
14387 if (empty($conf->cache['nonce'])) {
14388 $conf->cache['nonce'] = dolGetRandomBytes(8);
14389 }
14390
14391 return $conf->cache['nonce'];
14392}
14393
14394
14408function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
14409{
14410 global $langs;
14411
14412 print '<div class="div-table-responsive-no-min">';
14413 print '<table class="noborder centpercent">';
14414 print '<tr class="liste_titre">';
14415
14416 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
14417
14418 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
14419
14420 if (!empty($link)) {
14421 if (!empty($arguments)) {
14422 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
14423 } else {
14424 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
14425 }
14426 }
14427
14428 if ($number > -1) {
14429 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
14430 } elseif (!empty($link)) {
14431 print '<span class="badge marginleftonlyshort">...</span>';
14432 }
14433
14434 if (!empty($link)) {
14435 print '</a>';
14436 }
14437
14438 print '</th>';
14439
14440 if ($number < 0 && !empty($link)) {
14441 print '<th class="right">';
14442 print '</th>';
14443 }
14444
14445 print '</tr>';
14446}
14447
14456function finishSimpleTable($addLineBreak = false)
14457{
14458 print '</table>';
14459 print '</div>';
14460
14461 if ($addLineBreak) {
14462 print '<br>';
14463 }
14464}
14465
14477function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
14478{
14479 global $langs;
14480
14481 if ($num === 0) {
14482 print '<tr class="oddeven">';
14483 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
14484 print '</tr>';
14485 return;
14486 }
14487
14488 if ($nbofloop === 0) {
14489 // don't show a summary line
14490 return;
14491 }
14492
14493 /* Case already handled above, commented to satisfy phpstan.
14494 if ($num === 0) {
14495 $colspan = $tableColumnCount;
14496 } else
14497 */
14498 if ($num > $nbofloop) {
14499 $colspan = $tableColumnCount;
14500 } else {
14501 $colspan = $tableColumnCount - 1;
14502 }
14503
14504 if ($extraRightColumn) {
14505 $colspan--;
14506 }
14507
14508 print '<tr class="liste_total">';
14509
14510 if ($nbofloop > 0 && $num > $nbofloop) {
14511 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
14512 } else {
14513 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
14514 print '<td class="right centpercent">'.price($total).'</td>';
14515 }
14516
14517 if ($extraRightColumn) {
14518 print '<td></td>';
14519 }
14520
14521 print '</tr>';
14522}
14523
14532function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
14533{
14534 if ($method == -1) {
14535 $method = 0;
14536 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
14537 $method = 1;
14538 }
14539 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
14540 $method = 2;
14541 }
14542 }
14543
14544 // Be sure we don't have output buffering enabled to have readfile working correctly
14545 while (ob_get_level()) {
14546 ob_end_flush();
14547 }
14548
14549 // Solution 0
14550 if ($method == 0) {
14551 readfile($fullpath_original_file_osencoded);
14552 } elseif ($method == 1) {
14553 // Solution 1
14554 $handle = fopen($fullpath_original_file_osencoded, "rb");
14555 while (!feof($handle)) {
14556 print fread($handle, 8192);
14557 }
14558 fclose($handle);
14559 } elseif ($method == 2) {
14560 // Solution 2
14561 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
14562 $handle2 = fopen("php://output", "wb");
14563 stream_copy_to_stream($handle1, $handle2);
14564 fclose($handle1);
14565 fclose($handle2);
14566 }
14567}
14568
14578function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
14579{
14580 global $langs;
14581
14582 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
14583
14584 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
14585 if ($texttoshow === 'none') {
14586 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14587 $result .= '<span class="clipboardCPValueToPrint"></span>';
14588 } elseif ($texttoshow) {
14589 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14590 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
14591 } else {
14592 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14593 }
14594 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
14595 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
14596 $result .= '<span class="clipboardCPText"></span>';
14597 $result .= '</span>';
14598
14599 return $result;
14600}
14601
14602
14609function jsonOrUnserialize($stringtodecode)
14610{
14611 $result = json_decode($stringtodecode);
14612 if ($result === null) {
14613 $result = unserialize($stringtodecode);
14614 }
14615
14616 return $result;
14617}
14618
14619
14636function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
14637{
14638 global $db, $user;
14639
14640 if (is_null($filter) || !is_string($filter) || $filter === '') {
14641 return '';
14642 }
14643 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
14644 $filter = '(' . $filter . ')';
14645 }
14646
14647 $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'
14648 $firstandlastparenthesis = 0;
14649
14650 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
14651 if ($noerror) {
14652 return '1 = 2';
14653 } else {
14654 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
14655 }
14656 }
14657
14658 // Test the filter syntax
14659 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
14660 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
14661 // If the string result contains something else than '()', the syntax was wrong
14662
14663 if (preg_match('/[^\‍(\‍)]/', $t)) {
14664 $tmperrorstr = 'Bad syntax of the search string';
14665 $errorstr = 'Bad syntax of the search string: '.$filter;
14666 if ($noerror) {
14667 return '1 = 2';
14668 } else {
14669 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
14670 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
14671 }
14672 }
14673
14674 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
14675
14676 if (is_object($db)) {
14677 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
14678 }
14679 if (is_object($user)) {
14680 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
14681 }
14682
14683 return $ret;
14684}
14685
14693function dolForgeExplodeAnd($sqlfilters)
14694{
14695 $arrayofandtags = array();
14696 $nbofchars = dol_strlen($sqlfilters);
14697
14698 $error = '';
14699 $parenthesislevel = 0;
14700 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
14701 if (!$result) {
14702 return array();
14703 }
14704 if ($parenthesislevel >= 1) {
14705 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
14706 }
14707
14708 $i = 0;
14709 $s = '';
14710 $countparenthesis = 0;
14711 while ($i < $nbofchars) {
14712 $char = dol_substr($sqlfilters, $i, 1);
14713
14714 if ($char == '(') {
14715 $countparenthesis++;
14716 } elseif ($char == ')') {
14717 $countparenthesis--;
14718 }
14719
14720 if ($countparenthesis == 0) {
14721 $char2 = dol_substr($sqlfilters, $i + 1, 1);
14722 $char3 = dol_substr($sqlfilters, $i + 2, 1);
14723 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
14724 // We found a AND
14725 $s = trim($s);
14726 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14727 $s = '('.$s.')';
14728 }
14729 $arrayofandtags[] = $s;
14730 $s = '';
14731 $i += 2;
14732 } else {
14733 $s .= $char;
14734 }
14735 } else {
14736 $s .= $char;
14737 }
14738 $i++;
14739 }
14740 if ($s) {
14741 $s = trim($s);
14742 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14743 $s = '('.$s.')';
14744 }
14745 $arrayofandtags[] = $s;
14746 }
14747
14748 return $arrayofandtags;
14749}
14750
14760function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
14761{
14762 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
14763 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
14764 $tmp = $sqlfilters;
14765
14766 $nb = dol_strlen($tmp);
14767 $counter = 0;
14768 $parenthesislevel = 0;
14769
14770 $error = '';
14771
14772 $i = 0;
14773 while ($i < $nb) {
14774 $char = dol_substr($tmp, $i, 1);
14775
14776 if ($char == '(') {
14777 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
14778 // We open a parenthesis and it is the first char
14779 $parenthesislevel++;
14780 }
14781 $counter++;
14782 } elseif ($char == ')') {
14783 $nbcharremaining = ($nb - $i - 1);
14784 if ($nbcharremaining >= $counter) {
14785 $parenthesislevel = min($parenthesislevel, $counter - 1);
14786 }
14787 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
14788 $parenthesislevel = $counter;
14789 }
14790 $counter--;
14791 }
14792
14793 if ($counter < 0) {
14794 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14795 $parenthesislevel = 0;
14796 dol_syslog($error, LOG_WARNING);
14797 return false;
14798 }
14799
14800 $i++;
14801 }
14802
14803 if ($counter > 0) {
14804 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14805 $parenthesislevel = 0;
14806 dol_syslog($error, LOG_WARNING);
14807 return false;
14808 }
14809
14810 return true;
14811}
14812
14820function dolForgeDummyCriteriaCallback($matches)
14821{
14822 //dol_syslog("Convert matches ".$matches[1]);
14823 if (empty($matches[1])) {
14824 return '';
14825 }
14826 $tmp = explode(':', $matches[1]);
14827 if (count($tmp) < 3) {
14828 return '';
14829 }
14830
14831 return '()'; // An empty criteria
14832}
14833
14842function dolForgeSQLCriteriaCallback($matches)
14843{
14844 global $db;
14845
14846 //dol_syslog("Convert matches ".$matches[1]);
14847 if (empty($matches[1])) {
14848 return '';
14849 }
14850 $tmp = explode(':', $matches[1], 3);
14851 if (count($tmp) < 3) {
14852 return '';
14853 }
14854
14855 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
14856
14857 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
14858
14859 $realOperator = [
14860 'NOTLIKE' => 'NOT LIKE',
14861 'ISNOT' => 'IS NOT',
14862 'NOTIN' => 'NOT IN',
14863 '!=' => '<>',
14864 ];
14865
14866 if (array_key_exists($operator, $realOperator)) {
14867 $operator = $realOperator[$operator];
14868 }
14869
14870 $tmpescaped = $tmp[2];
14871
14872 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
14873
14874 $regbis = array();
14875
14876 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID/code/field only (or subrequest if MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTERnot enabled)
14877 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
14878 $tmpescaped2 = '(';
14879 // Explode and sanitize each element in list
14880 $tmpelemarray = explode(',', $tmpescaped);
14881 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
14882 $reg = array();
14883 $tmpelem = trim($tmpelem);
14884 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
14885 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
14886 } elseif (preg_match('/^[0-9]+$/', (string) $tmpelem)) { // if only 0-9 chars, no .
14887 $tmpelemarray[$tmpkey] = (int) $tmpelem;
14888 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
14889 $tmpelemarray[$tmpkey] = (float) $tmpelem;
14890 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
14891 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
14892 } else {
14893 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
14894 }
14895 }
14896 $tmpescaped2 .= implode(',', $tmpelemarray);
14897 $tmpescaped2 .= ')';
14898
14899 $tmpescaped = $tmpescaped2;
14900 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14901 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14902 $tmpescaped = $regbis[1];
14903 }
14904 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14905 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14906 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14907 // TODO Retrieve type of field for $operand field name.
14908 // So we can complete format. For example we could complete a year with month and day.
14909 $tmpescaped = "'".$db->escape($regbis[1])."'";
14910 } else {
14911 if (strtoupper($tmpescaped) == 'NULL') {
14912 $tmpescaped = 'NULL';
14913 } elseif (preg_match('/^[0-9]+$/', (string) $tmpescaped)) { // if only 0-9 chars, no .
14914 $tmpescaped = (int) $tmpescaped;
14915 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
14916 $tmpescaped = (float) $tmpescaped;
14917 } else {
14918 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
14919 }
14920 }
14921
14922 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14923}
14924
14925
14935function getTimelineIcon($actionstatic, &$histo, $key)
14936{
14937 global $langs;
14938
14939 $out = '<!-- timeline icon -->'."\n";
14940 $iconClass = 'fa fa-comments';
14941 $img_picto = '';
14942 $colorClass = '';
14943 $pictoTitle = '';
14944
14945 if ($histo[$key]['percent'] == -1) {
14946 $colorClass = 'timeline-icon-not-applicble';
14947 $pictoTitle = $langs->trans('StatusNotApplicable');
14948 } elseif ($histo[$key]['percent'] == 0) {
14949 $colorClass = 'timeline-icon-todo';
14950 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14951 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14952 $colorClass = 'timeline-icon-in-progress';
14953 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14954 } elseif ($histo[$key]['percent'] >= 100) {
14955 $colorClass = 'timeline-icon-done';
14956 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14957 }
14958
14959 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14960 $iconClass = 'fa fa-ticket';
14961 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14962 $iconClass = 'fa fa-pencilxxx';
14963 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14964 $iconClass = 'fa fa-comments';
14965 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14966 $iconClass = 'fa fa-mask';
14967 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14968 if ($actionstatic->type_picto) {
14969 $img_picto = img_picto('', $actionstatic->type_picto);
14970 } else {
14971 if ($actionstatic->type_code == 'AC_RDV') {
14972 $iconClass = 'fa fa-handshake';
14973 } elseif ($actionstatic->type_code == 'AC_TEL') {
14974 $iconClass = 'fa fa-phone';
14975 } elseif ($actionstatic->type_code == 'AC_FAX') {
14976 $iconClass = 'fa fa-fax';
14977 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14978 $iconClass = 'fa fa-envelope';
14979 } elseif ($actionstatic->type_code == 'AC_INT') {
14980 $iconClass = 'fa fa-shipping-fast';
14981 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14982 $iconClass = 'fa fa-robot';
14983 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14984 $iconClass = 'fa fa-robot';
14985 }
14986 }
14987 }
14988
14989 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14990 return $out;
14991}
14992
15000{
15001 global $conf, $db;
15002
15003 $documents = array();
15004
15005 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
15006 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
15007 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
15008 //$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
15009 $sql.= ' OR ecm.agenda_id = '.(int) $object->id;
15010 $sql .= ' ORDER BY ecm.position ASC';
15011
15012 $resql = $db->query($sql);
15013 if ($resql) {
15014 if ($db->num_rows($resql)) {
15015 while ($obj = $db->fetch_object($resql)) {
15016 $documents[$obj->id] = $obj;
15017 }
15018 }
15019 }
15020
15021 return $documents;
15022}
15023
15024
15042function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
15043{
15044 global $user, $conf;
15045 global $form;
15046
15047 global $param, $massactionbutton;
15048
15049 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
15050
15051 // Check parameters
15052 if (!is_object($filterobj) && !is_object($objcon)) {
15053 dol_print_error(null, 'BadParameter');
15054 }
15055
15056 $histo = array();
15057 '@phan-var-force array<int,array{type:string,tododone:string,id:string,datestart:int|string,dateend:int|string,note:string,message:string,percent:string,userid:string,login:string,userfirstname:string,userlastname:string,userphoto:string,msg_from?:string,contact_id?:string,socpeopleassigned?:int[],lastname?:string,firstname?:string,fk_element?:int,elementtype?:string,acode:string,alabel?:string,libelle?:string,apicto?:string}> $histo';
15058
15059 $numaction = 0;
15060 $now = dol_now();
15061
15062 $sortfield_list = explode(',', $sortfield);
15063 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
15064 $sortfield_new_list = array();
15065 foreach ($sortfield_list as $sortfield_value) {
15066 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
15067 }
15068 $sortfield_new = implode(',', $sortfield_new_list);
15069
15070 $sql = null;
15071 $sql2 = null;
15072
15073 if (isModEnabled('agenda')) {
15074 // Search histo on actioncomm
15075 if (is_object($objcon) && $objcon->id > 0) {
15076 $sql = "SELECT DISTINCT a.id, a.label as label,";
15077 } else {
15078 $sql = "SELECT a.id, a.label as label,";
15079 }
15080 $sql .= " a.datep as dp,";
15081 $sql .= " a.note as message,";
15082 $sql .= " a.datep2 as dp2,";
15083 $sql .= " a.percent as percent, 'action' as type,";
15084 $sql .= " a.fk_element, a.elementtype,";
15085 $sql .= " a.fk_contact,";
15086 $sql .= " a.email_from as msg_from,";
15087 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
15088 $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";
15089 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15090 $sql .= ", sp.lastname, sp.firstname";
15091 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15092 $sql .= ", m.lastname, m.firstname";
15093 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
15094 $sql .= ", o.ref";
15095 }
15096 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
15097 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
15098 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
15099
15100 $force_filter_contact = $filterobj instanceof User;
15101
15102 if (is_object($objcon) && $objcon->id > 0) {
15103 $force_filter_contact = true;
15104 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
15105 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
15106 }
15107
15108 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
15109 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
15110 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
15111 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
15112 $sql .= " ON er.resource_type = 'dolresource'";
15113 $sql .= " AND er.element_id = a.id";
15114 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
15115 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15116 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
15117 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15118 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
15119 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15120 $sql .= ", ".MAIN_DB_PREFIX."product as o";
15121 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15122 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
15123 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15124 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
15125 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15126 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
15127 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15128 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
15129 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15130 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
15131 }
15132
15133 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
15134 if (!$force_filter_contact) {
15135 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
15136 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
15137 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
15138 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
15139 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15140 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
15141 if ($filterobj->id) {
15142 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15143 }
15144 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
15145 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
15146 if ($filterobj->id) {
15147 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15148 }
15149 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15150 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
15151 if ($filterobj->id) {
15152 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15153 }
15154 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15155 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
15156 if ($filterobj->id) {
15157 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15158 }
15159 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15160 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
15161 if ($filterobj->id) {
15162 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15163 }
15164 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15165 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
15166 if ($filterobj->id) {
15167 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15168 }
15169 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15170 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
15171 if ($filterobj->id) {
15172 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15173 }
15174 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
15175 $sql .= " AND a.fk_contact = sp.rowid";
15176 if ($filterobj->id) {
15177 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
15178 }
15179 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15180 $sql .= " AND a.fk_element = o.rowid";
15181 if ($filterobj->id) {
15182 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
15183 }
15184 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15185 $sql .= " AND a.fk_element = o.rowid";
15186 if ($filterobj->id) {
15187 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
15188 }
15189 }
15190 } else {
15191 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
15192 }
15193
15194 // Condition on actioncode
15195 if (!empty($actioncode) && $actioncode != '-1') {
15196 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
15197 if ($actioncode == 'AC_NON_AUTO') {
15198 $sql .= " AND c.type != 'systemauto'";
15199 } elseif ($actioncode == 'AC_ALL_AUTO') {
15200 $sql .= " AND c.type = 'systemauto'";
15201 } else {
15202 if ($actioncode == 'AC_OTH') {
15203 $sql .= " AND c.type != 'systemauto'";
15204 } elseif ($actioncode == 'AC_OTH_AUTO') {
15205 $sql .= " AND c.type = 'systemauto'";
15206 }
15207 }
15208 } else {
15209 if ($actioncode == 'AC_NON_AUTO') {
15210 $sql .= " AND c.type != 'systemauto'";
15211 } elseif ($actioncode == 'AC_ALL_AUTO') {
15212 $sql .= " AND c.type = 'systemauto'";
15213 } else {
15214 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
15215 }
15216 }
15217 }
15218 if ($donetodo == 'todo') {
15219 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15220 } elseif ($donetodo == 'done') {
15221 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15222 }
15223 if (is_array($filters) && $filters['search_agenda_label']) {
15224 $sql .= natural_search('a.label', $filters['search_agenda_label']);
15225 }
15226 }
15227
15228 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
15229 if (isModEnabled('mailing') && !empty($objcon->email)
15230 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
15231 $langs->load("mails");
15232
15233 $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";
15234 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
15235 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
15236 $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
15237 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15238 $sql2 .= ", '' as lastname, '' as firstname";
15239 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15240 $sql2 .= ", '' as lastname, '' as firstname";
15241 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15242 $sql2 .= ", '' as ref";
15243 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15244 $sql2 .= ", '' as ref";
15245 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15246 $sql2 .= ", '' as ref";
15247 }
15248 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
15249 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
15250 $sql2 .= " AND mc.statut = 1";
15251 $sql2 .= " AND u.rowid = m.fk_user_valid";
15252 $sql2 .= " AND mc.fk_mailing=m.rowid";
15253 }
15254
15255 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
15256 if (!empty($sql) && !empty($sql2)) {
15257 $sql = $sql." UNION ".$sql2;
15258 } elseif (empty($sql) && !empty($sql2)) {
15259 $sql = $sql2;
15260 }
15261
15262 //TODO Add navigation with this limits...
15263 $offset = 0;
15264 $limit = 1000;
15265
15266 // Complete request and execute it with limit
15267 $sql .= $db->order($sortfield_new, $sortorder);
15268 if ($limit) {
15269 $sql .= $db->plimit($limit + 1, $offset);
15270 }
15271
15272 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
15273 $resql = $db->query($sql);
15274 if ($resql) {
15275 $i = 0;
15276 $num = $db->num_rows($resql);
15277
15278 $imaxinloop = ($limit ? min($num, $limit) : $num);
15279 while ($i < $imaxinloop) {
15280 $obj = $db->fetch_object($resql);
15281
15282 if ($obj->type == 'action') {
15283 $contactaction = new ActionComm($db);
15284 $contactaction->id = $obj->id;
15285 $result = $contactaction->fetchResources();
15286 if ($result < 0) {
15287 dol_print_error($db);
15288 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
15289 }
15290
15291 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15292 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15293 $tododone = '';
15294 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
15295 $tododone = 'todo';
15296 }
15297
15298 $histo[$numaction] = array(
15299 'type' => $obj->type,
15300 'tododone' => $tododone,
15301 'id' => $obj->id,
15302 'datestart' => $db->jdate($obj->dp),
15303 'dateend' => $db->jdate($obj->dp2),
15304 'note' => $obj->label,
15305 'message' => dol_htmlentitiesbr($obj->message),
15306 'percent' => $obj->percent,
15307
15308 'userid' => $obj->user_id,
15309 'login' => $obj->user_login,
15310 'userfirstname' => $obj->user_firstname,
15311 'userlastname' => $obj->user_lastname,
15312 'userphoto' => $obj->user_photo,
15313 'msg_from' => $obj->msg_from,
15314
15315 'contact_id' => $obj->fk_contact,
15316 'socpeopleassigned' => $contactaction->socpeopleassigned,
15317 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
15318 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
15319 'fk_element' => $obj->fk_element,
15320 'elementtype' => $obj->elementtype,
15321 // Type of event
15322 'acode' => $obj->acode,
15323 'alabel' => $obj->alabel,
15324 'libelle' => $obj->alabel, // deprecated
15325 'apicto' => $obj->apicto
15326 );
15327 } else {
15328 $histo[$numaction] = array(
15329 'type' => $obj->type,
15330 'tododone' => 'done',
15331 'id' => $obj->id,
15332 'datestart' => $db->jdate($obj->dp),
15333 'dateend' => $db->jdate($obj->dp2),
15334 'note' => $obj->label,
15335 'message' => dol_htmlentitiesbr($obj->message),
15336 'percent' => $obj->percent,
15337 'acode' => $obj->acode,
15338
15339 'userid' => $obj->user_id,
15340 'login' => $obj->user_login,
15341 'userfirstname' => $obj->user_firstname,
15342 'userlastname' => $obj->user_lastname,
15343 'userphoto' => $obj->user_photo
15344 );
15345 }
15346
15347 $numaction++;
15348 $i++;
15349 }
15350 } else {
15351 dol_print_error($db);
15352 }
15353 }
15354
15355 // Set $out to show events
15356 $out = '';
15357
15358 if (!isModEnabled('agenda')) {
15359 $langs->loadLangs(array("admin", "errors"));
15360 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
15361 }
15362
15363 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
15364 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
15365
15366 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
15367 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
15368 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
15369 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
15370
15371 $formactions = new FormActions($db);
15372
15373 $actionstatic = new ActionComm($db);
15374 $userstatic = new User($db);
15375 $contactstatic = new Contact($db);
15376 $userGetNomUrlCache = array();
15377 $contactGetNomUrlCache = array();
15378
15379 $out .= '<div class="filters-container" >';
15380 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
15381 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
15382
15383 if ($objcon && get_class($objcon) == 'Contact' &&
15384 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
15385 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
15386 } else {
15387 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
15388 }
15389 if (($filterobj && get_class($filterobj) == 'Societe')) {
15390 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
15391 } else {
15392 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
15393 }
15394
15395 $out .= "\n";
15396
15397 $out .= '<div class="div-table-responsive-no-min">';
15398 $out .= '<table class="noborder borderbottom centpercent">';
15399
15400 $out .= '<tr class="liste_titre">';
15401
15402 // Action column
15403 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15404 $out .= '<th class="liste_titre width50 middle">';
15405 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15406 $out .= $searchpicto;
15407 $out .= '</th>';
15408 }
15409
15410 // Date
15411 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
15412
15413 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
15414 if ($donetodo) {
15415 $out .= '<th class="liste_titre"></th>';
15416 }
15417 // Type of event
15418 $out .= '<th class="liste_titre">';
15419 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
15420 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
15421 $out .= '</th>';
15422 // Label
15423 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
15424 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
15425 $out .= '</th>';
15426
15427 // Action column
15428 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15429 $out .= '<th class="liste_titre width50 middle">';
15430 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15431 $out .= $searchpicto;
15432 $out .= '</th>';
15433 }
15434
15435 $out .= '</tr>';
15436
15437 $out .= '</table>';
15438
15439 $out .= '</form>';
15440 $out .= '</div>';
15441
15442 $out .= "\n";
15443
15444 $out .= '<ul class="timeline">';
15445
15446 if ($donetodo) {
15447 $tmp = '';
15448 if ($filterobj instanceof Societe) {
15449 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15450 }
15451 if ($filterobj instanceof User) {
15452 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15453 }
15454 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
15455 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
15456 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
15457 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
15458 if ($filterobj instanceof Societe) {
15459 $tmp .= '</a>';
15460 }
15461 if ($filterobj instanceof User) {
15462 $tmp .= '</a>';
15463 }
15464 $out .= getTitleFieldOfList($tmp);
15465 }
15466
15467 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
15468 $caction = new CActionComm($db);
15469 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
15470
15471 $actualCycleDate = false;
15472
15473 // Loop on each event to show it
15474 foreach ($histo as $key => $value) {
15475 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
15476
15477 $actionstatic->type_picto = $histo[$key]['apicto'];
15478 $actionstatic->type_code = $histo[$key]['acode'];
15479
15480 $labeltype = $actionstatic->type_code;
15481 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
15482 $labeltype = 'AC_OTH';
15483 }
15484 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15485 $labeltype = $langs->trans("Message");
15486 } else {
15487 if (!empty($arraylist[$labeltype])) {
15488 $labeltype = $arraylist[$labeltype];
15489 }
15490 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
15491 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
15492 }
15493 }
15494
15495 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
15496
15497 $tmpa = dol_getdate($histo[$key]['datestart'], false);
15498
15499 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
15500 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
15501 $out .= '<!-- timeline time label -->';
15502 $out .= '<li class="time-label">';
15503 $out .= '<span class="timeline-badge-date">';
15504 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
15505 $out .= '</span>';
15506 $out .= '</li>';
15507 $out .= '<!-- /.timeline-label -->';
15508 }
15509
15510
15511 $out .= '<!-- timeline item -->'."\n";
15512 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
15513
15514 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
15515 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
15516 //$out .= $timelineicon;
15517 //var_dump($timelineicon);
15518 $out .= $typeicon;
15519
15520 $out .= '<div class="timeline-item">'."\n";
15521
15522 $out .= '<span class="time timeline-header-action2">';
15523
15524 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
15525 $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").' ';
15526 $out .= $histo[$key]['id'];
15527 $out .= '</a> ';
15528 } else {
15529 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
15530 }
15531
15532 if ($user->hasRight('agenda', 'allactions', 'create') ||
15533 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
15534 $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).'">';
15535 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
15536 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
15537 $out .= '</a>';
15538 }
15539
15540 $out .= '</span>';
15541
15542 // Date
15543 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
15544 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
15545 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
15546 $tmpa = dol_getdate($histo[$key]['datestart'], true);
15547 $tmpb = dol_getdate($histo[$key]['dateend'], true);
15548 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
15549 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
15550 } else {
15551 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
15552 }
15553 }
15554 $late = 0;
15555 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15556 $late = 1;
15557 }
15558 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15559 $late = 1;
15560 }
15561 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
15562 $late = 1;
15563 }
15564 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15565 $late = 1;
15566 }
15567 if ($late) {
15568 $out .= img_warning($langs->trans("Late")).' ';
15569 }
15570 $out .= "</span></span>\n";
15571
15572 $out .= '<span class="time">';
15573 $out .= $actionstatic->getLibStatut(2);
15574 $out .= '</span>';
15575
15576 // Ref
15577 $out .= '<h3 class="timeline-header">';
15578
15579 // Author of event
15580 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
15581 if ($histo[$key]['userid'] > 0) {
15582 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
15583 $userstatic->fetch($histo[$key]['userid']);
15584 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
15585 }
15586 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
15587 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
15588 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
15589 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
15590 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
15591 } else {
15592 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
15593 }
15594 }
15595 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
15596 }
15597 $out .= '</div>';
15598
15599 // Title
15600 $out .= ' <div class="messaging-title inline-block">';
15601 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
15602 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
15603 $out .= $labeltype.' - ';
15604 }
15605
15606 $libelle = '';
15607
15608 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
15609 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
15610 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15611 $out .= $langs->trans('TicketNewMessage');
15612 } elseif (isset($histo[$key]['type'])) {
15613 if ($histo[$key]['type'] == 'action') {
15614 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15615 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
15616 $libelle = $histo[$key]['note'];
15617 $actionstatic->id = $histo[$key]['id'];
15618 if ($libelle != $labeltype) {
15619 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15620 }
15621 } elseif ($histo[$key]['type'] == 'mailing') {
15622 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
15623 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15624 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
15625 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15626 } else {
15627 $libelle .= $histo[$key]['note'];
15628 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15629 }
15630 }
15631 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
15632
15633 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
15634 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
15635 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
15636 } else {
15637 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
15638 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
15639 }
15640 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
15641 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
15642 }
15643 if ($link) {
15644 $out .= ' - '.$link;
15645 }
15646 }
15647
15648 $out .= '</div>';
15649
15650 $out .= '</h3>';
15651
15652 // Message
15653 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
15654 && $actionstatic->code != 'AC_TICKET_CREATE'
15655 && $actionstatic->code != 'AC_TICKET_MODIFY'
15656 ) {
15657 $out .= '<div class="timeline-body wordbreak small">';
15658 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
15659 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
15660 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
15661 $out .= '<div class="readmore-block --closed" >';
15662 $out .= ' <div class="readmore-block__excerpt">';
15663 $out .= dolPrintHTML($truncatedText);
15664 $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>';
15665 $out .= ' </div>';
15666 $out .= ' <div class="readmore-block__full-text" >';
15667 $out .= dolPrintHTML($histo[$key]['message']);
15668 $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>';
15669 $out .= ' </div>';
15670 $out .= '</div>';
15671 } else {
15672 $out .= dolPrintHTML($histo[$key]['message']);
15673 }
15674
15675 $out .= '</div>';
15676 }
15677
15678 // Timeline footer
15679 $footer = '';
15680
15681 // Contact for this action
15682 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
15683 $contactList = '';
15684 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
15685 if (empty($conf->cache['contact'][$cid])) {
15686 $contact = new Contact($db);
15687 $contact->fetch($cid);
15688 $conf->cache['contact'][$cid] = $contact;
15689 } else {
15690 $contact = $conf->cache['contact'][$cid];
15691 }
15692
15693 if ($contact) {
15694 $contactList .= !empty($contactList) ? ', ' : '';
15695 $contactList .= $contact->getNomUrl(1);
15696 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15697 if (!empty($contact->phone_pro)) {
15698 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
15699 }
15700 }
15701 }
15702 }
15703
15704 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
15705 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
15706 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
15707 $contact = new Contact($db);
15708 $result = $contact->fetch($histo[$key]['contact_id']);
15709 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
15710 } else {
15711 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
15712 $result = ($contact instanceof Contact) ? $contact->id : 0;
15713 }
15714
15715 if ($result > 0) {
15716 $footer .= $contact->getNomUrl(1);
15717 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15718 if (!empty($contact->phone_pro)) {
15719 $footer .= '('.dol_print_phone($contact->phone_pro).')';
15720 }
15721 }
15722 }
15723 }
15724
15725 $documents = getActionCommEcmList($actionstatic);
15726 if (!empty($documents)) {
15727 $footer .= '<div class="timeline-documents-container">';
15728 foreach ($documents as $doc) {
15729 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
15730 $footer .= ' data-id="'.$doc->id.'" ';
15731 $footer .= ' data-path="'.$doc->filepath.'"';
15732 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
15733 $footer .= '>';
15734
15735 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
15736 $mime = dol_mimetype($filePath);
15737 if (empty($doc->agenda_id)) {
15738 $dir_ref = $actionstatic->id;
15739 $modulepart = 'actions';
15740 } else {
15741 $split_dir = explode('/', $doc->filepath);
15742 $modulepart = array_shift($split_dir);
15743 $dir_ref = implode('/', $split_dir);
15744 }
15745
15746 $file = $dir_ref.'/'.$doc->filename;
15747 $thumb = $dir_ref.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
15748 $doclink = dol_buildpath('document.php', 1).'?modulepart='.$modulepart.'&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
15749 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart='.$modulepart.'&file='.urlencode($thumb).'&entity='.$conf->entity;
15750
15751
15752
15753 $mimeAttr = ' mime="'.$mime.'" ';
15754 $class = '';
15755 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
15756 $class .= ' documentpreview';
15757 }
15758
15759 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
15760 $footer .= img_mime($filePath).' '.$doc->filename;
15761 $footer .= '</a>';
15762
15763 $footer .= '</span>';
15764 }
15765 $footer .= '</div>';
15766 }
15767
15768 if (!empty($footer)) {
15769 $out .= '<div class="timeline-footer">'.$footer.'</div>';
15770 }
15771
15772 $out .= '</div>'."\n"; // end timeline-item
15773
15774 $out .= '</li>';
15775 $out .= '<!-- END timeline item -->';
15776 }
15777
15778 $out .= "</ul>\n";
15779
15780 // Code to manage the click on button data-read-more-action to show full description of an event
15781 $out .= '<script>
15782 jQuery(document).ready(function () {
15783 $(document).on("click", "[data-read-more-action]", function(e){
15784 console.log("We click on data-read-more-action");
15785 let readMoreBloc = $(this).closest(".readmore-block");
15786 if(readMoreBloc.length > 0){
15787 e.preventDefault();
15788 if($(this).attr("data-read-more-action") == "close"){
15789 readMoreBloc.addClass("--closed").removeClass("--open");
15790 $("html, body").animate({
15791 scrollTop: readMoreBloc.offset().top - 200
15792 }, 100);
15793 }else{
15794 readMoreBloc.addClass("--open").removeClass("--closed");
15795 }
15796 }
15797 });
15798 });
15799 </script>';
15800
15801
15802 if (empty($histo)) {
15803 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
15804 }
15805 }
15806
15807 if ($noprint) {
15808 return $out;
15809 } else {
15810 print $out;
15811 return null;
15812 }
15813}
15814
15826function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
15827{
15828 if ($timestamp === null) {
15829 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
15830 }
15831 $TParam = array(
15832 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
15833 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
15834 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
15835 );
15836 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
15837 $TParam = array_merge($TParam, array(
15838 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
15839 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
15840 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
15841 ));
15842 }
15843
15844 return '&' . http_build_query($TParam);
15845}
15846
15865function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
15866{
15867 global $conf, $db, $langs, $hookmanager;
15868 global $action, $object;
15869
15870 if (!is_object($langs)) {
15871 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
15872 $langs = new Translate('', $conf);
15873 $langs->setDefaultLang();
15874 }
15875
15876 $langs->load("errors");
15877
15878 if ($printheader) {
15879 if (function_exists("llxHeader")) {
15880 llxHeader('');
15881 } elseif (function_exists("llxHeaderVierge")) {
15882 llxHeaderVierge('');
15883 }
15884 }
15885
15886 print '<div class="error">';
15887 if (empty($message)) {
15888 print $langs->trans("ErrorRecordNotFound");
15889 } else {
15890 print $langs->trans($message);
15891 }
15892 print '</div>';
15893 print '<br>';
15894
15895 if (empty($showonlymessage)) {
15896 if (empty($hookmanager)) {
15897 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
15898 $hookmanager = new HookManager($db);
15899 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
15900 $hookmanager->initHooks(array('main'));
15901 }
15902
15903 $parameters = array('message' => $message, 'params' => $params);
15904 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
15905 print $hookmanager->resPrint;
15906 }
15907
15908 if ($printfooter && function_exists("llxFooter")) {
15909 llxFooter();
15910 if (is_object($db)) {
15911 $db->close();
15912 }
15913 }
15914 exit(0);
15915}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
if(!defined( 'NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined( 'NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) if(!defined( 'NOLOGIN')) if(!defined('NOCSRFCHECK')) if(!defined( 'NOIPCHECK')) llxHeaderVierge($title, $head="", $disablejs=0, $disablehead=0, $arrayofjs=[], $arrayofcss=[])
Header function.
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:475
ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array(), $morecss='', $htmlname='', $forcenojs=0, $moreparam='')
On/off button to change a property status of an object This uses the ajax service objectonoff....
Definition ajax.lib.php:766
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
$c
Definition line.php:331
$object ref
Definition info.php:90
Class to manage agenda events (actions)
Class to manage different types of events.
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...)
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)
dol_get_prev_month($month, $year)
Return previous month.
Definition date.lib.php:519
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:504
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:86
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition date.lib.php:488
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:538
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert an image file or a PDF into another image format.
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_is_dir($folder)
Test if filename is a directory.
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
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_fiche_end($notab=0)
Show tab footer of a card.
verifCond($strToEvaluate, $onlysimplestring='1')
Verify if condition in string is ok or not.
recordNotFound($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Displays an error page when a record is not found.
getDolGlobalFloat($key, $default=0)
Return a Dolibarr global constant float value.
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formatted size.
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
Function that return localtax of a product line (according to seller, buyer and product vat rate) If ...
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
show_actions_messaging($conf, $langs, $db, $filterobj, $objcon=null, $noprint=0, $actioncode='', $donetodo='done', $filters=array(), $sortfield='a.datep, a.id', $sortorder='DESC')
Show html area with actions in messaging format.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
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.
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.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto='UTF-8')
This function is called to decode a HTML string (it decodes entities and br tags)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs=null, $mode=0, $extralangcode='')
Return a formatted address (part address/zip/town/state) according to country rules.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dolPrintHTML($s, $allowiframe=0)
Return a string (that can be on several lines) ready to be output on a HTML page.
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
dolPrintHTMLForTextArea($s, $allowiframe=0)
Return a string ready to be output on input textarea.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_eval_new($s)
Replace eval function to add more security.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Get formatted error messages to output (Used to show messages on html output).
dol_user_country()
Return country code for current user.
dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes=null)
Clean a string from some undesirable HTML tags.
getMultidirTemp($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its temporary ...
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
dol_get_fiche_end($notab=0)
Return tab footer of a card.
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.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
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-...
checkVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
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.
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.
dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
getImgPictoNameList()
Get all usage icon key usable for img_picto(..., key)
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
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_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_get_object_properties($obj, $properties=[])
Get properties for an object - including magic properties when requested.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_set_focus($selector)
Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
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.
dol_eval_standard($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
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_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 ...
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.
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.
img_pdf($titlealt='default', $size=3)
Show pdf logo.
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolPrintPassword($s)
Return a string ready to be output on an HTML attribute (alt, title, ...)
dol_escape_all($stringtoescape)
Returns text escaped for all protocols (so only alpha chars and numbers)
GETPOSTFLOAT($paramname, $rounding='')
Return the value of a $_GET or $_POST supervariable, converted into float.
dolForgeSQLCriteriaCallback($matches)
Function to forge a SQL criteria from a USF (Universal Filter Syntax) string.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
dol_shutdown()
Function called at end of web php process.
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl='')
Format address string.
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
dol_escape_uri($stringtoescape)
Returns text escaped by RFC 3986 for inclusion into a clickable link.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
print_titre($title)
Show a title.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
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).
dol_clone($object, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
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.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_string_is_good_iso($s, $clean=0)
Check if a string is a correct iso string If not, it will not be considered as HTML encoded even if i...
getNonce()
Return a random string to be used as a nonce value for js.
isStringVarMatching($var, $regextext, $matchrule=1)
Check if a variable with name $var startx with $text.
dolSlugify($stringtoslugify)
Returns text slugified (lowercase and no special char, separator is "-").
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...
get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
Return localtax vat rate of a product in a particular country or default country vat if product is un...
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.
jsonOrUnserialize($stringtodecode)
Decode an encode string.
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.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice=0)
Return vat rate of a product in a particular country, or default country vat if product is unknown.
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.
img_credit_card($brand, $morecss=null)
Return image of a credit card according to its brand name.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
img_searchclear($titlealt='default', $other='')
Show search logo.
getWarningDelay($module, $parmlevel1, $parmlevel2='')
Return a warning delay You can use it like this: if (getWarningDelay('module', 'paramlevel1')) It rep...
utf8_check($str)
Check if a string is in UTF8.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Print formatted error messages to output (Used to show messages on html output).
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
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.
dol_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
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.
treeview li table
No Email.
ui state ui widget content ui state ui widget header ui state a ui button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
div refaddress div address
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
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.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:158
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:161
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition waf.inc.php:66