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 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
162 }
163 if ($forobject && $object->id > 0) {
164 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
165 }
166 return $s;
167 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
168 $s = '';
169 if ($mode != 'outputrel') {
170 $s = $conf->$module->dir_output;
171 }
172 if ($forobject && $object->id > 0) {
173 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
174 }
175 return $s;
176 } else {
177 return 'error-diroutput-not-defined-for-this-object='.$module;
178 }
179 } elseif ($mode == 'temp') {
180 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
181 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
182 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
183 return $conf->$module->dir_temp;
184 } else {
185 return 'error-dirtemp-not-defined-for-this-object='.$module;
186 }
187 } else {
188 return 'error-bad-value-for-mode';
189 }
190}
191
201function getMultidirTemp($object, $module = '', $forobject = 0)
202{
203 return getMultidirOutput($object, $module, $forobject, 'temp');
204}
205
215function getMultidirVersion($object, $module = '', $forobject = 0)
216{
217 return getMultidirOutput($object, $module, $forobject, 'version');
218}
219
220
229function getDolGlobalString($key, $default = '')
230{
231 global $conf;
232 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
233}
234
244function getDolGlobalInt($key, $default = 0)
245{
246 global $conf;
247 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
248}
249
259function getDolGlobalFloat($key, $default = 0)
260{
261 global $conf;
262 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
263}
264
273function getDolGlobalBool($key, $default = false)
274{
275 global $conf;
276 return (bool) ($conf->global->$key ?? $default);
277}
278
288function getDolUserString($key, $default = '', $tmpuser = null)
289{
290 if (empty($tmpuser)) {
291 global $user;
292 $tmpuser = $user;
293 }
294
295 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
296}
297
306function getDolUserInt($key, $default = 0, $tmpuser = null)
307{
308 if (empty($tmpuser)) {
309 global $user;
310 $tmpuser = $user;
311 }
312
313 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
314}
315
316
326define(
327 'MODULE_MAPPING',
328 array(
329 // Map deprecated names to new names
330 'adherent' => 'member', // Has new directory
331 'member_type' => 'adherent_type', // No directory, but file called adherent_type
332 'banque' => 'bank', // Has new directory
333 'contrat' => 'contract', // Has new directory
334 'entrepot' => 'stock', // Has new directory
335 'projet' => 'project', // Has new directory
336 'categorie' => 'category', // Has old directory
337 'commande' => 'order', // Has old directory
338 'expedition' => 'shipping', // Has old directory
339 'facture' => 'invoice', // Has old directory
340 'fichinter' => 'intervention', // Has old directory
341 'ficheinter' => 'intervention', // Backup for 'fichinter'
342 'propale' => 'propal', // Has old directory
343 'socpeople' => 'contact', // Has old directory
344 'fournisseur' => 'supplier', // Has old directory
345
346 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
347 'product_price' => 'productprice', // NO directory
348 'product_fournisseur_price' => 'productsupplierprice', // NO directory
349 )
350);
351
358function isModEnabled($module)
359{
360 global $conf;
361
362 // Fix old names (map to new names)
363 $arrayconv = MODULE_MAPPING;
364 $arrayconvbis = array_flip(MODULE_MAPPING);
365
366 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
367 // Special cases: both use the same module.
368 $arrayconv['supplier_order'] = 'fournisseur';
369 $arrayconv['supplier_invoice'] = 'fournisseur';
370 }
371 // Special case.
372 // @TODO Replace isModEnabled('delivery_note') with
373 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
374 if ($module == 'delivery_note') {
375 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
376 return false;
377 } else {
378 $module = 'shipping';
379 }
380 }
381
382 $module_alt = $module;
383 if (!empty($arrayconv[$module])) {
384 $module_alt = $arrayconv[$module];
385 }
386 $module_bis = $module;
387 if (!empty($arrayconvbis[$module])) {
388 $module_bis = $arrayconvbis[$module];
389 }
390
391 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
392 //return !empty($conf->$module->enabled);
393}
394
405function getWarningDelay($module, $parmlevel1, $parmlevel2 = '')
406{
407 global $conf;
408
409 // For compatibility with bad naming on module
410 $moduletomoduletouse = array(
411 'invoice' => 'facture',
412 );
413 $moduleParmsMapping = array(
414 'product' => 'produit',
415 );
416
417 if (!empty($moduletomoduletouse[$module])) {
418 $module = $moduletomoduletouse[$module];
419 }
420
421 $warningDelayPath = $parmlevel1;
422 if (!empty($moduleParmsMapping[$warningDelayPath])) {
423 $warningDelayPath = $moduleParmsMapping[$warningDelayPath];
424 }
425
426 if ($parmlevel2) {
427 if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
428 if (!empty($conf->$module->$warningDelayPath->$parmlevel2->warning_delay)) {
429 return (int) $conf->$module->$warningDelayPath->$parmlevel2->warning_delay;
430 }
431 }
432 } else {
433 if (!empty($conf->$module->$warningDelayPath->warning_delay)) {
434 return (int) $conf->$module->$warningDelayPath->$parmlevel1->warning_delay;
435 }
436 }
437
438 return 0;
439}
440
447function isDolTms($timestamp)
448{
449 if ($timestamp === '') {
450 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
451 return false;
452 }
453 if (is_null($timestamp) || !is_numeric($timestamp)) {
454 return false;
455 }
456
457 return true;
458}
459
471function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
472{
473 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
474
475 $class = 'DoliDB'.ucfirst($type);
476 $db = new $class($type, $host, $user, $pass, $name, $port);
477 return $db;
478}
479
497function getEntity($element, $shared = 1, $currentobject = null)
498{
499 global $conf, $mc, $hookmanager, $object, $action, $db;
500
501 if (!is_object($hookmanager)) {
502 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
503 $hookmanager = new HookManager($db);
504 }
505
506 // fix different element names (France to English)
507 switch ($element) {
508 case 'projet':
509 $element = 'project';
510 break;
511 case 'contrat':
512 $element = 'contract';
513 break; // "/contrat/class/contrat.class.php"
514 case 'order_supplier':
515 $element = 'supplier_order';
516 break; // "/fourn/class/fournisseur.commande.class.php"
517 case 'invoice_supplier':
518 $element = 'supplier_invoice';
519 break; // "/fourn/class/fournisseur.facture.class.php"
520 }
521
522 if (is_object($mc)) {
523 $out = $mc->getEntity($element, $shared, $currentobject);
524 } else {
525 $out = '';
526 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
527 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
528 $addzero[] = 'c_holiday_types';
529 }
530 if (in_array($element, $addzero)) {
531 $out .= '0,';
532 }
533 $out .= ((int) $conf->entity);
534 }
535
536 // Manipulate entities to query on the fly
537 $parameters = array(
538 'element' => $element,
539 'shared' => $shared,
540 'object' => $object,
541 'currentobject' => $currentobject,
542 'out' => $out
543 );
544 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
545 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
546
547 if (is_numeric($reshook)) {
548 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
549 $out .= ','.$hookmanager->resPrint; // add
550 } elseif ($reshook == 1) {
551 $out = $hookmanager->resPrint; // replace
552 }
553 }
554
555 return $out;
556}
557
564function setEntity($currentobject)
565{
566 global $conf, $mc;
567
568 if (is_object($mc) && method_exists($mc, 'setEntity')) {
569 return $mc->setEntity($currentobject);
570 } else {
571 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
572 }
573}
574
581function isASecretKey($keyname)
582{
583 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
584}
585
586
593function num2Alpha($n)
594{
595 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
596 $r = chr($n % 26 + 0x41) . $r;
597 }
598 return $r;
599}
600
601
618function getBrowserInfo($user_agent)
619{
620 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
621
622 $name = 'unknown';
623 $version = '';
624 $os = 'unknown';
625 $phone = '';
626
627 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
628
629 $detectmobile = new Mobile_Detect(null, $user_agent);
630 $tablet = $detectmobile->isTablet();
631
632 if ($detectmobile->isMobile()) {
633 $phone = 'unknown';
634
635 // If phone/smartphone, we set phone os name.
636 if ($detectmobile->is('AndroidOS')) {
637 $os = $phone = 'android';
638 } elseif ($detectmobile->is('BlackBerryOS')) {
639 $os = $phone = 'blackberry';
640 } elseif ($detectmobile->is('iOS')) {
641 $os = 'ios';
642 $phone = 'iphone';
643 } elseif ($detectmobile->is('PalmOS')) {
644 $os = $phone = 'palm';
645 } elseif ($detectmobile->is('SymbianOS')) {
646 $os = 'symbian';
647 } elseif ($detectmobile->is('webOS')) {
648 $os = 'webos';
649 } elseif ($detectmobile->is('MaemoOS')) {
650 $os = 'maemo';
651 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
652 $os = 'windows';
653 }
654 }
655
656 // OS
657 if (preg_match('/linux/i', $user_agent)) {
658 $os = 'linux';
659 } elseif (preg_match('/macintosh/i', $user_agent)) {
660 $os = 'macintosh';
661 } elseif (preg_match('/windows/i', $user_agent)) {
662 $os = 'windows';
663 }
664
665 // Name
666 $reg = array();
667 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
668 $name = 'firefox';
669 $version = empty($reg[2]) ? '' : $reg[2];
670 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
671 $name = 'edge';
672 $version = empty($reg[2]) ? '' : $reg[2];
673 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
674 $name = 'chrome';
675 $version = empty($reg[2]) ? '' : $reg[2];
676 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
677 // we can have 'chrome (Mozilla...) chrome x.y' in one string
678 $name = 'chrome';
679 } elseif (preg_match('/iceweasel/i', $user_agent)) {
680 $name = 'iceweasel';
681 } elseif (preg_match('/epiphany/i', $user_agent)) {
682 $name = 'epiphany';
683 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
684 $name = 'safari';
685 $version = empty($reg[2]) ? '' : $reg[2];
686 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
687 // Safari is often present in string for mobile but its not.
688 $name = 'opera';
689 $version = empty($reg[2]) ? '' : $reg[2];
690 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
691 $name = 'ie';
692 $version = end($reg);
693 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
694 // MS products at end
695 $name = 'ie';
696 $version = end($reg);
697 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
698 // MS products at end
699 $name = 'textbrowser';
700 $version = empty($reg[3]) ? '' : $reg[3];
701 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
702 // MS products at end
703 $name = 'textbrowser';
704 $version = empty($reg[1]) ? '' : $reg[1];
705 }
706
707 if ($tablet) {
708 $layout = 'tablet';
709 } elseif ($phone) {
710 $layout = 'phone';
711 } else {
712 $layout = 'classic';
713 }
714
715 return array(
716 'browsername' => $name,
717 'browserversion' => $version,
718 'browseros' => $os,
719 'browserua' => $user_agent,
720 'layout' => $layout, // tablet, phone, classic
721 'phone' => $phone, // deprecated
722 'tablet' => $tablet // deprecated
723 );
724}
725
731function dol_shutdown()
732{
733 global $db;
734 $disconnectdone = false;
735 $depth = 0;
736 if (is_object($db) && !empty($db->connected)) {
737 $depth = $db->transaction_opened;
738 $disconnectdone = $db->close();
739 }
740 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));
741}
742
752function GETPOSTISSET($paramname)
753{
754 $isset = false;
755
756 $relativepathstring = $_SERVER["PHP_SELF"];
757 // Clean $relativepathstring
758 if (constant('DOL_URL_ROOT')) {
759 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
760 }
761 $relativepathstring = ltrim($relativepathstring, '/');
762 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
763 //var_dump($relativepathstring);
764 //var_dump($user->default_values);
765
766 // Code for search criteria persistence.
767 // Retrieve values if restore_lastsearch_values
768 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
769 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
770 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
771 if (is_array($tmp)) {
772 foreach ($tmp as $key => $val) {
773 if ($key == $paramname) { // We are on the requested parameter
774 $isset = true;
775 break;
776 }
777 }
778 }
779 }
780 // If there is saved contextpage, limit, page or mode
781 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
782 $isset = true;
783 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
784 $isset = true;
785 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
786 $isset = true;
787 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
788 $isset = true;
789 }
790 } else {
791 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
792 }
793
794 return $isset;
795}
796
805function GETPOSTISARRAY($paramname, $method = 0)
806{
807 // for $method test need return the same $val as GETPOST
808 if (empty($method)) {
809 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
810 } elseif ($method == 1) {
811 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
812 } elseif ($method == 2) {
813 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
814 } elseif ($method == 3) {
815 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
816 } else {
817 $val = 'BadFirstParameterForGETPOST';
818 }
819
820 return is_array($val);
821}
822
853function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
854{
855 global $mysoc, $user, $conf;
856
857 if (empty($paramname)) { // Explicit test for null for phan.
858 return 'BadFirstParameterForGETPOST';
859 }
860 if (empty($check)) {
861 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);
862 // Enable this line to know who call the GETPOST with '' $check parameter.
863 //var_dump(getCallerInfoString());
864 }
865
866 if (empty($method)) {
867 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
868 } elseif ($method == 1) {
869 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
870 } elseif ($method == 2) {
871 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
872 } elseif ($method == 3) {
873 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
874 } else {
875 return 'BadThirdParameterForGETPOST';
876 }
877
878 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
879
880 if (empty($method) || $method == 3 || $method == 4) {
881 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
882 // Clean $relativepathstring
883 if (constant('DOL_URL_ROOT')) {
884 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
885 }
886 $relativepathstring = ltrim($relativepathstring, '/');
887 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
888 //var_dump($relativepathstring);
889 //var_dump($user->default_values);
890
891 // Code for search criteria persistence.
892 // Retrieve saved values if restore_lastsearch_values is set
893 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
894 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
895 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
896 if (is_array($tmp)) {
897 foreach ($tmp as $key => $val) {
898 if ($key == $paramname) { // We are on the requested parameter
899 $out = $val;
900 break;
901 }
902 }
903 }
904 }
905 // If there is saved contextpage, page or limit
906 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
907 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
908 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
909 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
910 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
911 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
912 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
913 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
914 }
915 } elseif (!isset($_GET['sortfield'])) {
916 // Else, retrieve default values if we are not doing a sort
917 // 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
918 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
919 // Search default value from $object->field
920 global $object;
921 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
922 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
923 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
924 $out = $object->fields[$paramname]['default'];
925 }
926 }
927 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
928 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
929 // Now search in setup to overwrite default values
930 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
931 if (isset($user->default_values[$relativepathstring]['createform'])) {
932 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
933 $qualified = 0;
934 if ($defkey != '_noquery_') {
935 $tmpqueryarraytohave = explode('&', $defkey);
936 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
937 $foundintru = 0;
938 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
939 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
940 $foundintru = 1;
941 }
942 }
943 if (!$foundintru) {
944 $qualified = 1;
945 }
946 //var_dump($defkey.'-'.$qualified);
947 } else {
948 $qualified = 1;
949 }
950
951 if ($qualified) {
952 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
953 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
954 break;
955 }
956 }
957 }
958 }
959 }
960 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
961 // Management of default search_filters and sort order
962 if (!empty($user->default_values)) {
963 // $user->default_values defined from menu 'Setup - Default values'
964 //var_dump($user->default_values[$relativepathstring]);
965 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
966 // Sorted on which fields ? ASC or DESC ?
967 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
968 // Even if paramname is sortfield, data are stored into ['sortorder...']
969 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
970 $qualified = 0;
971 if ($defkey != '_noquery_') {
972 $tmpqueryarraytohave = explode('&', $defkey);
973 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
974 $foundintru = 0;
975 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
976 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
977 $foundintru = 1;
978 }
979 }
980 if (!$foundintru) {
981 $qualified = 1;
982 }
983 //var_dump($defkey.'-'.$qualified);
984 } else {
985 $qualified = 1;
986 }
987
988 if ($qualified) {
989 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
990 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
991 if ($out) {
992 $out .= ', ';
993 }
994 if ($paramname == 'sortfield') {
995 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
996 }
997 if ($paramname == 'sortorder') {
998 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
999 }
1000 }
1001 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
1002 }
1003 }
1004 }
1005 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
1006 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
1007 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
1008 continue;
1009 }
1010 $qualified = 0;
1011 if ($defkey != '_noquery_') {
1012 $tmpqueryarraytohave = explode('&', $defkey);
1013 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
1014 $foundintru = 0;
1015 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
1016 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
1017 $foundintru = 1;
1018 }
1019 }
1020 if (!$foundintru) {
1021 $qualified = 1;
1022 }
1023 //var_dump($defkey.'-'.$qualified);
1024 } else {
1025 $qualified = 1;
1026 }
1027
1028 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
1029 // We must keep $_POST and $_GET here
1030 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
1031 // We made a search from quick search menu, do we still use default filter ?
1032 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
1033 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1034 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1035 }
1036 } else {
1037 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
1038 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
1039 }
1040 break;
1041 }
1042 }
1043 }
1044 }
1045 }
1046 }
1047 }
1048 }
1049
1050 // 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)
1051 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1052 // 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.
1053 '@phan-var-force string $paramname';
1054 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1055 $reg = array();
1056 $regreplace = array();
1057 $maxloop = 20;
1058 $loopnb = 0; // Protection against infinite loop
1059
1060 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.
1061 $loopnb++;
1062 $newout = '';
1063
1064 if ($reg[1] == 'DAY') {
1065 $tmp = dol_getdate(dol_now(), true);
1066 $newout = $tmp['mday'];
1067 } elseif ($reg[1] == 'MONTH') {
1068 $tmp = dol_getdate(dol_now(), true);
1069 $newout = $tmp['mon'];
1070 } elseif ($reg[1] == 'YEAR') {
1071 $tmp = dol_getdate(dol_now(), true);
1072 $newout = $tmp['year'];
1073 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1074 $tmp = dol_getdate(dol_now(), true);
1075 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1076 $newout = $tmp2['day'];
1077 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1078 $tmp = dol_getdate(dol_now(), true);
1079 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1080 $newout = $tmp2['month'];
1081 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1082 $tmp = dol_getdate(dol_now(), true);
1083 $newout = ($tmp['year'] - 1);
1084 } elseif ($reg[1] == 'NEXT_DAY') {
1085 $tmp = dol_getdate(dol_now(), true);
1086 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1087 $newout = $tmp2['day'];
1088 } elseif ($reg[1] == 'NEXT_MONTH') {
1089 $tmp = dol_getdate(dol_now(), true);
1090 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1091 $newout = $tmp2['month'];
1092 } elseif ($reg[1] == 'NEXT_YEAR') {
1093 $tmp = dol_getdate(dol_now(), true);
1094 $newout = ($tmp['year'] + 1);
1095 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1096 $newout = $mysoc->country_id;
1097 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1098 $newout = $user->id;
1099 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1100 $newout = $user->fk_user;
1101 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1102 $newout = $conf->entity;
1103 } elseif ($reg[1] == 'ID') {
1104 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1105 } else {
1106 $newout = 'REGREPLACE_'.$loopnb; // Key not found, we replace with temporary string to reload later
1107 $regreplace[$loopnb] = $reg[0];
1108 }
1109 //var_dump('__'.$reg[1].'__ -> '.$newout);
1110 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1111 }
1112 if (!empty($regreplace)) {
1113 foreach ($regreplace as $key => $value) {
1114 $out = preg_replace('/REGREPLACE_'.$key.'/', $value, $out);
1115 }
1116 }
1117 }
1118
1119 // Check type of variable and make sanitization according to this
1120 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1121 $tmpcheck = 'alphanohtml';
1122 if (empty($out)) {
1123 $out = array();
1124 } elseif (!is_array($out)) {
1125 $out = explode(',', $out);
1126 } else {
1127 $tmparray = explode(':', $check);
1128 if (!empty($tmparray[1])) {
1129 $tmpcheck = $tmparray[1];
1130 }
1131 }
1132 foreach ($out as $outkey => $outval) {
1133 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1134 }
1135 } else {
1136 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1137 // 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
1138 if (strpos($paramname, 'search_') === 0) {
1139 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1140 }
1141
1142 // @phan-suppress-next-line UnknownSanitizeType
1143 $out = sanitizeVal($out, $check, $filter, $options);
1144 }
1145
1146 // Sanitizing for special parameters.
1147 // 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.
1148 // @TODO Merge backtopage with backtourl
1149 // @TODO Rename backtolist into backtopagelist
1150 if (preg_match('/^backto/i', $paramname)) {
1151 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1152 $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.
1153 do {
1154 $oldstringtoclean = $out;
1155 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1156 $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'
1157 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1158 } while ($oldstringtoclean != $out);
1159 }
1160
1161 // Code for search criteria persistence.
1162 // Save data into session if key start with 'search_'
1163 if (empty($method) || $method == 3 || $method == 4) {
1164 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1165 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1166
1167 // We save search key only if $out not empty that means:
1168 // - posted value not empty, or
1169 // - 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).
1170
1171 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1172 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1173 }
1174 }
1175 }
1176
1177 if ($paramname == 'hashp' && $out == 'shared') {
1178 $out = ''; // We refuse to have hashp=shared as a parameter
1179 }
1180
1181 return $out;
1182}
1183
1193function GETPOSTINT($paramname, $method = 0)
1194{
1195 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1196}
1197
1206function GETPOSTFLOAT($paramname, $rounding = '')
1207{
1208 // 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.)
1209 return (float) price2num(GETPOST($paramname), $rounding, 2);
1210}
1211
1227function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto', $saverestore = '')
1228{
1229 $m = array();
1230 if ($hourTime === 'getpost' || $hourTime === 'getpostend') {
1231 $hour = (GETPOSTISSET($prefix . 'hour') && GETPOSTINT($prefix . 'hour') >= 0) ? GETPOSTINT($prefix . 'hour') : ($hourTime === 'getpostend' ? 23 : 0);
1232 $minute = (GETPOSTISSET($prefix . 'min') && GETPOSTINT($prefix . 'min') >= 0) ? GETPOSTINT($prefix . 'min') : ($hourTime === 'getpostend' ? 59 : 0);
1233 $second = (GETPOSTISSET($prefix . 'sec') && GETPOSTINT($prefix . 'sec') >= 0) ? GETPOSTINT($prefix . 'sec') : ($hourTime === 'getpostend' ? 59 : 0);
1234 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1235 $hour = intval($m[1]);
1236 $minute = intval($m[2]);
1237 $second = intval($m[3]);
1238 } elseif ($hourTime === 'end') {
1239 $hour = 23;
1240 $minute = 59;
1241 $second = 59;
1242 } else {
1243 $hour = $minute = $second = 0;
1244 }
1245
1246 if (
1247 $saverestore
1248 && !GETPOSTISSET($prefix . 'day')
1249 && !GETPOSTISSET($prefix . 'month')
1250 && !GETPOSTISSET($prefix . 'year')
1251 && isset($_SESSION['DOLDATE_' . $saverestore . '_day'])
1252 && isset($_SESSION['DOLDATE_' . $saverestore . '_month'])
1253 && isset($_SESSION['DOLDATE_' . $saverestore . '_year'])
1254 ) {
1255 $day = $_SESSION['DOLDATE_'.$saverestore.'_day'];
1256 $month = $_SESSION['DOLDATE_'.$saverestore.'_month'];
1257 $year = $_SESSION['DOLDATE_'.$saverestore.'_year'];
1258 } else {
1259 $month = GETPOSTINT($prefix . 'month');
1260 $day = GETPOSTINT($prefix . 'day');
1261 $year = GETPOSTINT($prefix . 'year');
1262 }
1263
1264 // normalize out of range values
1265 $hour = (int) min($hour, 23);
1266 $minute = (int) min($minute, 59);
1267 $second = (int) min($second, 59);
1268
1269 if ($saverestore) {
1270 $_SESSION['DOLDATE_'.$saverestore.'_day'] = $day;
1271 $_SESSION['DOLDATE_'.$saverestore.'_month'] = $month;
1272 $_SESSION['DOLDATE_'.$saverestore.'_year'] = $year;
1273 }
1274
1275 //print "$hour, $minute, $second, $month, $day, $year, $gm<br>";
1276 return dol_mktime($hour, $minute, $second, $month, $day, $year, $gm);
1277}
1278
1279
1290function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1291{
1292 return sanitizeVal($out, $check, $filter, $options);
1293}
1294
1304function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1305{
1306 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1307 // Check is done after replacement
1308 if ($out === null) {
1309 $out = '';
1310 }
1311 switch ($check) {
1312 case 'none':
1313 case 'password':
1314 break;
1315 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1316 if (!is_numeric($out)) {
1317 $out = '';
1318 }
1319 break;
1320 case 'intcomma':
1321 if (is_array($out)) {
1322 $out = implode(',', $out);
1323 }
1324 if (preg_match('/[^0-9,-]+/i', $out)) {
1325 $out = '';
1326 }
1327 break;
1328 case 'san_alpha':
1329 $out = filter_var($out, FILTER_SANITIZE_STRING);
1330 break;
1331 case 'email':
1332 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1333 break;
1334 case 'url':
1335 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1336 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1337 // TODO Allow ( ) but only into password of https://login:password@domain...
1338 break;
1339 case 'aZ':
1340 if (!is_array($out)) {
1341 $out = trim($out);
1342 if (preg_match('/[^a-z]+/i', $out)) {
1343 $out = '';
1344 }
1345 }
1346 break;
1347 case 'aZ09':
1348 if (!is_array($out)) {
1349 $out = trim($out);
1350 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1351 $out = '';
1352 }
1353 }
1354 break;
1355 case 'aZ09arobase': // great to sanitize $objecttype parameter
1356 if (!is_array($out)) {
1357 $out = trim($out);
1358 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1359 $out = '';
1360 }
1361 }
1362 break;
1363 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1364 if (!is_array($out)) {
1365 $out = trim($out);
1366 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1367 $out = '';
1368 }
1369 }
1370 break;
1371 case 'alpha': // No html and no ../ and "
1372 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1373 if (!is_array($out)) {
1374 $out = trim($out);
1375 do {
1376 $oldstringtoclean = $out;
1377 // Remove html tags
1378 $out = dol_string_nohtmltag($out, 0);
1379 // 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).
1380 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1381 // Remove also other dangerous string sequences
1382 // '../' or '..\' is dangerous because it allows dir transversals
1383 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1384 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1385 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1386 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1387 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1388 } while ($oldstringtoclean != $out);
1389 // keep lines feed
1390 }
1391 break;
1392 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'
1393 if (!is_array($out)) {
1394 $out = trim($out);
1395 do {
1396 $oldstringtoclean = $out;
1397 // Decode html entities
1398 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1399 // 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).
1400 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1401 // Remove also other dangerous string sequences
1402 // '../' or '..\' is dangerous because it allows dir transversals
1403 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1404 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1405 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1406 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1407 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1408 } while ($oldstringtoclean != $out);
1409 }
1410 break;
1411 case 'nohtml': // No html. Valid for JSON strings.
1412 $out = dol_string_nohtmltag($out, 0);
1413 break;
1414 case 'restricthtmlnolink':
1415 case 'restricthtml': // Recommended for most html textarea
1416 case 'restricthtmlallowclass':
1417 case 'restricthtmlallowiframe':
1418 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1419 case 'restricthtmlallowunvalid':
1420 $out = dol_htmlwithnojs($out, 1, $check);
1421 break;
1422 case 'custom':
1423 if (!empty($out)) {
1424 if (empty($filter)) {
1425 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1426 }
1427 if (is_null($options)) {
1428 $options = 0;
1429 }
1430 $out = filter_var($out, $filter, $options);
1431 }
1432 break;
1433 default:
1434 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1435 $out = GETPOST($out, 'alphanohtml');
1436 break;
1437 }
1438
1439 return $out;
1440}
1441
1450function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1451{
1452 global $dolibarr_main_force_https;
1453
1454 if ($expire == -1) {
1455 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1456 }
1457
1458 if (PHP_VERSION_ID < 70300) {
1459 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1460 } else {
1461 // Only available for php >= 7.3
1462 $cookieparams = array(
1463 'expires' => empty($cookievalue) ? 0 : $expire,
1464 'path' => '/',
1465 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1466 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1467 'httponly' => true,
1468 'samesite' => 'Lax' // None || Lax || Strict
1469 );
1470 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1471 }
1472 if (empty($cookievalue)) {
1473 unset($_COOKIE[$cookiename]);
1474 }
1475}
1476
1477if (!function_exists('dol_getprefix')) {
1488 function dol_getprefix($mode = '')
1489 {
1490 // If prefix is for email (we need to have $conf already loaded for this case)
1491 if ($mode == 'email') {
1492 global $conf;
1493
1494 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1495 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1496 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1497 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1498 return $_SERVER["SERVER_NAME"];
1499 }
1500 }
1501
1502 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1503 if (!empty($conf->file->instance_unique_id)) {
1504 return sha1('dolibarr'.$conf->file->instance_unique_id);
1505 }
1506
1507 // For backward compatibility when instance_unique_id is not set
1508 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1509 }
1510
1511 // If prefix is for session (no need to have $conf loaded)
1512 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1513 $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
1514
1515 // The recommended value (may be not defined for old versions)
1516 if (!empty($tmp_instance_unique_id)) {
1517 return sha1('dolibarr'.$tmp_instance_unique_id);
1518 }
1519
1520 // For backward compatibility when instance_unique_id is not set
1521 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1522 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1523 } else {
1524 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1525 }
1526 }
1527}
1528
1539function dol_include_once($relpath, $classname = '')
1540{
1541 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']
1542
1543 $fullpath = dol_buildpath($relpath);
1544
1545 if (!file_exists($fullpath)) {
1546 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1547 return false;
1548 }
1549 if (!empty($classname) && !class_exists($classname)) {
1550 return include $fullpath;
1551 } else {
1552 return include_once $fullpath;
1553 }
1554}
1555
1556
1570function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1571{
1572 global $conf;
1573
1574 $path = preg_replace('/^\//', '', $path);
1575
1576 if (empty($type)) { // For a filesystem path
1577 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1578 if (is_array($conf->file->dol_document_root)) {
1579 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1580 if ($key == 'main') {
1581 continue;
1582 }
1583 // if (@file_exists($dirroot.'/'.$path)) {
1584 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1585 $res = $dirroot.'/'.$path;
1586 return $res;
1587 }
1588 }
1589 }
1590 if ($returnemptyifnotfound) {
1591 // Not found into alternate dir
1592 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1593 return '';
1594 }
1595 }
1596 } else {
1597 // For an url path
1598 // We try to get local path of file on filesystem from url
1599 // Note that trying to know if a file on disk exist by forging path on disk from url
1600 // works only for some web server and some setup. This is bugged when
1601 // using proxy, rewriting, virtual path, etc...
1602 $res = '';
1603 if ($type == 1) {
1604 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1605 }
1606 if ($type == 2) {
1607 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1608 }
1609 if ($type == 3) {
1610 $res = DOL_URL_ROOT.'/'.$path;
1611 }
1612
1613 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1614 if ($key == 'main') {
1615 if ($type == 3) {
1616 /*global $dolibarr_main_url_root;*/
1617
1618 // Define $urlwithroot
1619 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1620 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1621 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1622
1623 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1624 }
1625 continue;
1626 }
1627 $regs = array();
1628 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1629 if (!empty($regs[1])) {
1630 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1631 //if (file_exists($dirroot.'/'.$regs[1])) {
1632 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1633 if ($type == 1) {
1634 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1635 } elseif ($type == 2) {
1636 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1637 } elseif ($type == 3) {
1638 /*global $dolibarr_main_url_root;*/
1639
1640 // Define $urlwithroot
1641 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1642 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1643 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1644
1645 $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
1646 }
1647 break;
1648 }
1649 }
1650 }
1651 }
1652
1653 return $res;
1654}
1655
1666function dol_get_object_properties($obj, $properties = [])
1667{
1668 // Get real properties using get_object_vars() if $properties is empty
1669 if (empty($properties)) {
1670 return get_object_vars($obj);
1671 }
1672
1673 $existingProperties = [];
1674 $realProperties = get_object_vars($obj);
1675
1676 // Get the real or magic property values
1677 foreach ($properties as $property) {
1678 if (array_key_exists($property, $realProperties)) {
1679 // Real property, add the value
1680 $existingProperties[$property] = $obj->{$property};
1681 } elseif (property_exists($obj, $property)) {
1682 // Magic property
1683 $existingProperties[$property] = $obj->{$property};
1684 }
1685 }
1686
1687 return $existingProperties;
1688}
1689
1690
1706function dol_clone($object, $native = 2)
1707{
1708 if ($native == 0) {
1709 // deprecated method, use the method with native = 2 instead
1710 $tmpsavdb = null;
1711 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1712 $tmpsavdb = $object->db;
1713 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1714 }
1715
1716 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1717
1718 if (!empty($tmpsavdb)) {
1719 $object->db = $tmpsavdb;
1720 }
1721 } elseif ($native == 2) {
1722 // recommended method to have a full isolated cloned object
1723 $myclone = new stdClass();
1724 $tmparray = get_object_vars($object); // return only public properties
1725
1726 if (is_array($tmparray)) {
1727 foreach ($tmparray as $propertykey => $propertyval) {
1728 if (is_scalar($propertyval) || is_array($propertyval)) {
1729 $myclone->$propertykey = $propertyval;
1730 }
1731 }
1732 }
1733 } else {
1734 $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)
1735 }
1736
1737 return $myclone;
1738}
1739
1749function dol_size($size, $type = '')
1750{
1751 global $conf;
1752 if (empty($conf->dol_optimize_smallscreen)) {
1753 return $size;
1754 }
1755 if ($type == 'width' && $size > 250) {
1756 return 250;
1757 } else {
1758 return 10;
1759 }
1760}
1761
1762
1775function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0)
1776{
1777 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1778 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1779 // Char '/' and '\' are file delimiters.
1780 // 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
1781 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1782 if ($includequotes) {
1783 $filesystem_forbidden_chars[] = "'";
1784 }
1785 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1786 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1787 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1788 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1789 $tmp = str_replace('..', '', $tmp);
1790 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1791
1792 return $tmp;
1793}
1794
1795
1807function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1808{
1809 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1810 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1811 // 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
1812 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1813
1814 $tmp = $str;
1815 if ($unaccent) {
1816 $tmp = dol_string_unaccent($tmp);
1817 }
1818 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1819 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1820 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1821 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1822 $tmp = str_replace('..', '', $tmp);
1823 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1824
1825 return $tmp;
1826}
1827
1835function dol_sanitizeUrl($stringtoclean, $type = 1)
1836{
1837 // 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)
1838 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1839 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1840 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1841 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1842
1843 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1844 if ($type == 1) {
1845 // removing : should disable links to external url like http:aaa)
1846 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1847 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1848 }
1849
1850 do {
1851 $oldstringtoclean = $stringtoclean;
1852 // removing '&colon' should disable links to external url like http:aaa)
1853 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1854 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1855 } while ($oldstringtoclean != $stringtoclean);
1856
1857 if ($type == 1) {
1858 // removing '//' should disable links to external url like //aaa or http//)
1859 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1860 }
1861
1862 return $stringtoclean;
1863}
1864
1871function dol_sanitizeEmail($stringtoclean)
1872{
1873 do {
1874 $oldstringtoclean = $stringtoclean;
1875 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1876 } while ($oldstringtoclean != $stringtoclean);
1877
1878 return $stringtoclean;
1879}
1880
1889function dol_sanitizeKeyCode($str)
1890{
1891 return preg_replace('/[^\w]+/', '', $str);
1892}
1893
1894
1903function dol_string_unaccent($str)
1904{
1905 if (is_null($str)) {
1906 return '';
1907 }
1908
1909 if (utf8_check($str)) {
1910 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1911 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1912 return $transliterator->transliterate($str);
1913 }
1914 // See http://www.utf8-chartable.de/
1915 $string = rawurlencode($str);
1916 $replacements = array(
1917 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1918 '%C3%87' => 'C',
1919 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1920 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1921 '%C3%91' => 'N',
1922 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', '%C5%90' => 'O',
1923 '%C5%A0' => 'S',
1924 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', '%C5%B0' => 'U',
1925 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1926 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1927 '%C3%A7' => 'c',
1928 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1929 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1930 '%C3%B1' => 'n',
1931 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', '%C5%91' => 'o',
1932 '%C5%A1' => 's',
1933 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', '%C5%B1' => 'u',
1934 '%C3%BD' => 'y', '%C3%BF' => 'y'
1935 );
1936 $string = strtr($string, $replacements);
1937 return rawurldecode($string);
1938 } else {
1939 // See http://www.ascii-code.com/
1940 $string = strtr(
1941 $str,
1942 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1943 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1944 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1945 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1946 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1947 \xF9\xFA\xFB\xFC\xFD\xFF",
1948 "AAAAAAC
1949 EEEEIIIIDN
1950 OOOOOUUUY
1951 aaaaaaceeee
1952 iiiidnooooo
1953 uuuuyy"
1954 );
1955 $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"));
1956 return $string;
1957 }
1958}
1959
1973function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1974{
1975 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1976 if (empty($keepspaces)) {
1977 $forbidden_chars_to_replace[] = " ";
1978 }
1979 $forbidden_chars_to_remove = array();
1980 //$forbidden_chars_to_remove=array("(",")");
1981
1982 if (is_array($badcharstoreplace)) {
1983 $forbidden_chars_to_replace = $badcharstoreplace;
1984 }
1985 if (is_array($badcharstoremove)) {
1986 $forbidden_chars_to_remove = $badcharstoremove;
1987 }
1988
1989 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1990 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1991}
1992
1993
2007function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2008{
2009 if ($removetabcrlf) {
2010 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2011 } else {
2012 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
2013 }
2014}
2015
2022function dolSlugify($stringtoslugify)
2023{
2024 $slug = dol_string_unaccent($stringtoslugify);
2025
2026 // Convert special characters to their ASCII equivalents
2027 if (function_exists('iconv')) {
2028 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2029 }
2030
2031 // Convert to lowercase
2032 $slug = strtolower($slug);
2033
2034 // Replace non-alphanumeric characters with hyphens
2035 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2036
2037 // Remove leading and trailing hyphens
2038 $slug = trim($slug, '-');
2039
2040 return $slug;
2041}
2042
2051function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2052{
2053 if (is_null($stringtoescape)) {
2054 return '';
2055 }
2056
2057 // escape quotes and backslashes, newlines, etc.
2058 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2059 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2060 if (empty($noescapebackslashn)) {
2061 $substitjs["\n"] = '\\n';
2062 $substitjs['\\'] = '\\\\';
2063 }
2064 if (empty($mode)) {
2065 $substitjs["'"] = "\\'";
2066 $substitjs['"'] = "\\'";
2067 } elseif ($mode == 1) {
2068 $substitjs["'"] = "\\'";
2069 } elseif ($mode == 2) {
2070 $substitjs['"'] = '\\"';
2071 } elseif ($mode == 3) {
2072 $substitjs["'"] = "\\'";
2073 $substitjs['"'] = "\\\"";
2074 }
2075 return strtr((string) $stringtoescape, $substitjs);
2076}
2077
2087function dol_escape_uri($stringtoescape)
2088{
2089 return rawurlencode($stringtoescape);
2090}
2091
2098function dol_escape_json($stringtoescape)
2099{
2100 return str_replace('"', '\"', $stringtoescape);
2101}
2102
2110function dol_escape_php($stringtoescape, $stringforquotes = 2)
2111{
2112 if (is_null($stringtoescape)) {
2113 return '';
2114 }
2115
2116 if ($stringforquotes == 2) {
2117 return str_replace('"', "'", $stringtoescape);
2118 } elseif ($stringforquotes == 1) {
2119 // We remove the \ char.
2120 // If we allow the \ char, we can have $stringtoescape =
2121 // abc\';phpcodedanger; so the escapement will become
2122 // abc\\';phpcodedanger; and injecting this into
2123 // $a='...' will give $ac='abc\\';phpcodedanger;
2124 $stringtoescape = str_replace('\\', '', $stringtoescape);
2125 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2126 }
2127
2128 return 'Bad parameter for stringforquotes in dol_escape_php';
2129}
2130
2137function dol_escape_all($stringtoescape)
2138{
2139 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2140}
2141
2148function dol_escape_xml($stringtoescape)
2149{
2150 return $stringtoescape;
2151}
2152
2162function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2163{
2164 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2165}
2166
2175function dolPrintText($s)
2176{
2177 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2178}
2179
2190function dolPrintHTML($s, $allowiframe = 0)
2191{
2192 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2193 //$isAlreadyHTML = dol_textishtml($s);
2194
2195 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2196 // encode only special char like é but not &, <, >, ", ' if already HTML.
2197 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2198
2199 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2200}
2201
2212function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2213{
2214 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2215 if (!empty($allowothertags) && is_array($allowothertags)) {
2216 $allowedtags = array_merge($allowedtags, $allowothertags);
2217 }
2218 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2219 // The dol_escape_htmltag will escape html tags.
2220 if ($escapeonlyhtmltags) {
2221 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2222 } else {
2223 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2224 }
2225}
2226
2235function dolPrintHTMLForAttributeUrl($s)
2236{
2237 // 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)
2238 // The dol_escape_htmltag will escape html chars.
2239 $escapeonlyhtmltags = 1;
2240 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2241}
2242
2252function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2253{
2254 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2255}
2256
2263function dolPrintPassword($s)
2264{
2265 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2266}
2267
2268
2285function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2286{
2287 if ($noescapetags == 'common') {
2288 $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';
2289 // Add also html5 tags
2290 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2291 }
2292 if ($cleanalsojavascript) {
2293 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2294 }
2295
2296 // escape quotes and backslashes, newlines, etc.
2297 if ($escapeonlyhtmltags) {
2298 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2299 } else {
2300 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2301 // Because content can be or not already HTML.
2302 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2303 // In a future, we should not need this
2304
2305 $tmp = (string) $stringtoescape;
2306
2307 // We protect the 6 special entities that we don't want to decode.
2308 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2309 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2310 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2311 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2312 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2313 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2314
2315 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2316
2317 // We restore the 6 special entities that we don't want to have been decoded by previous command
2318 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2319 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2320 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2321 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2322 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2323 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2324
2325 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2326 }
2327 if (!$keepb) {
2328 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2329 }
2330 if (!$keepn) {
2331 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2332 } elseif ($keepn == -1) {
2333 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2334 }
2335
2336 if ($escapeonlyhtmltags) {
2337 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2338 return $tmp;
2339 } else {
2340 // Now we protect all the tags we want to keep
2341 $tmparrayoftags = array();
2342 if ($noescapetags) {
2343 $tmparrayoftags = explode(',', $noescapetags);
2344 }
2345
2346 if (count($tmparrayoftags)) {
2347 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2348
2349 $reg = array();
2350 // Remove reserved keywords. They are forbidden in a source string
2351 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2352
2353 foreach ($tmparrayoftags as $tagtoreplace) {
2354 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2355 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2356 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2357 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2358
2359 // For case of tag with attributes
2360 do {
2361 $tmpold = $tmp;
2362
2363 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2364 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2365 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2366 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2367 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2368 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2369 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2370 }
2371
2372 $diff = strcmp($tmpold, $tmp);
2373 } while ($diff);
2374 }
2375
2376 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2377 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2378 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2379 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2380 }
2381
2382 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2383 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2384
2385 //print $result;
2386
2387 if (count($tmparrayoftags)) {
2388 // Restore protected tags
2389 foreach ($tmparrayoftags as $tagtoreplace) {
2390 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2391 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2392 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2393 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2394 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2395 }
2396
2397 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2398
2399 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2400 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2401 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2402 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2403 }
2404
2405 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2406
2407 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2408
2409 return $result;
2410 }
2411}
2412
2420function dol_strtolower($string, $encoding = "UTF-8")
2421{
2422 if (function_exists('mb_strtolower')) {
2423 return mb_strtolower($string, $encoding);
2424 } else {
2425 return strtolower($string);
2426 }
2427}
2428
2437function dol_strtoupper($string, $encoding = "UTF-8")
2438{
2439 if (function_exists('mb_strtoupper')) {
2440 return mb_strtoupper($string, $encoding);
2441 } else {
2442 return strtoupper($string);
2443 }
2444}
2445
2454function dol_ucfirst($string, $encoding = "UTF-8")
2455{
2456 if (function_exists('mb_substr')) {
2457 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2458 } else {
2459 return ucfirst($string);
2460 }
2461}
2462
2471function dol_ucwords($string, $encoding = "UTF-8")
2472{
2473 if (function_exists('mb_convert_case')) {
2474 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2475 } else {
2476 return ucwords($string);
2477 }
2478}
2479
2480
2486function getCallerInfoString()
2487{
2488 $backtrace = debug_backtrace();
2489 $msg = "";
2490 if (count($backtrace) >= 1) {
2491 $pos = 1;
2492 if (count($backtrace) == 1) {
2493 $pos = 0;
2494 }
2495 $trace = $backtrace[$pos];
2496 if (isset($trace['file'], $trace['line'])) {
2497 $msg = " From {$trace['file']}:{$trace['line']}.";
2498 }
2499 }
2500 return $msg;
2501}
2502
2525function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2526{
2527 global $conf, $user, $debugbar;
2528
2529 // If syslog module enabled
2530 if (!isModEnabled('syslog')) {
2531 return;
2532 }
2533
2534 // Check if we are into execution of code of a website
2535 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2536 global $website, $websitekey;
2537 if (is_object($website) && !empty($website->ref)) {
2538 $suffixinfilename .= '_website_'.$website->ref;
2539 } elseif (!empty($websitekey)) {
2540 $suffixinfilename .= '_website_'.$websitekey;
2541 }
2542 }
2543
2544 // Check if we have a forced suffix
2545 if (defined('USESUFFIXINLOG')) {
2546 $suffixinfilename .= constant('USESUFFIXINLOG');
2547 }
2548
2549 if ($ident < 0) {
2550 foreach ($conf->loghandlers as $loghandlerinstance) {
2551 $loghandlerinstance->setIdent($ident);
2552 }
2553 }
2554
2555 if (!empty($message)) {
2556 // Test log level
2557 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2558 $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');
2559
2560 if (!array_key_exists($level, $logLevels)) {
2561 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2562 $level = LOG_ERR;
2563 }
2564 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2565 return;
2566 }
2567
2568 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2569 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2570 }
2571
2572 // If adding log inside HTML page is required
2573 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2574 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2575 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2576 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2577
2578 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2579 }
2580
2581 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2582 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2583 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2584 print "\n\n<!-- Log start\n";
2585 print dol_escape_htmltag($message)."\n";
2586 print "Log end -->\n";
2587 }
2588
2589 $data = array(
2590 'message' => $message,
2591 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2592 'level' => $level,
2593 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2594 'ip' => '',
2595 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2596 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2597 );
2598
2599 // For log, we want the reliable IP first.
2600 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2601 if (!empty($remoteip)) {
2602 $data['ip'] = $remoteip;
2603 // This is when server run behind a reverse proxy
2604 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2605 // $data['ip'] is last
2606 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2607 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2608 $data['ip'] = '';
2609 $foundremoteip = 0;
2610 $j = 0;
2611 foreach ($tmpips as $tmpip) {
2612 $tmpip = trim($tmpip);
2613 if (strtolower($tmpip) == strtolower($remoteip)) {
2614 $foundremoteip = 1;
2615 }
2616 if (empty($data['ip'])) {
2617 $data['ip'] = $tmpip;
2618 } else {
2619 $j++;
2620 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2621 }
2622 }
2623 if (!$foundremoteip) {
2624 $j++;
2625 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2626 }
2627 $data['ip'] .= (($j > 0) ? ']' : '');
2628 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2629 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2630 $data['ip'] = '';
2631 $foundremoteip = 0;
2632 $j = 0;
2633 foreach ($tmpips as $tmpip) {
2634 $tmpip = trim($tmpip);
2635 if (strtolower($tmpip) == strtolower($remoteip)) {
2636 $foundremoteip = 1;
2637 }
2638 if (empty($data['ip'])) {
2639 $data['ip'] = $tmpip;
2640 } else {
2641 $j++;
2642 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2643 }
2644 }
2645 if (!$foundremoteip) {
2646 $j++;
2647 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2648 }
2649 $data['ip'] .= (($j > 0) ? ']' : '');
2650 }
2651 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2652 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2653 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2654 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2655 // 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).
2656 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2657 } else {
2658 $data['ip'] = '???';
2659 }
2660
2661 if (!empty($_SERVER['USERNAME'])) {
2662 // 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).
2663 $data['osuser'] = (string) $_SERVER['USERNAME'];
2664 } elseif (!empty($_SERVER['LOGNAME'])) {
2665 // 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).
2666 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2667 }
2668
2669 // Loop on each log handler and send output
2670 foreach ($conf->loghandlers as $loghandlerinstance) {
2671 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2672 continue;
2673 }
2674 $loghandlerinstance->export($data, $suffixinfilename);
2675 }
2676 unset($data);
2677 }
2678
2679 if ($ident > 0) {
2680 foreach ($conf->loghandlers as $loghandlerinstance) {
2681 $loghandlerinstance->setIdent($ident);
2682 }
2683 }
2684}
2685
2697function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2698{
2699 global $langs, $db;
2700
2701 $form = new Form($db);
2702
2703 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2704 if (empty($templatenameforexport)) {
2705 $templatenameforexport = 'website_'.$website->ref;
2706 }
2707
2708 $out = '';
2709 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2710
2711 // for generate popup
2712 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2713 $out .= 'jQuery(document).ready(function () {';
2714 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2715 $out .= ' var dialogHtml = \'';
2716
2717 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2718 $dialogcontent .= ' <div style="margin-top: 20px;">';
2719 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2720 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2721 $dialogcontent .= ' </div>';
2722 $dialogcontent .= ' <br>';
2723 $dialogcontent .= ' <div style="margin-top: 20px;">';
2724 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2725 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2726 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2727 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2728 $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>';
2729 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2730 $dialogcontent .= ' </form>';
2731 $dialogcontent .= ' </div>';
2732 $dialogcontent .= ' </div>';
2733
2734 $out .= dol_escape_js($dialogcontent);
2735
2736 $out .= '\';';
2737
2738
2739 // Add the content of the dialog to the body of the page
2740 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2741 $out .= ' if ($dialog.length > 0) {
2742 $dialog.remove();
2743 }
2744 jQuery("body").append(dialogHtml);';
2745
2746 // Configuration of popup
2747 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2748 $out .= ' autoOpen: false,';
2749 $out .= ' modal: true,';
2750 $out .= ' height: 290,';
2751 $out .= ' width: "40%",';
2752 $out .= ' title: "' . dol_escape_js($label) . '",';
2753 $out .= ' });';
2754
2755 // Simulate a click on the original "submit" input to export the site.
2756 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2757 $out .= ' console.log("Clic on exportsite.");';
2758 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2759 $out .= ' console.log("element founded:", target.length > 0);';
2760 $out .= ' if (target.length > 0) { target.click(); }';
2761 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2762 $out .= ' });';
2763
2764 // open popup
2765 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2766 $out .= ' return false;';
2767 $out .= ' });';
2768 $out .= '});';
2769 $out .= '</script>';
2770
2771 return $out;
2772}
2773
2774
2791function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
2792{
2793 global $conf;
2794
2795 if (strpos($url, '?') > 0) {
2796 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2797 } else {
2798 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2799 }
2800
2801 $out = '';
2802
2803 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2804 $out .= '<!-- a link for button to open url into a dialog popup -->';
2805 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2806 if (empty($conf->use_javascript_ajax)) {
2807 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2808 } elseif ($jsonopen) {
2809 $out .= ' href="#" onclick="'.$jsonopen.'"';
2810 } else {
2811 $out .= ' href="#"';
2812 }
2813 $out .= '>'.$buttonstring.'</a>';
2814
2815 if (!empty($conf->use_javascript_ajax)) {
2816 // Add code to open url using the popup.
2817 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2818 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2819
2820 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2821 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2822 jQuery(document).ready(function () {
2823 jQuery(".button_'.$name.'").click(function () {
2824 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2825 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2826 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2827 $tmpdialog.dialog({
2828 autoOpen: false,
2829 modal: true,
2830 height: (window.innerHeight - 150),
2831 width: \'80%\',
2832 title: \''.dol_escape_js($label).'\',
2833 open: function (event, ui) {
2834 console.log("open popup name='.$name.'");
2835 },
2836 close: function (event, ui) {
2837 console.log("Popup is closed, run jsonclose = '.$jsonclose.'");
2838 '.(empty($jsonclose) ? '' : $jsonclose.';').'
2839 }
2840 });
2841
2842 $tmpdialog.dialog(\'open\');
2843 return false;
2844 });
2845 });
2846 </script>';
2847 }
2848 return $out;
2849}
2850
2867function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2868{
2869 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2870}
2871
2889function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
2890{
2891 global $conf, $langs, $hookmanager;
2892
2893 // Show title
2894 $showtitle = 1;
2895 if (!empty($conf->dol_optimize_smallscreen)) {
2896 $showtitle = 0;
2897 }
2898
2899 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2900
2901 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2902 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2903 }
2904
2905 // Show right part
2906 if ($morehtmlright) {
2907 $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.
2908 }
2909
2910 // Show tabs
2911
2912 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2913 $maxkey = -1;
2914 if (is_array($links) && !empty($links)) {
2915 $keys = array_keys($links);
2916 if (count($keys)) {
2917 $maxkey = max($keys);
2918 }
2919 }
2920
2921 // Show tabs
2922 // if =0 we don't use the feature
2923 if (empty($limittoshow)) {
2924 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2925 }
2926 if (!empty($conf->dol_optimize_smallscreen)) {
2927 $limittoshow = 2;
2928 }
2929
2930 $displaytab = 0;
2931 $nbintab = 0;
2932 $popuptab = 0;
2933 $outmore = '';
2934 for ($i = 0; $i <= $maxkey; $i++) {
2935 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2936 // If active tab is already present
2937 if ($i >= $limittoshow) {
2938 $limittoshow--;
2939 }
2940 }
2941 }
2942
2943 for ($i = 0; $i <= $maxkey; $i++) {
2944 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2945 $isactive = true;
2946 } else {
2947 $isactive = false;
2948 }
2949
2950 if ($i < $limittoshow || $isactive) {
2951 // Output entry with a visible tab
2952 $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])).' -->';
2953
2954 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2955 if (!empty($links[$i][0])) {
2956 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2957 } else {
2958 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2959 }
2960 } elseif (!empty($links[$i][1])) {
2961 //print "x $i $active ".$links[$i][2]." z";
2962 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2963
2964 if (!empty($links[$i][0])) {
2965 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2966 $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).'">';
2967 }
2968
2969 if ($displaytab == 0 && $picto) {
2970 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2971 }
2972
2973 $out .= $links[$i][1];
2974 if (!empty($links[$i][0])) {
2975 $out .= '</a>'."\n";
2976 }
2977 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2978 $out .= '</div>';
2979 }
2980
2981 $out .= '</div>';
2982 } else {
2983 // Add entry into the combo popup with the other tabs
2984 if (!$popuptab) {
2985 $popuptab = 1;
2986 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2987 }
2988 $outmore_content = '';
2989
2990 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2991 if (!empty($links[$i][0])) {
2992 $outmore_content .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2993 } else {
2994 $outmore_content .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2995 }
2996 } elseif (!empty($links[$i][1])) {
2997 $outmore_content .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2998 $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.
2999 $outmore_content .= '</a>'."\n";
3000 }
3001 if ($outmore_content !== '') {
3002 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
3003 }
3004
3005 $nbintab++;
3006 }
3007
3008 $displaytab = $i + 1;
3009 }
3010 if ($popuptab) {
3011 $outmore .= '</div>';
3012 }
3013
3014 if ($popuptab) { // If there is some tabs not shown
3015 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3016 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3017 $widthofpopup = 240;
3018
3019 $tabsname = $moretabssuffix;
3020 if (empty($tabsname)) {
3021 $tabsname = str_replace("@", "", $picto);
3022 }
3023 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
3024 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3025 $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".
3026 }
3027 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
3028 $out .= $outmore;
3029 $out .= '</div>';
3030 $out .= '<div></div>';
3031 $out .= "</div>\n";
3032
3033 $out .= '<script nonce="'.getNonce().'">';
3034 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
3035 var x = this.offsetLeft, y = this.offsetTop;
3036 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3037 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
3038 $('#moretabsList".$tabsname."').css('".$right."','8px');
3039 }
3040 $('#moretabsList".$tabsname."').css('".$left."','auto');
3041 });
3042 ";
3043 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
3044 $out .= "</script>";
3045 }
3046
3047 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3048 $out .= "</div>\n";
3049 }
3050
3051 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3052 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
3053 $out .= ($morecssdiv ? ' '.$morecssdiv : '');
3054 $out .= '">'."\n";
3055 }
3056 if (!empty($dragdropfile)) {
3057 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3058 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3059 }
3060 $parameters = array('tabname' => $active, 'out' => $out);
3061 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3062 if ($reshook > 0) {
3063 $out = $hookmanager->resPrint;
3064 }
3065
3066 return $out;
3067}
3068
3076function dol_fiche_end($notab = 0)
3077{
3078 print dol_get_fiche_end($notab);
3079}
3080
3087function dol_get_fiche_end($notab = 0)
3088{
3089 if (!$notab || $notab == -1) {
3090 return "\n</div>\n";
3091 } else {
3092 return '';
3093 }
3094}
3095
3115function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3116{
3117 global $conf, $form, $user, $langs, $hookmanager, $action;
3118
3119 $error = 0;
3120
3121 $maxvisiblephotos = 1;
3122 $showimage = 1;
3123 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3124 // @phan-suppress-next-line PhanUndeclaredMethod
3125 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3126 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3127 $showbarcode = 0;
3128 }
3129 $modulepart = 'unknown';
3130
3131 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3132 $modulepart = $object->element;
3133 } elseif ($object->element == 'member') {
3134 $modulepart = 'memberphoto';
3135 } elseif ($object->element == 'user') {
3136 $modulepart = 'userphoto';
3137 }
3138
3139 if (class_exists("Imagick")) {
3140 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3141 $modulepart = $object->element;
3142 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3143 $modulepart = 'ficheinter';
3144 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3145 $modulepart = 'contract';
3146 } elseif ($object->element == 'order_supplier') {
3147 $modulepart = 'supplier_order';
3148 } elseif ($object->element == 'invoice_supplier') {
3149 $modulepart = 'supplier_invoice';
3150 }
3151 }
3152
3153 if ($object->element == 'product') {
3155 '@phan-var-force Product $object';
3156 $width = 80;
3157 $cssclass = 'photowithmargin photoref';
3158 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3159 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3160 if ($conf->browser->layout == 'phone') {
3161 $maxvisiblephotos = 1;
3162 }
3163 if ($showimage) {
3164 $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>';
3165 } else {
3166 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3167 $nophoto = '';
3168 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3169 } else { // Show no photo link
3170 $nophoto = '/public/theme/common/nophoto.png';
3171 $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>';
3172 }
3173 }
3174 } elseif ($object->element == 'category') {
3176 '@phan-var-force Categorie $object';
3177 $width = 80;
3178 $cssclass = 'photowithmargin photoref';
3179 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3180 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3181 if ($conf->browser->layout == 'phone') {
3182 $maxvisiblephotos = 1;
3183 }
3184 if ($showimage) {
3185 $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>';
3186 } else {
3187 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3188 $nophoto = '';
3189 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3190 } else { // Show no photo link
3191 $nophoto = '/public/theme/common/nophoto.png';
3192 $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>';
3193 }
3194 }
3195 } elseif ($object->element == 'bom') {
3197 '@phan-var-force Bom $object';
3198 $width = 80;
3199 $cssclass = 'photowithmargin photoref';
3200 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3201 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3202 if ($conf->browser->layout == 'phone') {
3203 $maxvisiblephotos = 1;
3204 }
3205 if ($showimage) {
3206 $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>';
3207 } else {
3208 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3209 $nophoto = '';
3210 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3211 } else { // Show no photo link
3212 $nophoto = '/public/theme/common/nophoto.png';
3213 $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>';
3214 }
3215 }
3216 } elseif ($object->element == 'ticket') {
3217 $width = 80;
3218 $cssclass = 'photoref';
3220 '@phan-var-force Ticket $object';
3221 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
3222 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3223 if ($conf->browser->layout == 'phone') {
3224 $maxvisiblephotos = 1;
3225 }
3226
3227 if ($showimage) {
3228 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3229 if ($object->nbphoto > 0) {
3230 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
3231 } else {
3232 $showimage = 0;
3233 }
3234 }
3235 if (!$showimage) {
3236 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3237 $nophoto = '';
3238 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3239 } else { // Show no photo link
3240 $nophoto = img_picto('No photo', 'object_ticket');
3241 $morehtmlleft .= '<!-- No photo to show -->';
3242 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3243 $morehtmlleft .= $nophoto;
3244 $morehtmlleft .= '</div></div>';
3245 }
3246 }
3247 } else {
3248 if ($showimage) {
3249 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3250 $phototoshow = '';
3251 // Check if a preview file is available
3252 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3253 $objectref = dol_sanitizeFileName($object->ref);
3254 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
3255 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3256 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3257 $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
3258 } else {
3259 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3260 }
3261 if (empty($subdir)) {
3262 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3263 }
3264
3265 $filepath = $dir_output.$subdir."/";
3266
3267 $filepdf = $filepath.$objectref.".pdf";
3268 $relativepath = $subdir.'/'.$objectref.'.pdf';
3269
3270 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3271 $fileimage = $filepdf.'_preview.png';
3272 $relativepathimage = $relativepath.'_preview.png';
3273
3274 $pdfexists = file_exists($filepdf);
3275
3276 // If PDF file exists
3277 if ($pdfexists) {
3278 // Conversion du PDF en image png si fichier png non existent
3279 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3280 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3281 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3282 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3283 if ($ret < 0) {
3284 $error++;
3285 }
3286 }
3287 }
3288 }
3289
3290 if ($pdfexists && !$error) {
3291 $heightforphotref = 80;
3292 if (!empty($conf->dol_optimize_smallscreen)) {
3293 $heightforphotref = 60;
3294 }
3295 // If the preview file is found
3296 if (file_exists($fileimage)) {
3297 $phototoshow = '<div class="photoref">';
3298 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3299 $phototoshow .= '</div>';
3300 }
3301 }
3302 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3303 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3304 }
3305
3306 if ($phototoshow) {
3307 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3308 $morehtmlleft .= $phototoshow;
3309 $morehtmlleft .= '</div>';
3310 }
3311 }
3312
3313 if (empty($phototoshow)) { // Show No photo link (picto of object)
3314 if ($object->element == 'action') {
3315 $width = 80;
3316 $cssclass = 'photorefcenter';
3317 $nophoto = img_picto('No photo', 'title_agenda');
3318 } else {
3319 $width = 14;
3320 $cssclass = 'photorefcenter';
3321 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3322 $prefix = 'object_';
3323 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3324 $picto = 'project'; // instead of projectpub
3325 }
3326 if (strpos($picto, 'fontawesome_') !== false) {
3327 $prefix = '';
3328 }
3329 $nophoto = img_picto('No photo', $prefix.$picto);
3330 }
3331 $morehtmlleft .= '<!-- No photo to show -->';
3332 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3333 $morehtmlleft .= $nophoto;
3334 $morehtmlleft .= '</div></div>';
3335 }
3336 }
3337 }
3338
3339 // Show barcode
3340 if ($showbarcode) {
3341 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3342 }
3343
3344 if ($object->element == 'societe') {
3345 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3346 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3347 } else {
3348 $morehtmlstatus .= $object->getLibStatut(6);
3349 }
3350 } elseif ($object->element == 'product') {
3351 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3352 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3353 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3354 } else {
3355 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3356 }
3357 $morehtmlstatus .= ' &nbsp; ';
3358 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3359 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3360 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3361 } else {
3362 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3363 }
3364 } elseif (in_array($object->element, array('salary'))) {
3365 '@phan-var-force Salary $object';
3366 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3367 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3368 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3369 }
3370 $morehtmlstatus .= $tmptxt;
3371 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3372 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3373 $totalallpayments = $object->getSommePaiement(0);
3374 $totalallpayments += $object->getSumCreditNotesUsed(0);
3375 $totalallpayments += $object->getSumDepositsUsed(0);
3376 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3377 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3378 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3379 }
3380 $morehtmlstatus .= $tmptxt;
3381 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3382 '@phan-var-force ChargeSociales|Loan|Tva $object';
3383 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3384 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3385 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3386 }
3387 $morehtmlstatus .= $tmptxt;
3388 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3389 if ($object->statut == 0) {
3390 $morehtmlstatus .= $object->getLibStatut(5);
3391 } else {
3392 $morehtmlstatus .= $object->getLibStatut(4);
3393 }
3394 } elseif ($object->element == 'facturerec') {
3395 '@phan-var-force FactureRec $object';
3396 if ($object->frequency == 0) {
3397 $morehtmlstatus .= $object->getLibStatut(2);
3398 } else {
3399 $morehtmlstatus .= $object->getLibStatut(5);
3400 }
3401 } elseif ($object->element == 'project_task') {
3402 $tmptxt = $object->getLibStatut(4);
3403 $morehtmlstatus .= $tmptxt;
3404 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3405 $tmptxt = $object->getLibStatut(6);
3406 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3407 $tmptxt = $object->getLibStatut(5);
3408 }
3409 $morehtmlstatus .= $tmptxt;
3410 }
3411
3412 // Say if object was dispatched/transferred "into accountancy"
3413 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3414 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3415 if (method_exists($object, 'getVentilExportCompta')) {
3416 $accounted = $object->getVentilExportCompta(1);
3417 $langs->load("accountancy");
3418 $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>';
3419 }
3420 }
3421
3422 // Add alias for thirdparty
3423 if (!empty($object->name_alias)) {
3424 '@phan-var-force Societe $object';
3425 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3426 }
3427
3428 // Add label
3429 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3430 if (!empty($object->label)) {
3431 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3432 }
3433 }
3434 // Show address and email
3435 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3436 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3437 if ($moreaddress) {
3438 $morehtmlref .= '<div class="refidno refaddress">';
3439 $morehtmlref .= $moreaddress;
3440 $morehtmlref .= '</div>';
3441 }
3442 }
3443 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)) {
3444 $morehtmlref .= '<div style="clear: both;"></div>';
3445 $morehtmlref .= '<div class="refidno opacitymedium">';
3446 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3447 $morehtmlref .= '</div>';
3448 }
3449
3450 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3451 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3452 if ($reshook < 0) {
3453 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3454 } elseif (empty($reshook)) {
3455 $morehtmlref .= $hookmanager->resPrint;
3456 } elseif ($reshook > 0) {
3457 $morehtmlref = $hookmanager->resPrint;
3458 }
3459
3460 // $morehtml is the right part (link "Back to list")
3461 // $morehtmlleft is the picto or photo of banner
3462 // $morehtmlstatus is part under the status
3463 // $morehtmlright is part of htmlright
3464
3465 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3466 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3467 print '</div>';
3468 print '<div class="underrefbanner clearboth"></div>';
3469}
3470
3480function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3481{
3482 global $langs;
3483 $ret = '';
3484 if ($fieldrequired) {
3485 $ret .= '<span class="fieldrequired">';
3486 }
3487 $ret .= '<label for="'.$fieldkey.'">';
3488 $ret .= $langs->trans($langkey);
3489 $ret .= '</label>';
3490 if ($fieldrequired) {
3491 $ret .= '</span>';
3492 }
3493 return $ret;
3494}
3495
3509function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3510{
3511 global $langs, $hookmanager;
3512
3513 $ret = '';
3514 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3515
3516 // See format of addresses on https://en.wikipedia.org/wiki/Address
3517 // Address
3518 if (empty($mode)) {
3519 $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)));
3520 }
3521 // Zip/Town/State
3522 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3523 // US: title firstname name \n address lines \n town, state, zip \n country
3524 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3525 $ret .= (($ret && $town) ? $sep : '').$town;
3526
3527 if (!empty($object->state)) {
3528 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3529 }
3530 if (!empty($object->zip)) {
3531 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3532 }
3533 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3534 // UK: title firstname name \n address lines \n town state \n zip \n country
3535 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3536 $ret .= ($ret ? $sep : '').$town;
3537 if (!empty($object->state)) {
3538 $ret .= ($ret ? ", " : '').$object->state;
3539 }
3540 if (!empty($object->zip)) {
3541 $ret .= ($ret ? $sep : '').$object->zip;
3542 }
3543 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3544 // ES: title firstname name \n address lines \n zip town \n state \n country
3545 $ret .= ($ret ? $sep : '').$object->zip;
3546 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3547 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3548 if (!empty($object->state)) {
3549 $ret .= $sep.$object->state;
3550 }
3551 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3552 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3553 // See https://www.sljfaq.org/afaq/addresses.html
3554 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3555 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3556 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3557 // IT: title firstname name\n address lines \n zip town state_code \n country
3558 $ret .= ($ret ? $sep : '').$object->zip;
3559 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3560 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3561 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3562 } else {
3563 // Other: title firstname name \n address lines \n zip town[, state] \n country
3564 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3565 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3566 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3567 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3568 $ret .= ($ret ? ", " : '').$object->state;
3569 }
3570 }
3571
3572 if (!is_object($outputlangs)) {
3573 $outputlangs = $langs;
3574 }
3575 if ($withcountry) {
3576 $langs->load("dict");
3577 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3578 }
3579 if ($hookmanager) {
3580 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3581 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3582 if ($reshook > 0) {
3583 $ret = '';
3584 }
3585 $ret .= $hookmanager->resPrint;
3586 }
3587
3588 return $ret;
3589}
3590
3591
3592
3602function dol_strftime($fmt, $ts = false, $is_gmt = false)
3603{
3604 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3605 return dol_print_date($ts, $fmt, $is_gmt);
3606 } else {
3607 return 'Error date outside supported range';
3608 }
3609}
3610
3632function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3633{
3634 global $conf, $langs;
3635
3636 // If date undefined or "", we return ""
3637 if (dol_strlen((string) $time) == 0) {
3638 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3639 }
3640
3641 if ($tzoutput === 'auto') {
3642 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3643 }
3644
3645 // Clean parameters
3646 $to_gmt = false;
3647 $offsettz = $offsetdst = 0;
3648 if ($tzoutput) {
3649 $to_gmt = true; // For backward compatibility
3650 if (is_string($tzoutput)) {
3651 if ($tzoutput == 'tzserver') {
3652 $to_gmt = false;
3653 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3654 // @phan-suppress-next-line PhanPluginRedundantAssignment
3655 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3656 // @phan-suppress-next-line PhanPluginRedundantAssignment
3657 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3658 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3659 $to_gmt = true;
3660 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3661
3662 if (class_exists('DateTimeZone')) {
3663 $user_date_tz = new DateTimeZone($offsettzstring);
3664 $user_dt = new DateTime();
3665 $user_dt->setTimezone($user_date_tz);
3666 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3667 $offsettz = $user_dt->getOffset(); // should include dst ?
3668 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3669 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3670 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3671 }
3672 }
3673 }
3674 }
3675 if (!is_object($outputlangs)) {
3676 $outputlangs = $langs;
3677 }
3678 if (!$format) {
3679 $format = 'daytextshort';
3680 }
3681
3682 // Do we have to reduce the length of date (year on 2 chars) to save space.
3683 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3684 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3685 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3686 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3687 if ($formatwithoutreduce != $format) {
3688 $format = $formatwithoutreduce;
3689 $reduceformat = 1;
3690 } // so format 'dayreduceformat' is processed like day
3691
3692 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3693 // TODO Add format daysmallyear and dayhoursmallyear
3694 if ($format == 'day') {
3695 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3696 } elseif ($format == 'hour') {
3697 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3698 } elseif ($format == 'hourduration') {
3699 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3700 } elseif ($format == 'daytext') {
3701 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3702 } elseif ($format == 'daytextshort') {
3703 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3704 } elseif ($format == 'dayhour') {
3705 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3706 } elseif ($format == 'dayhoursec') {
3707 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3708 } elseif ($format == 'dayhourtext') {
3709 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3710 } elseif ($format == 'dayhourtextshort') {
3711 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3712 } elseif ($format == 'dayhourlog') {
3713 // Format not sensitive to language
3714 $format = '%Y%m%d%H%M%S';
3715 } elseif ($format == 'dayhourlogsmall') {
3716 // Format not sensitive to language
3717 $format = '%y%m%d%H%M';
3718 } elseif ($format == 'dayhourldap') {
3719 $format = '%Y%m%d%H%M%SZ';
3720 } elseif ($format == 'dayhourxcard') {
3721 $format = '%Y%m%dT%H%M%SZ';
3722 } elseif ($format == 'dayxcard') {
3723 $format = '%Y%m%d';
3724 } elseif ($format == 'dayrfc') {
3725 $format = '%Y-%m-%d'; // DATE_RFC3339
3726 } elseif ($format == 'dayhourrfc') {
3727 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3728 } elseif ($format == 'standard') {
3729 $format = '%Y-%m-%d %H:%M:%S';
3730 }
3731
3732 if ($reduceformat) {
3733 $format = str_replace('%Y', '%y', $format);
3734 $format = str_replace('yyyy', 'yy', $format);
3735 }
3736
3737 // Clean format
3738 if (preg_match('/%b/i', $format)) { // There is some text to translate
3739 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3740 $format = str_replace('%b', '__b__', $format);
3741 $format = str_replace('%B', '__B__', $format);
3742 }
3743 if (preg_match('/%a/i', $format)) { // There is some text to translate
3744 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3745 $format = str_replace('%a', '__a__', $format);
3746 $format = str_replace('%A', '__A__', $format);
3747 }
3748
3749 // Analyze date
3750 $reg = array();
3751 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
3752 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"]));
3753 return '';
3754 } 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
3755 // This part of code should not be used anymore.
3756 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);
3757 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3758 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3759 $syear = (!empty($reg[1]) ? $reg[1] : '');
3760 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3761 $sday = (!empty($reg[3]) ? $reg[3] : '');
3762 $shour = (!empty($reg[4]) ? $reg[4] : '');
3763 $smin = (!empty($reg[5]) ? $reg[5] : '');
3764 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3765
3766 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3767
3768 if ($to_gmt) {
3769 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3770 } else {
3771 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3772 }
3773 $dtts = new DateTime();
3774 $dtts->setTimestamp($time);
3775 $dtts->setTimezone($tzo);
3776 $newformat = str_replace(
3777 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3778 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3779 $format
3780 );
3781 $ret = $dtts->format($newformat);
3782 $ret = str_replace(
3783 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3784 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3785 $ret
3786 );
3787 } else {
3788 // Date is a timestamps
3789 if ($time < 100000000000) { // Protection against bad date values
3790 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3791
3792 if ($to_gmt) {
3793 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3794 } else {
3795 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3796 }
3797 $dtts = new DateTime();
3798 $dtts->setTimestamp($timetouse);
3799 $dtts->setTimezone($tzo);
3800 $newformat = str_replace(
3801 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3802 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3803 $format
3804 );
3805 $ret = $dtts->format($newformat);
3806 $ret = str_replace(
3807 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3808 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3809 $ret
3810 );
3811 //var_dump($ret);exit;
3812 } else {
3813 $ret = 'Bad value '.$time.' for date';
3814 }
3815 }
3816
3817 if (preg_match('/__b__/i', $format)) {
3818 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3819
3820 if ($to_gmt) {
3821 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3822 } else {
3823 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3824 }
3825 $dtts = new DateTime();
3826 $dtts->setTimestamp($timetouse);
3827 $dtts->setTimezone($tzo);
3828 $month = (int) $dtts->format("m");
3829 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3830 if ($encodetooutput) {
3831 $monthtext = $outputlangs->transnoentities('Month'.$month);
3832 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3833 } else {
3834 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3835 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3836 }
3837 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3838 $ret = str_replace('__b__', $monthtextshort, $ret);
3839 $ret = str_replace('__B__', $monthtext, $ret);
3840 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3841 //return $ret;
3842 }
3843 if (preg_match('/__a__/i', $format)) {
3844 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3845 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3846
3847 if ($to_gmt) {
3848 $tzo = new DateTimeZone('UTC');
3849 } else {
3850 $tzo = new DateTimeZone(date_default_timezone_get());
3851 }
3852 $dtts = new DateTime();
3853 $dtts->setTimestamp($timetouse);
3854 $dtts->setTimezone($tzo);
3855 $w = $dtts->format("w");
3856 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3857
3858 $ret = str_replace('__A__', $dayweek, $ret);
3859 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3860 }
3861
3862 return $ret;
3863}
3864
3865
3886function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3887{
3888 if ($timestamp === '') {
3889 return array();
3890 }
3891
3892 $datetimeobj = new DateTime();
3893 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3894 if ($forcetimezone) {
3895 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3896 }
3897 $arrayinfo = array(
3898 'year' => ((int) date_format($datetimeobj, 'Y')),
3899 'mon' => ((int) date_format($datetimeobj, 'm')),
3900 'mday' => ((int) date_format($datetimeobj, 'd')),
3901 'wday' => ((int) date_format($datetimeobj, 'w')),
3902 'yday' => ((int) date_format($datetimeobj, 'z')),
3903 'hours' => ((int) date_format($datetimeobj, 'H')),
3904 'minutes' => ((int) date_format($datetimeobj, 'i')),
3905 'seconds' => ((int) date_format($datetimeobj, 's')),
3906 '0' => $timestamp
3907 );
3908
3909 return $arrayinfo;
3910}
3911
3933function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3934{
3935 global $conf;
3936 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3937
3938 if ($gm === 'auto') {
3939 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3940 }
3941 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3942
3943 // Clean parameters
3944 if ($hour == -1 || empty($hour)) {
3945 $hour = 0;
3946 }
3947 if ($minute == -1 || empty($minute)) {
3948 $minute = 0;
3949 }
3950 if ($second == -1 || empty($second)) {
3951 $second = 0;
3952 }
3953
3954 // Check parameters
3955 if ($check) {
3956 if (!$month || !$day) {
3957 return '';
3958 }
3959 if ($day > 31) {
3960 return '';
3961 }
3962 if ($month > 12) {
3963 return '';
3964 }
3965 if ($hour < 0 || $hour > 24) {
3966 return '';
3967 }
3968 if ($minute < 0 || $minute > 60) {
3969 return '';
3970 }
3971 if ($second < 0 || $second > 60) {
3972 return '';
3973 }
3974 }
3975
3976 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3977 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3978 $localtz = new DateTimeZone($default_timezone);
3979 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3980 // We use dol_tz_string first because it is more reliable.
3981 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3982 try {
3983 $localtz = new DateTimeZone($default_timezone);
3984 } catch (Exception $e) {
3985 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3986 $default_timezone = @date_default_timezone_get();
3987 }
3988 } elseif (strrpos($gm, "tz,") !== false) {
3989 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3990 try {
3991 $localtz = new DateTimeZone($timezone);
3992 } catch (Exception $e) {
3993 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3994 }
3995 }
3996
3997 if (empty($localtz)) {
3998 $localtz = new DateTimeZone('UTC');
3999 }
4000 //var_dump($localtz);
4001 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
4002 $dt = new DateTime('now', $localtz);
4003 $dt->setDate((int) $year, (int) $month, (int) $day);
4004 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4005 $date = $dt->getTimestamp(); // should include daylight saving time
4006 //var_dump($date);
4007 return $date;
4008}
4009
4010
4021function dol_now($mode = 'auto')
4022{
4023 $ret = 0;
4024
4025 if ($mode === 'auto') {
4026 $mode = 'gmt';
4027 }
4028
4029 if ($mode == 'gmt') {
4030 $ret = time(); // Time for now at greenwich.
4031 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4032 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4033 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4034 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4035 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4036 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4037 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4038 // $ret=dol_now('gmt')+($tzsecond*3600);
4039 //}
4040 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4041 // Time for now with user timezone added
4042 //print 'time: '.time();
4043 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4044 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4045 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4046 }
4047
4048 return $ret;
4049}
4050
4051
4060function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4061{
4062 global $conf, $langs;
4063 $level = 1024;
4064
4065 if (!empty($conf->dol_optimize_smallscreen)) {
4066 $shortunit = 1;
4067 }
4068
4069 // Set value text
4070 if (empty($shortvalue) || $size < ($level * 10)) {
4071 $ret = $size;
4072 $textunitshort = $langs->trans("b");
4073 $textunitlong = $langs->trans("Bytes");
4074 } else {
4075 $ret = round($size / $level, 0);
4076 $textunitshort = $langs->trans("Kb");
4077 $textunitlong = $langs->trans("KiloBytes");
4078 }
4079 // Use long or short text unit
4080 if (empty($shortunit)) {
4081 $ret .= ' '.$textunitlong;
4082 } else {
4083 $ret .= ' '.$textunitshort;
4084 }
4085
4086 return $ret;
4087}
4088
4099function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4100{
4101 global $langs;
4102
4103 if (empty($url)) {
4104 return '';
4105 }
4106
4107 $linkstart = '<a href="';
4108 if (!preg_match('/^http/i', $url)) {
4109 $linkstart .= 'http://';
4110 }
4111 $linkstart .= $url;
4112 $linkstart .= '"';
4113 if ($target) {
4114 $linkstart .= ' target="'.$target.'"';
4115 }
4116 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
4117 $linkstart .= '>';
4118
4119 $link = '';
4120 if (!preg_match('/^http/i', $url)) {
4121 $link .= 'http://';
4122 }
4123 $link .= dol_trunc($url, $max);
4124
4125 $linkend = '</a>';
4126
4127 if ($morecss == 'float') { // deprecated
4128 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
4129 } else {
4130 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
4131 }
4132}
4133
4147function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
4148{
4149 global $user, $langs, $hookmanager;
4150
4151 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4152 //$showinvalid = 1; $email = 'rrrrr';
4153
4154 $newemail = dol_escape_htmltag($email);
4155
4156 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4157 $withpicto = 0;
4158 }
4159
4160 if (empty($email)) {
4161 return '&nbsp;';
4162 }
4163
4164 if ($addlink == 1) {
4165 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
4166 if (!preg_match('/^mailto:/i', $email)) {
4167 $newemail .= 'mailto:';
4168 }
4169 $newemail .= $email;
4170 $newemail .= '" target="_blank">';
4171
4172 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4173
4174 if ($max > 0) {
4175 $newemail .= dol_trunc($email, $max);
4176 } else {
4177 $newemail .= $email;
4178 }
4179 $newemail .= '</a>';
4180 if ($showinvalid && !isValidEmail($email)) {
4181 $langs->load("errors");
4182 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
4183 }
4184
4185 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4186 $type = 'AC_EMAIL';
4187 $linktoaddaction = '';
4188 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4189 $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>';
4190 }
4191 if ($linktoaddaction) {
4192 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
4193 }
4194 }
4195 } elseif ($addlink === 'thirdparty') {
4196 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
4197 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4198 if ($withpicto == 1) {
4199 $tmpnewemail .= $newemail;
4200 }
4201 $tmpnewemail .= '</a>';
4202
4203 $newemail = $tmpnewemail;
4204 } else {
4205 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4206
4207 if ($showinvalid && !isValidEmail($email)) {
4208 $langs->load("errors");
4209 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
4210 }
4211 }
4212
4213 //$rep = '<div class="nospan" style="margin-right: 10px">';
4214 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4215 //$rep .= '</div>';
4216 $rep = $newemail;
4217
4218 if ($hookmanager) {
4219 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4220
4221 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4222 if ($reshook > 0) {
4223 $rep = '';
4224 }
4225 $rep .= $hookmanager->resPrint;
4226 }
4227
4228 return $rep;
4229}
4230
4236function getArrayOfSocialNetworks()
4237{
4238 global $conf, $db;
4239
4240 $socialnetworks = array();
4241 // Enable caching of array
4242 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
4243 $cachekey = 'socialnetworks_' . $conf->entity;
4244 $dataretrieved = dol_getcache($cachekey);
4245 if (!is_null($dataretrieved)) {
4246 $socialnetworks = $dataretrieved;
4247 } else {
4248 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
4249 $sql .= " WHERE entity=".$conf->entity;
4250 $resql = $db->query($sql);
4251 if ($resql) {
4252 while ($obj = $db->fetch_object($resql)) {
4253 $socialnetworks[$obj->code] = array(
4254 'rowid' => $obj->rowid,
4255 'label' => $obj->label,
4256 'url' => $obj->url,
4257 'icon' => $obj->icon,
4258 'active' => $obj->active,
4259 );
4260 }
4261 }
4262 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4263 }
4264 return $socialnetworks;
4265}
4266
4277function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4278{
4279 global $hookmanager, $langs, $user;
4280
4281 $htmllink = $value;
4282
4283 if (empty($value)) {
4284 return '&nbsp;';
4285 }
4286
4287 if (!empty($type)) {
4288 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4289 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4290 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4291 if ($type == 'skype') {
4292 $htmllink .= dol_escape_htmltag($value);
4293 $htmllink .= '&nbsp; <a href="skype:';
4294 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4295 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4296 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4297 $htmllink .= '</a><a href="skype:';
4298 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4299 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4300 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4301 $htmllink .= '</a>';
4302 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4303 $addlink = 'AC_SKYPE';
4304 $link = '';
4305 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4306 $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>';
4307 }
4308 $htmllink .= ($link ? ' ' . $link : '');
4309 }
4310 } else {
4311 if (!empty($dictsocialnetworks[$type]['url'])) {
4312 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4313 if ($tmpvirginurl) {
4314 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4315 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4316
4317 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4318 if ($tmpvirginurl3) {
4319 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4320 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4321 }
4322
4323 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4324 if ($tmpvirginurl2) {
4325 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4326 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4327 }
4328 }
4329 if (preg_match('/^https?:\/\//i', $value)) {
4330 $link = $value;
4331 } else {
4332 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4333 }
4334 $valuetoshow = $value;
4335 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4336 if (preg_match('/^https?:\/\//i', $link)) {
4337 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4338 } else {
4339 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4340 }
4341 } else {
4342 $htmllink .= dol_escape_htmltag($value);
4343 }
4344 }
4345 $htmllink .= '</div>';
4346 } else {
4347 $langs->load("errors");
4348 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4349 }
4350
4351 if ($hookmanager) {
4352 $parameters = array(
4353 'value' => $value,
4354 'cid' => $contactid,
4355 'socid' => $socid,
4356 'type' => $type,
4357 'dictsocialnetworks' => $dictsocialnetworks,
4358 );
4359
4360 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4361 if ($reshook > 0) {
4362 $htmllink = '';
4363 }
4364 $htmllink .= $hookmanager->resPrint;
4365 }
4366
4367 return $htmllink;
4368}
4369
4379function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4380{
4381 global $mysoc;
4382
4383 if (empty($profID) || empty($profIDtype)) {
4384 return '';
4385 }
4386 if (empty($countrycode)) {
4387 $countrycode = $mysoc->country_code;
4388 }
4389 $newProfID = $profID;
4390 $id = substr($profIDtype, -1);
4391 $ret = '';
4392 if (strtoupper($countrycode) == 'FR') {
4393 // France
4394 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4395
4396 if ($id == 1 && dol_strlen($newProfID) == 9) {
4397 // SIREN (ex: 123 123 123)
4398 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4399 }
4400 if ($id == 2 && dol_strlen($newProfID) == 14) {
4401 // SIRET (ex: 123 123 123 12345)
4402 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4403 }
4404 if ($id == 3 && dol_strlen($newProfID) == 5) {
4405 // NAF/APE (ex: 69.20Z)
4406 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4407 }
4408 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4409 // TVA intracommunautaire (ex: FR12 123 123 123)
4410 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4411 }
4412 }
4413 if (!empty($addcpButton)) {
4414 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4415 } else {
4416 $ret = $newProfID;
4417 }
4418 return $ret;
4419}
4420
4436function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4437{
4438 global $conf, $user, $langs, $mysoc, $hookmanager;
4439
4440 // Clean phone parameter
4441 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4442 if (empty($phone)) {
4443 return '';
4444 }
4445 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4446 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4447 }
4448 if (empty($countrycode) && is_object($mysoc)) {
4449 $countrycode = $mysoc->country_code;
4450 }
4451
4452 // Short format for small screens
4453 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4454 $separ = '';
4455 }
4456
4457 $newphone = $phone;
4458 $newphonewa = $phone;
4459 if (strtoupper($countrycode) == "FR") {
4460 // France
4461 if (dol_strlen($phone) == 10) {
4462 $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);
4463 } elseif (dol_strlen($phone) == 7) {
4464 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4465 } elseif (dol_strlen($phone) == 9) {
4466 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4467 } elseif (dol_strlen($phone) == 11) {
4468 $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);
4469 } elseif (dol_strlen($phone) == 12) {
4470 $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);
4471 } elseif (dol_strlen($phone) == 13) {
4472 $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);
4473 }
4474 } elseif (strtoupper($countrycode) == "CA") {
4475 if (dol_strlen($phone) == 10) {
4476 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4477 }
4478 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4479 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4480 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4481 }
4482 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4483 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4484 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4485 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4486 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4487 }
4488 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4489 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4490 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4491 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4492 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4493 }
4494 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4495 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4496 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4497 }
4498 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4499 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4500 $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);
4501 }
4502 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4503 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4504 $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);
4505 }
4506 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4507 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4508 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4509 }
4510 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4511 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4512 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4513 }
4514 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4515 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4516 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4517 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4518 $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);
4519 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4520 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4521 }
4522 } elseif (strtoupper($countrycode) == "ML") {//Mali
4523 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4524 $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);
4525 }
4526 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4527 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4528 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4529 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4530 $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);
4531 }
4532 } elseif (strtoupper($countrycode) == "MU") {
4533 //Maurice
4534 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4535 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4536 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4537 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4538 }
4539 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4540 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4541 $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);
4542 }
4543 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4544 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4545 $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);
4546 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4547 $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);
4548 }
4549 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4550 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4551 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4552 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4553 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4554 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4555 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4556 }
4557 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4558 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4559 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4560 }
4561 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4562 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4563 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4564 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4565 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4566 }
4567 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4568 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4569 $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);
4570 }
4571 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4572 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4573 $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);
4574 }
4575 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4576 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4577 $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);
4578 }
4579 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4580 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4581 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4582 }
4583 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4584 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4585 $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);
4586 }
4587 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4588 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4589 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4590 }
4591 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4592 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4593 $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);
4594 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4595 $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);
4596 }
4597 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4598 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4599 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4600 }
4601 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4602 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4603 $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);
4604 }
4605 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4606 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4607 $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);
4608 }
4609 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4610 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4611 $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);
4612 }
4613 } elseif (strtoupper($countrycode) == "IT") {//Italie
4614 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4615 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4616 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4617 $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);
4618 }
4619 } elseif (strtoupper($countrycode) == "AU") {
4620 //Australie
4621 if (dol_strlen($phone) == 12) {
4622 //ex: +61_A_BCDE_FGHI
4623 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4624 }
4625 } elseif (strtoupper($countrycode) == "LU") {
4626 // Luxembourg
4627 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4628 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4629 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4630 $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);
4631 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4632 $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);
4633 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4634 $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);
4635 }
4636 } elseif (strtoupper($countrycode) == "PE") {
4637 // Peru
4638 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4639 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4640 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4641 $newphonewa = '+51'.$newphone;
4642 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3);
4643 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4644 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4645 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4646 $newphonewa = $newphone;
4647 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4648 }
4649 } elseif (strtoupper($countrycode) == "IN") {//India
4650 if (dol_strlen($phone) == 13) {
4651 if ($withpicto == 'phone') {//ex: +91_AB_CDEF_GHIJ
4652 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 4).$separ.substr($newphone, 9, 4);
4653 } else {//ex: +91_ABCDE_FGHIJ
4654 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 5).$separ.substr($newphone, 8, 5);
4655 }
4656 }
4657 }
4658
4659 $newphoneastart = $newphoneaend = '';
4660 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4661 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
4662 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4663 $newphoneaend .= '</a>';
4664 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4665 if (empty($user->clicktodial_loaded)) {
4666 $user->fetch_clicktodial();
4667 }
4668
4669 // Define urlmask
4670 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4671 if (!empty($user->clicktodial_url)) {
4672 $urlmask = $user->clicktodial_url;
4673 }
4674
4675 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4676 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4677 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4678 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4679 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4680 // Those lines are for substitution
4681 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4682 '__PHONETO__' => urlencode($phone),
4683 '__LOGIN__' => $clicktodial_login,
4684 '__PASS__' => $clicktodial_password);
4685 $url = make_substitutions($url, $substitarray);
4686 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4687 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4688 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4689 $newphoneaend = '</a>';
4690 } else {
4691 // Old method
4692 $newphoneastart = '<a href="'.$url.'"';
4693 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4694 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4695 }
4696 $newphoneastart .= '>';
4697 $newphoneaend .= '</a>';
4698 }
4699 }
4700
4701 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4702 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4703 $type = 'AC_TEL';
4704 $addlinktoagenda = '';
4705 if ($addlink == 'AC_FAX') {
4706 $type = 'AC_FAX';
4707 }
4708 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4709 $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>';
4710 }
4711 if ($addlinktoagenda) {
4712 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4713 }
4714 }
4715 }
4716
4717 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4718 // Link to Whatsapp
4719 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4720 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4721 }
4722
4723 if (empty($titlealt)) {
4724 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4725 }
4726 $rep = '';
4727
4728 if ($hookmanager) {
4729 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4730 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4731 $rep .= $hookmanager->resPrint;
4732 }
4733 if (empty($reshook)) {
4734 $picto = '';
4735 if ($withpicto) {
4736 if ($withpicto == 'fax') {
4737 $picto = 'phoning_fax';
4738 } elseif ($withpicto == 'phone') {
4739 $picto = 'phoning';
4740 } elseif ($withpicto == 'mobile') {
4741 $picto = 'phoning_mobile';
4742 } else {
4743 $picto = '';
4744 }
4745 }
4746 if ($adddivfloat == 1) {
4747 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4748 } elseif (empty($adddivfloat)) {
4749 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4750 }
4751
4752 $rep .= $newphoneastart;
4753 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4754 if ($separ != 'hidenum') {
4755 $rep .= ($withpicto ? ' ' : '').$newphone;
4756 }
4757 $rep .= $newphoneaend;
4758
4759 if ($adddivfloat == 1) {
4760 $rep .= '</div>';
4761 } elseif (empty($adddivfloat)) {
4762 $rep .= '</span>';
4763 }
4764 }
4765
4766 return $rep;
4767}
4768
4777function dol_print_ip($ip, $mode = 0, $showname = 0)
4778{
4779 global $conf;
4780
4781 $ret = '';
4782 if (!isset($conf->cache['resolveips'])) {
4783 $conf->cache['resolveips'] = array();
4784 }
4785
4786 if ($mode != 2) {
4787 $countrycode = dolGetCountryCodeFromIp($ip);
4788 if ($countrycode) { // If success, countrycode is us, fr, ...
4789 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4790 $ret .= picto_from_langcode($countrycode);
4791 // $ret .= img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4792 } else {
4793 $ret .= '('.$countrycode.')';
4794 }
4795 $ret .= '&nbsp;';
4796 } else {
4797 // Nothing
4798 }
4799 }
4800
4801 if (in_array($mode, [0, 2])) {
4802 $domain = '';
4803 if ($showname) {
4804 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
4805 $domain = gethostbyaddr($ip);
4806 $conf->cache['resolveips'][$ip] = $domain; // false or domain
4807 } else {
4808 $domain = $conf->cache['resolveips'][$ip];
4809 }
4810 }
4811 if ($domain) {
4812 $ret .= $domain;
4813 } else {
4814 $ret .= $ip;
4815 }
4816 }
4817
4818 return $ret;
4819}
4820
4833function getUserRemoteIP($trusted = 0)
4834{
4835 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
4836 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
4837 // 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)
4838 // This can happen if the proxy were added in the list of trusted proxy.
4839 return $ip;
4840 }
4841
4842 // Try to guess the real IP of client (but this may not be reliable)
4843 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4844 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
4845 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4846 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
4847 } else {
4848 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4849 }
4850 } else {
4851 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
4852 }
4853 } else {
4854 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
4855 }
4856 return $ip;
4857}
4858
4867function isHTTPS()
4868{
4869 $isSecure = false;
4870 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4871 $isSecure = true;
4872 } 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') {
4873 $isSecure = true;
4874 }
4875 return $isSecure;
4876}
4877
4884function dolGetCountryCodeFromIp($ip)
4885{
4886 $countrycode = '';
4887
4888 if (isModEnabled('geoipmaxmind')) {
4889 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4890 //$ip='24.24.24.24';
4891 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4892 if ($datafile) {
4893 try {
4894 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4895 $geoip = new DolGeoIP('country', $datafile);
4896 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4897 $countrycode = $geoip->getCountryCodeFromIP($ip);
4898 } catch (Exception $e) {
4899 //print 'Error with GeoIP database: '.$e->getMessage();
4900 }
4901 }
4902 }
4903
4904 return $countrycode;
4905}
4906
4907
4914function dol_user_country()
4915{
4916 global $conf, $langs, $user;
4917
4918 //$ret=$user->xxx;
4919 $ret = '';
4920 if (isModEnabled('geoipmaxmind')) {
4921 $ip = getUserRemoteIP();
4922 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4923 //$ip='24.24.24.24';
4924 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4925 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4926 $geoip = new DolGeoIP('country', $datafile);
4927 $countrycode = $geoip->getCountryCodeFromIP($ip);
4928 $ret = $countrycode;
4929 }
4930 return $ret;
4931}
4932
4945function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4946{
4947 global $hookmanager;
4948
4949 $out = '';
4950
4951 if ($address) {
4952 if ($hookmanager) {
4953 $parameters = array('element' => $element, 'id' => $id);
4954 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4955 $out .= $hookmanager->resPrint;
4956 }
4957 if (empty($reshook)) {
4958 if (empty($charfornl)) {
4959 $out .= nl2br((string) $address);
4960 } else {
4961 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
4962 }
4963
4964 // TODO Remove this block, we can add this using the hook now
4965 $showgmap = $showomap = 0;
4966 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4967 $showgmap = 1;
4968 }
4969 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4970 $showgmap = 1;
4971 }
4972 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4973 $showgmap = 1;
4974 }
4975 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4976 $showgmap = 1;
4977 }
4978 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4979 $showomap = 1;
4980 }
4981 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4982 $showomap = 1;
4983 }
4984 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4985 $showomap = 1;
4986 }
4987 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4988 $showomap = 1;
4989 }
4990 if ($showgmap) {
4991 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4992 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4993 }
4994 if ($showomap) {
4995 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4996 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4997 }
4998 }
4999 }
5000 if ($noprint) {
5001 return $out;
5002 } else {
5003 print $out;
5004 return null;
5005 }
5006}
5007
5008
5018function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5019{
5020 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5021 return true;
5022 }
5023 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5024 return true;
5025 }
5026 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5027 return true;
5028 }
5029
5030 return false;
5031}
5032
5042function isValidMXRecord($domain)
5043{
5044 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5045 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5046 return 0;
5047 }
5048 if (function_exists('getmxrr')) {
5049 $mxhosts = array();
5050 $weight = array();
5051 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5052 if (count($mxhosts) > 1) {
5053 return 1;
5054 }
5055 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5056 return 1;
5057 }
5058
5059 return 0;
5060 }
5061 }
5062
5063 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5064 return -1;
5065}
5066
5074function isValidPhone($phone)
5075{
5076 return true;
5077}
5078
5079
5089function dolGetFirstLetters($s, $nbofchar = 1)
5090{
5091 $ret = '';
5092 $tmparray = explode(' ', $s);
5093 foreach ($tmparray as $tmps) {
5094 $ret .= dol_substr($tmps, 0, $nbofchar);
5095 }
5096
5097 return $ret;
5098}
5099
5100
5108function dol_strlen($string, $stringencoding = 'UTF-8')
5109{
5110 if (is_null($string)) {
5111 return 0;
5112 }
5113
5114 if (function_exists('mb_strlen')) {
5115 return mb_strlen($string, $stringencoding);
5116 } else {
5117 return strlen($string);
5118 }
5119}
5120
5131function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5132{
5133 global $langs;
5134
5135 if (empty($stringencoding)) {
5136 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5137 }
5138
5139 $ret = '';
5140 if (empty($trunconbytes)) {
5141 if (function_exists('mb_substr')) {
5142 $ret = mb_substr($string, $start, $length, $stringencoding);
5143 } else {
5144 $ret = substr($string, $start, $length);
5145 }
5146 } else {
5147 if (function_exists('mb_strcut')) {
5148 $ret = mb_strcut($string, $start, $length, $stringencoding);
5149 } else {
5150 $ret = substr($string, $start, $length);
5151 }
5152 }
5153 return $ret;
5154}
5155
5156
5170function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5171{
5172 global $conf;
5173
5174 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5175 return $string;
5176 }
5177
5178 if (empty($stringencoding)) {
5179 $stringencoding = 'UTF-8';
5180 }
5181 // reduce for small screen
5182 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5183 $size = round($size / 3);
5184 }
5185
5186 // We go always here
5187 if ($trunc == 'right') {
5188 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5189 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5190 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5191 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
5192 } else {
5193 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5194 return $string;
5195 }
5196 } elseif ($trunc == 'middle') {
5197 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5198 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5199 $size1 = (int) round($size / 2);
5200 $size2 = (int) round($size / 2);
5201 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5202 } else {
5203 return $string;
5204 }
5205 } elseif ($trunc == 'left') {
5206 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5207 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5208 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5209 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5210 } else {
5211 return $string;
5212 }
5213 } elseif ($trunc == 'wrap') {
5214 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5215 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5216 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5217 } else {
5218 return $string;
5219 }
5220 } else {
5221 return 'BadParam3CallingDolTrunc';
5222 }
5223}
5224
5232function getPictoForType($key, $morecss = '')
5233{
5234 // Set array with type -> picto
5235 $type2picto = array(
5236 'varchar' => 'font',
5237 'text' => 'font',
5238 'html' => 'code',
5239 'int' => 'sort-numeric-down',
5240 'double' => 'sort-numeric-down',
5241 'price' => 'currency',
5242 'pricecy' => 'multicurrency',
5243 'password' => 'key',
5244 'boolean' => 'check-square',
5245 'date' => 'calendar',
5246 'datetime' => 'calendar',
5247 'duration' => 'hourglass',
5248 'phone' => 'phone',
5249 'mail' => 'email',
5250 'url' => 'url',
5251 'ip' => 'country',
5252 'select' => 'list',
5253 'sellist' => 'list',
5254 'stars' => 'fontawesome_star_fas',
5255 'radio' => 'check-circle',
5256 'checkbox' => 'list',
5257 'chkbxlst' => 'list',
5258 'link' => 'link',
5259 'icon' => "question",
5260 'point' => "country",
5261 'multipts' => 'country',
5262 'linestrg' => "country",
5263 'polygon' => "country",
5264 'separate' => 'minus'
5265 );
5266
5267 if (!empty($type2picto[$key])) {
5268 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5269 }
5270
5271 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5272}
5273
5274
5297function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5298{
5299 global $conf;
5300
5301 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5302 $url = DOL_URL_ROOT;
5303 $theme = isset($conf->theme) ? $conf->theme : null;
5304 $path = 'theme/'.$theme;
5305 if (empty($picto)) {
5306 $picto = 'generic';
5307 }
5308
5309 // Define fullpathpicto to use into src
5310 if ($pictoisfullpath) {
5311 // Clean parameters
5312 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5313 $picto .= '.png';
5314 }
5315 $fullpathpicto = $picto;
5316 $reg = array();
5317 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5318 $morecss .= ($morecss ? ' ' : '').$reg[1];
5319 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5320 }
5321 } else {
5322 // $picto can not be null since replaced with 'generic' in that case
5323 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5324 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5325 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5326 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5327
5328 // Fix some values of $pictowithouttext
5329 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5330 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5331 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5332 }
5333
5334 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5335 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5336 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5337 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5338
5339 // Compatibility with old fontawesome versions
5340 if ($pictowithouttext == 'file-o') {
5341 $pictowithouttext = 'file';
5342 }
5343
5344 $pictowithouttextarray = explode('_', $pictowithouttext);
5345 $marginleftonlyshort = 0;
5346
5347 if (!empty($pictowithouttextarray[1])) {
5348 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5349 $fakey = 'fa-'.$pictowithouttextarray[0];
5350 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5351 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5352 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5353 } else {
5354 $fakey = 'fa-'.$pictowithouttext;
5355 $faprefix = 'fas';
5356 $facolor = '';
5357 $fasize = '';
5358 }
5359
5360 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5361 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5362 $morestyle = '';
5363 $reg = array();
5364 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5365 $morecss .= ($morecss ? ' ' : '').$reg[1];
5366 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5367 }
5368 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5369 $morestyle = $reg[1];
5370 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5371 }
5372 $moreatt = trim($moreatt);
5373
5374 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5375 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5376 $enabledisablehtml .= '</span>';
5377
5378 return $enabledisablehtml;
5379 }
5380
5381 if (empty($srconly) && in_array($pictowithouttext, getImgPictoNameList())) {
5382 $fakey = $pictowithouttext;
5383 $facolor = '';
5384 $fasize = '';
5385 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5386 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'))) {
5387 $fa = 'far';
5388 }
5389 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'))) {
5390 $fa = 'fab';
5391 }
5392
5393 $arrayconvpictotofa = getImgPictoConv('fa');
5394
5395 if ($pictowithouttext == 'off') {
5396 $fakey = 'fa-square';
5397 $fasize = '1.3em';
5398 } elseif ($pictowithouttext == 'on') {
5399 $fakey = 'fa-check-square';
5400 $fasize = '1.3em';
5401 } elseif ($pictowithouttext == 'listlight') {
5402 $fakey = 'fa-download';
5403 $marginleftonlyshort = 1;
5404 } elseif ($pictowithouttext == 'printer') {
5405 $fakey = 'fa-print';
5406 $fasize = '1.2em';
5407 } elseif ($pictowithouttext == 'note') {
5408 $fakey = 'fa-sticky-note';
5409 $marginleftonlyshort = 1;
5410 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5411 $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');
5412 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5413 if (preg_match('/selected/', $pictowithouttext)) {
5414 $facolor = '#888';
5415 }
5416 $marginleftonlyshort = 1;
5417 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5418 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5419 } else {
5420 $fakey = 'fa-'.$pictowithouttext;
5421 }
5422
5423 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5424 $morecss .= ' em092';
5425 }
5426 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5427 $morecss .= ' em088';
5428 }
5429 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5430 $morecss .= ' em080';
5431 }
5432
5433 // Define $marginleftonlyshort
5434 $arrayconvpictotomarginleftonly = array(
5435 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5436 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5437 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5438 );
5439 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5440 $marginleftonlyshort = 0;
5441 }
5442
5443 // Add CSS
5444 $arrayconvpictotomorcess = array(
5445 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5446 'bank_account' => 'infobox-bank_account',
5447 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5448 'bookcal' => 'infobox-action',
5449 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5450 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5451 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5452 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5453 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5454 'incoterm' => 'infobox-supplier_proposal',
5455 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5456 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5457 'order' => 'infobox-commande',
5458 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5459 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5460 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'info_black' => 'font-status1', 'invoice' => 'infobox-commande',
5461 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5462 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5463 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5464 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5465 'resource' => 'infobox-action',
5466 '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',
5467 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5468 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5469 'vat' => 'infobox-bank_account',
5470 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5471 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode', 'calendarpertype' => 'imgforviewmode'
5472 );
5473 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5474 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5475 }
5476
5477 // Define $color
5478 $arrayconvpictotocolor = array(
5479 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5480 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5481 'dynamicprice' => '#a69944',
5482 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5483 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5484 'lock' => '#ddd', 'lot' => '#a69944',
5485 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5486 'other' => '#ddd', 'world' => '#986c6a',
5487 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5488 //'shipment'=>'#a69944',
5489 'search-plus' => '#808080', 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5490 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5491 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5492 'website' => '#304', 'workstation' => '#a69944'
5493 );
5494 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5495 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5496 }
5497
5498 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5499 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5500 $morestyle = '';
5501 $reg = array();
5502 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5503 $morecss .= ($morecss ? ' ' : '').$reg[1];
5504 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5505 }
5506 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5507 $morestyle = $reg[1];
5508 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5509 }
5510 $moreatt = trim($moreatt);
5511
5512 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5513 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5514 $enabledisablehtml .= '</span>';
5515
5516 return $enabledisablehtml;
5517 }
5518
5519 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5520 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5521 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5522 $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
5523 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5524 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5525 }
5526
5527 // If we ask an image into $url/$mymodule/img (instead of default path)
5528 $regs = array();
5529 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5530 $picto = $regs[1];
5531 $path = $regs[2]; // $path is $mymodule
5532 }
5533
5534 // Clean parameters
5535 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5536 $picto .= '.png';
5537 }
5538 // If alt path are defined, define url where img file is, according to physical path
5539 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5540 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5541 if ($type == 'main') {
5542 continue;
5543 }
5544 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5545 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5546 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5547 break;
5548 }
5549 }
5550
5551 // $url is '' or '/custom', $path is current theme or
5552 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5553 }
5554
5555 if ($srconly) {
5556 return $fullpathpicto;
5557 }
5558
5559 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5560 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
5561}
5562
5568function getImgPictoNameList()
5569{
5570 return array(
5571 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5572 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5573 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5574 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5575 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5576 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5577 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-up',
5578 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5579 'commercial', 'companies',
5580 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5581 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5582 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5583 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5584 'hands-helping', 'help', 'holiday',
5585 'id-card', 'images', 'incoterm', 'info', 'info_black', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5586 'key', 'knowledgemanagement',
5587 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5588 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5589 'off', 'on', 'order',
5590 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5591 'resize', 'search', 'service', 'stats', 'stock',
5592 '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',
5593 'discord', 'facebook', 'flickr', 'instagram','linkedin', 'github', 'google', 'jabber', 'meetup', 'microsoft', 'skype', 'slack', 'twitter', 'pinterest', 'reddit', 'snapchat', 'tumblr', 'youtube', 'viadeo', 'google-plus-g', 'whatsapp',
5594 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5595 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5596 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5597 'technic', 'ticket',
5598 'error', 'warning',
5599 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring', 'rss',
5600 'search-plus', 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'store', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5601 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5602 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'upload', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5603 'conferenceorbooth', 'eventorganization',
5604 'stamp', 'signature',
5605 'webportal'
5606 );
5607}
5608
5616function getImgPictoConv($mode = 'fa')
5617{
5618 global $conf;
5619
5620 // Array when the fa picto key is different than the Dolibarr picto key.
5621 $arrayconvpictotofa = array(
5622 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5623 'asset' => 'money-check-alt', 'autofill' => 'fill',
5624 'back' => 'arrow-left', 'bank_account' => 'university',
5625 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5626 'bookcal' => 'calendar-check',
5627 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5628 'bom' => 'shapes',
5629 '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',
5630 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5631 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5632 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5633 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5634 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5635 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5636 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5637 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5638 'generic' => 'file', 'holiday' => 'umbrella-beach',
5639 'info' => 'info-circle', 'info_black' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5640 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5641 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5642 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5643 'sign-out' => 'sign-out-alt',
5644 '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',
5645 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5646 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table', 'calendarpertype' => 'table',
5647 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5648 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5649 'other' => 'square',
5650 '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',
5651 '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',
5652 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5653 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5654 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5655 'service' => 'concierge-bell',
5656 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5657 'status' => 'stop-circle',
5658 'stripe' => 'stripe-s', 'supplier' => 'building',
5659 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5660 'title_agenda' => 'calendar-alt',
5661 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5662 'jabber' => 'comment',
5663 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5664 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5665 'webportal' => 'door-open'
5666 );
5667
5668 if ($conf->currency == 'EUR') {
5669 $arrayconvpictotofa['currency'] = 'euro-sign';
5670 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5671 } else {
5672 $arrayconvpictotofa['currency'] = 'dollar-sign';
5673 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5674 }
5675
5676 return $arrayconvpictotofa;
5677}
5678
5679
5694function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
5695{
5696 if (strpos($picto, '^') === 0) {
5697 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5698 } else {
5699 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5700 }
5701}
5702
5714function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5715{
5716 global $conf;
5717
5718 if (is_numeric($picto)) {
5719 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5720 //$picto = $leveltopicto[$picto];
5721 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5722 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5723 $picto .= '.png';
5724 }
5725
5726 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5727
5728 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5729}
5730
5742function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5743{
5744 global $conf;
5745
5746 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5747 $picto .= '.png';
5748 }
5749
5750 if ($pictoisfullpath) {
5751 $path = $picto;
5752 } else {
5753 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5754
5755 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5756 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5757
5758 if (file_exists($themepath)) {
5759 $path = $themepath;
5760 }
5761 }
5762 }
5763
5764 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5765}
5766
5780function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5781{
5782 global $langs;
5783
5784 if (empty($titlealt) || $titlealt == 'default') {
5785 if ($numaction == '-1' || $numaction == 'ST_NO') {
5786 $numaction = -1;
5787 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5788 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5789 $numaction = 0;
5790 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5791 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5792 $numaction = 1;
5793 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5794 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5795 $numaction = 2;
5796 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5797 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5798 $numaction = 3;
5799 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5800 } else {
5801 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5802 $numaction = 0;
5803 }
5804 }
5805 if (!is_numeric($numaction)) {
5806 $numaction = 0;
5807 }
5808
5809 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5810}
5811
5819function img_pdf($titlealt = 'default', $size = 3)
5820{
5821 global $langs;
5822
5823 if ($titlealt == 'default') {
5824 $titlealt = $langs->trans('Show');
5825 }
5826
5827 return img_picto($titlealt, 'pdf'.$size.'.png');
5828}
5829
5837function img_edit_add($titlealt = 'default', $other = '')
5838{
5839 global $langs;
5840
5841 if ($titlealt == 'default') {
5842 $titlealt = $langs->trans('Add');
5843 }
5844
5845 return img_picto($titlealt, 'edit_add.png', $other);
5846}
5854function img_edit_remove($titlealt = 'default', $other = '')
5855{
5856 global $langs;
5857
5858 if ($titlealt == 'default') {
5859 $titlealt = $langs->trans('Remove');
5860 }
5861
5862 return img_picto($titlealt, 'edit_remove.png', $other);
5863}
5864
5873function img_edit($titlealt = 'default', $float = 0, $other = '')
5874{
5875 global $langs;
5876
5877 if ($titlealt == 'default') {
5878 $titlealt = $langs->trans('Modify');
5879 }
5880
5881 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5882}
5883
5892function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5893{
5894 global $langs;
5895
5896 if ($titlealt == 'default') {
5897 $titlealt = $langs->trans('View');
5898 }
5899
5900 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5901
5902 return img_picto($titlealt, 'eye', $moreatt);
5903}
5904
5913function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5914{
5915 global $langs;
5916
5917 if ($titlealt == 'default') {
5918 $titlealt = $langs->trans('Delete');
5919 }
5920
5921 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5922}
5923
5931function img_printer($titlealt = "default", $other = '')
5932{
5933 global $langs;
5934 if ($titlealt == "default") {
5935 $titlealt = $langs->trans("Print");
5936 }
5937 return img_picto($titlealt, 'printer.png', $other);
5938}
5939
5947function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5948{
5949 global $langs;
5950
5951 if ($titlealt == 'default') {
5952 $titlealt = $langs->trans('Split');
5953 }
5954
5955 return img_picto($titlealt, 'split.png', $other);
5956}
5957
5965function img_help($usehelpcursor = 1, $usealttitle = 1)
5966{
5967 global $langs;
5968
5969 if ($usealttitle) {
5970 if (is_string($usealttitle)) {
5971 $usealttitle = dol_escape_htmltag($usealttitle);
5972 } else {
5973 $usealttitle = $langs->trans('Info');
5974 }
5975 }
5976
5977 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5978}
5979
5986function img_info($titlealt = 'default')
5987{
5988 global $langs;
5989
5990 if ($titlealt == 'default') {
5991 $titlealt = $langs->trans('Informations');
5992 }
5993
5994 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5995}
5996
6005function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6006{
6007 global $langs;
6008
6009 if ($titlealt == 'default') {
6010 $titlealt = $langs->trans('Warning');
6011 }
6012
6013 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6014 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
6015}
6016
6023function img_error($titlealt = 'default')
6024{
6025 global $langs;
6026
6027 if ($titlealt == 'default') {
6028 $titlealt = $langs->trans('Error');
6029 }
6030
6031 return img_picto($titlealt, 'error.png');
6032}
6033
6041function img_next($titlealt = 'default', $moreatt = '')
6042{
6043 global $langs;
6044
6045 if ($titlealt == 'default') {
6046 $titlealt = $langs->trans('Next');
6047 }
6048
6049 //return img_picto($titlealt, 'next.png', $moreatt);
6050 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
6051}
6052
6060function img_previous($titlealt = 'default', $moreatt = '')
6061{
6062 global $langs;
6063
6064 if ($titlealt == 'default') {
6065 $titlealt = $langs->trans('Previous');
6066 }
6067
6068 //return img_picto($titlealt, 'previous.png', $moreatt);
6069 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
6070}
6071
6080function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6081{
6082 global $langs;
6083
6084 if ($titlealt == 'default') {
6085 $titlealt = $langs->trans('Down');
6086 }
6087
6088 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
6089}
6090
6099function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6100{
6101 global $langs;
6102
6103 if ($titlealt == 'default') {
6104 $titlealt = $langs->trans('Up');
6105 }
6106
6107 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
6108}
6109
6118function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6119{
6120 global $langs;
6121
6122 if ($titlealt == 'default') {
6123 $titlealt = $langs->trans('Left');
6124 }
6125
6126 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
6127}
6128
6137function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6138{
6139 global $langs;
6140
6141 if ($titlealt == 'default') {
6142 $titlealt = $langs->trans('Right');
6143 }
6144
6145 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
6146}
6147
6155function img_allow($allow, $titlealt = 'default')
6156{
6157 global $langs;
6158
6159 if ($titlealt == 'default') {
6160 $titlealt = $langs->trans('Active');
6161 }
6162
6163 if ($allow == 1) {
6164 return img_picto($titlealt, 'tick.png');
6165 }
6166
6167 return '-';
6168}
6169
6177function img_credit_card($brand, $morecss = null)
6178{
6179 if (is_null($morecss)) {
6180 $morecss = 'fa-2x';
6181 }
6182
6183 if ($brand == 'visa' || $brand == 'Visa') {
6184 $brand = 'cc-visa';
6185 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6186 $brand = 'cc-mastercard';
6187 } elseif ($brand == 'amex' || $brand == 'American Express') {
6188 $brand = 'cc-amex';
6189 } elseif ($brand == 'discover' || $brand == 'Discover') {
6190 $brand = 'cc-discover';
6191 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6192 $brand = 'cc-jcb';
6193 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6194 $brand = 'cc-diners-club';
6195 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6196 $brand = 'credit-card';
6197 }
6198
6199 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
6200}
6201
6210function img_mime($file, $titlealt = '', $morecss = '')
6211{
6212 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
6213
6214 $mimetype = dol_mimetype($file, '', 1);
6215 //$mimeimg = dol_mimetype($file, '', 2);
6216 $mimefa = dol_mimetype($file, '', 4);
6217
6218 if (empty($titlealt)) {
6219 $titlealt = 'Mime type: '.$mimetype;
6220 }
6221
6222 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6223 return '<i class="fa fa-'.$mimefa.' '.(preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ').($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.dolPrintHTMLForAttribute($titlealt).'"' : '').'></i>';
6224}
6225
6226
6234function img_search($titlealt = 'default', $other = '')
6235{
6236 global $langs;
6237
6238 if ($titlealt == 'default') {
6239 $titlealt = $langs->trans('Search');
6240 }
6241
6242 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
6243
6244 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
6245 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6246
6247 return $input;
6248}
6249
6257function img_searchclear($titlealt = 'default', $other = '')
6258{
6259 global $langs;
6260
6261 if ($titlealt == 'default') {
6262 $titlealt = $langs->trans('Search');
6263 }
6264
6265 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6266
6267 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
6268 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6269
6270 return $input;
6271}
6272
6285function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6286{
6287 global $conf, $langs;
6288
6289 if ($infoonimgalt) {
6290 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
6291 } else {
6292 if (empty($conf->use_javascript_ajax)) {
6293 $textfordropdown = '';
6294 }
6295
6296 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
6297 $fa = 'info-circle';
6298 if ($picto == 'warning') {
6299 $fa = 'exclamation-triangle';
6300 }
6301 $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> ';
6302 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6303 $result .= ($nodiv ? '' : '</div>');
6304
6305 if ($textfordropdown) {
6306 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
6307 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
6308 jQuery(document).ready(function() {
6309 jQuery(".'.$class.'text").click(function() {
6310 console.log("toggle text");
6311 jQuery(".'.$class.'").toggle();
6312 });
6313 });
6314 </script>';
6315
6316 $result = $tmpresult.$result;
6317 }
6318 }
6319
6320 return $result;
6321}
6322
6323
6335function dol_print_error($db = null, $error = '', $errors = null)
6336{
6337 global $conf, $langs, $user, $argv;
6338 global $dolibarr_main_prod;
6339
6340 $out = '';
6341 $syslog = '';
6342
6343 // If error occurs before the $lang object was loaded
6344 if (!$langs) {
6345 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6346 $langs = new Translate('', $conf);
6347 $langs->load("main");
6348 }
6349
6350 // Load translation files required by the error messages
6351 $langs->loadLangs(array('main', 'errors'));
6352
6353 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6354 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6355 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6356 $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";
6357 }
6358 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6359
6360 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6361 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6362 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6363 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6364 }
6365 if ($user instanceof User) {
6366 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6367 }
6368 if (function_exists("phpversion")) {
6369 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6370 }
6371 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6372 if (function_exists("php_uname")) {
6373 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6374 }
6375 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6376 $out .= "<br>\n";
6377 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6378 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6379 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6380 $out .= "<br>\n";
6381 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6382 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6383 } else { // Mode CLI
6384 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6385 $syslog .= "pid=".dol_getmypid();
6386 }
6387
6388 if (!empty($conf->modules)) {
6389 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6390 }
6391
6392 if (is_object($db)) {
6393 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6394 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6395 $lastqueryerror = $db->lastqueryerror();
6396 if (!utf8_check($lastqueryerror)) {
6397 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6398 }
6399 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6400 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6401 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6402 $out .= "<br>\n";
6403 } else { // Mode CLI
6404 // No dol_escape_htmltag for output, we are in CLI mode
6405 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6406 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6407 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6408 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6409 }
6410 $syslog .= ", sql=".$db->lastquery();
6411 $syslog .= ", db_error=".$db->lasterror();
6412 }
6413
6414 if ($error || $errors) {
6415 // Merge all into $errors array
6416 if (is_array($error) && is_array($errors)) {
6417 $errors = array_merge($error, $errors);
6418 } elseif (is_array($error)) { // deprecated, use second parameters
6419 $errors = $error;
6420 } elseif (is_array($errors) && !empty($error)) {
6421 $errors = array_merge(array($error), $errors);
6422 } elseif (!empty($error)) {
6423 $errors = array_merge(array($error), array($errors));
6424 }
6425
6426 $langs->load("errors");
6427
6428 foreach ($errors as $msg) {
6429 if (empty($msg)) {
6430 continue;
6431 }
6432 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6433 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6434 } else { // Mode CLI
6435 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6436 }
6437 $syslog .= ", msg=".$msg;
6438 }
6439 }
6440 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6441 xdebug_print_function_stack();
6442 $out .= '<b>XDebug information:</b>'."<br>\n";
6443 $out .= 'File: '.xdebug_call_file()."<br>\n";
6444 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6445 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6446 $out .= "<br>\n";
6447 }
6448
6449 // Return a http header with error code if possible
6450 if (!headers_sent()) {
6451 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6452 top_httphead();
6453 }
6454 //http_response_code(500); // If we use 500, message is not output with some command line tools
6455 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
6456 }
6457
6458 if (empty($dolibarr_main_prod)) {
6459 print $out;
6460 } else {
6461 if (empty($langs->defaultlang)) {
6462 $langs->setDefaultLang();
6463 }
6464 $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.
6465 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6466 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";
6467 print $langs->trans("DolibarrHasDetectedError").'. ';
6468 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6469 if (!defined("MAIN_CORE_ERROR")) {
6470 define("MAIN_CORE_ERROR", 1);
6471 }
6472 }
6473
6474 dol_syslog("Error ".$syslog, LOG_ERR);
6475}
6476
6487function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6488{
6489 global $langs;
6490
6491 if (empty($email)) {
6492 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6493 }
6494
6495 $langs->load("errors");
6496 $now = dol_now();
6497
6498 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6499 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6500 if ($errormessage) {
6501 print '<br><br>'.$errormessage;
6502 }
6503 if (is_array($errormessages) && count($errormessages)) {
6504 foreach ($errormessages as $mesgtoshow) {
6505 print '<br><br>'.$mesgtoshow;
6506 }
6507 }
6508 print '</div></div>';
6509}
6510
6527function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6528{
6529 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6530}
6531
6550function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6551{
6552 global $langs, $form;
6553 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6554
6555 if ($moreattrib == 'class="right"') {
6556 $prefix .= 'right '; // For backward compatibility
6557 }
6558
6559 $sortorder = strtoupper((string) $sortorder);
6560 $out = '';
6561 $sortimg = '';
6562
6563 $tag = 'th';
6564 if ($thead == 2) {
6565 $tag = 'div';
6566 }
6567
6568 $tmpsortfield = explode(',', (string) $sortfield);
6569 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6570 $tmpfield = explode(',', $field);
6571 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6572
6573 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6574 $prefix = 'wrapcolumntitle '.$prefix;
6575 }
6576
6577 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6578 // If field is used as sort criteria we use a specific css class liste_titre_sel
6579 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6580 $liste_titre = 'liste_titre';
6581 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6582 $liste_titre = 'liste_titre_sel';
6583 }
6584
6585 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6586 //$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)).'"' : '');
6587 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dolPrintHTMLForAttribute($langs->trans($name)).'"' : '';
6588 $tagstart .= '>';
6589
6590 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6591 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6592 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6593 $options = preg_replace('/&+/i', '&', $options);
6594 if (!preg_match('/^&/', $options)) {
6595 $options = '&'.$options;
6596 }
6597
6598 $sortordertouseinlink = '';
6599 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6600 if (preg_match('/^DESC/i', $sortorder)) {
6601 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6602 } else { // We reverse the var $sortordertouseinlink
6603 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6604 }
6605 } else { // We are on field that is the first current sorting criteria
6606 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6607 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6608 } else {
6609 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6610 }
6611 }
6612 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6613 $out .= '<a class="reposition" href="'.$file.'?sortfield='.urlencode($field).'&sortorder='.urlencode($sortordertouseinlink).'&begin='.urlencode($begin).$options.'"';
6614 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
6615 $out .= '>';
6616 }
6617 if ($tooltip) {
6618 // You can also use 'TranslationString:keyfortooltiponclick:tooltipdirection' for a tooltip on click or to change tooltip position.
6619 if (strpos($tooltip, ':') !== false) {
6620 $tmptooltip = explode(':', $tooltip);
6621 } else {
6622 $tmptooltip = array($tooltip);
6623 }
6624 $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]));
6625 } else {
6626 $out .= $langs->trans((string) $name);
6627 }
6628
6629 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6630 $out .= '</a>';
6631 }
6632
6633 if (empty($thead) && $field) { // If this is a sort field
6634 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6635 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6636 $options = preg_replace('/&+/i', '&', $options);
6637 if (!preg_match('/^&/', $options)) {
6638 $options = '&'.$options;
6639 }
6640
6641 if (!$sortorder || ($field1 != $sortfield1)) {
6642 // Nothing
6643 } else {
6644 if (preg_match('/^DESC/', $sortorder)) {
6645 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6646 }
6647 if (preg_match('/^ASC/', $sortorder)) {
6648 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6649 }
6650 }
6651 }
6652
6653 $tagend = '</'.$tag.'>';
6654
6655 $out = $tagstart.$sortimg.$out.$tagend;
6656
6657 return $out;
6658}
6659
6668function print_titre($title)
6669{
6670 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6671
6672 print '<div class="titre">'.$title.'</div>';
6673}
6674
6686function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6687{
6688 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6689}
6690
6704function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6705{
6706 $return = '';
6707
6708 if ($picto == 'setup') {
6709 $picto = 'generic';
6710 }
6711
6712 $return .= "\n";
6713 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6714 $return .= '<tr class="toptitle">';
6715 if ($picto) {
6716 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6717 }
6718 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6719 $return .= '<div class="titre inline-block">';
6720 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6721 $return .= '</div>';
6722 $return .= '</td>';
6723 if (dol_strlen($morehtmlcenter)) {
6724 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6725 }
6726 if (dol_strlen($morehtmlright)) {
6727 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6728 }
6729 $return .= '</tr></table>'."\n";
6730
6731 return $return;
6732}
6733
6757function 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 = '')
6758{
6759 global $conf, $langs;
6760
6761 $savlimit = $limit;
6762 $savtotalnboflines = $totalnboflines;
6763 if (is_numeric($totalnboflines)) {
6764 $totalnboflines = abs($totalnboflines);
6765 }
6766
6767 // Detect if there is a subtitle
6768 $subtitle = '';
6769 $tmparray = preg_split('/<br>/i', $title, 2);
6770 if (!empty($tmparray[1])) {
6771 $title = $tmparray[0];
6772 $subtitle = $tmparray[1];
6773 }
6774
6775 $page = (int) $page;
6776
6777 if ($picto == 'setup') {
6778 $picto = 'title_setup.png';
6779 }
6780 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6781 $picto = 'title.gif';
6782 }
6783 if ($limit < 0) {
6784 $limit = $conf->liste_limit;
6785 }
6786
6787 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6788 $nextpage = 1;
6789 } else {
6790 $nextpage = 0;
6791 }
6792 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6793
6794 print "\n";
6795 print "<!-- Begin print_barre_liste -->\n";
6796 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6797 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6798
6799 // Left
6800
6801 if ($picto && $title) {
6802 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6803 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6804 print '</td>';
6805 }
6806
6807 print '<td class="nobordernopadding valignmiddle col-title">';
6808 print '<div class="titre inline-block">';
6809 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()
6810 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6811 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6812 }
6813 print '</div>';
6814 if (!empty($subtitle)) {
6815 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6816 }
6817 print '</td>';
6818
6819 // Center
6820 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6821 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6822 }
6823
6824 // Right
6825 print '<td class="nobordernopadding valignmiddle right col-right">';
6826 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6827 if ($sortfield) {
6828 $options .= "&sortfield=".urlencode($sortfield);
6829 }
6830 if ($sortorder) {
6831 $options .= "&sortorder=".urlencode($sortorder);
6832 }
6833 // Show navigation bar
6834 $pagelist = '';
6835 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6836 if ($totalnboflines) { // If we know total nb of lines
6837 // Define nb of extra page links before and after selected page + ... + first or last
6838 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6839
6840 if ($limit > 0) {
6841 $nbpages = ceil($totalnboflines / $limit);
6842 } else {
6843 $nbpages = 1;
6844 }
6845 $cpt = ($page - $maxnbofpage);
6846 if ($cpt < 0) {
6847 $cpt = 0;
6848 }
6849
6850 if ($cpt >= 1) {
6851 if (empty($pagenavastextinput)) {
6852 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6853 if ($cpt > 2) {
6854 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6855 } elseif ($cpt == 2) {
6856 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6857 }
6858 }
6859 }
6860
6861 do {
6862 if ($pagenavastextinput) {
6863 if ($cpt == $page) {
6864 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone heightofcombo" name="pageplusone" value="'.($page + 1).'"></li>';
6865 $pagelist .= '/';
6866 }
6867 } else {
6868 if ($cpt == $page) {
6869 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6870 } else {
6871 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6872 }
6873 }
6874 $cpt++;
6875 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6876
6877 if (empty($pagenavastextinput)) {
6878 if ($cpt < $nbpages) {
6879 if ($cpt < $nbpages - 2) {
6880 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6881 } elseif ($cpt == $nbpages - 2) {
6882 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6883 }
6884 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6885 }
6886 } else {
6887 //var_dump($page.' '.$cpt.' '.$nbpages);
6888 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6889 }
6890 } else {
6891 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6892 }
6893 }
6894
6895 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6896 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
6897 }
6898
6899 // js to autoselect page field on focus
6900 if ($pagenavastextinput) {
6901 print ajax_autoselect('.pageplusone');
6902 }
6903
6904 print '</td>';
6905 print '</tr>';
6906
6907 print '</table>'."\n";
6908
6909 // Center
6910 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6911 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6912 }
6913
6914 print "<!-- End title -->\n\n";
6915}
6916
6933function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6934{
6935 global $conf, $langs;
6936
6937 print '<div class="pagination"><ul>';
6938 if ($beforearrows) {
6939 print '<li class="paginationbeforearrows">';
6940 print $beforearrows;
6941 print '</li>';
6942 }
6943
6944 if (empty($hidenavigation)) {
6945 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6946 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6947 $pagesizechoices .= ',5000:5000';
6948 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6949 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6950 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6951 //$pagesizechoices .= ',2:2';
6952 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6953 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6954 }
6955
6956 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6957 print '<li class="pagination">';
6958 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.'">';
6959 print '<datalist id="limitlist">';
6960 } else {
6961 print '<li class="paginationcombolimit valignmiddle">';
6962 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")).'">';
6963 }
6964 $tmpchoice = explode(',', $pagesizechoices);
6965 $tmpkey = $limit.':'.$limit;
6966 if (!in_array($tmpkey, $tmpchoice)) {
6967 $tmpchoice[$tmpkey] = $tmpkey;
6968 }
6969 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6970 if (!in_array($tmpkey, $tmpchoice)) {
6971 $tmpchoice[$tmpkey] = $tmpkey;
6972 }
6973 asort($tmpchoice, SORT_NUMERIC);
6974 foreach ($tmpchoice as $val) {
6975 $selected = '';
6976 $tmp = explode(':', $val);
6977 $key = $tmp[0];
6978 $val = $tmp[1];
6979 if ($key != '' && $val != '') {
6980 if ((int) $key == (int) $limit) {
6981 $selected = ' selected="selected"';
6982 }
6983 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6984 }
6985 }
6986 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6987 print '</datalist>';
6988 } else {
6989 print '</select>';
6990 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
6991 //print ajax_combobox("limit");
6992 }
6993
6994 if ($conf->use_javascript_ajax) {
6995 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6996 <script>
6997 jQuery(document).ready(function () {
6998 jQuery(".selectlimit").change(function() {
6999 console.log("We change limit so we submit the form");
7000 $(this).parents(\'form:first\').submit();
7001 });
7002 });
7003 </script>
7004 ';
7005 }
7006 print '</li>';
7007 }
7008 if ($page > 0) {
7009 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>';
7010 }
7011 if ($betweenarrows) {
7012 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7013 print $betweenarrows;
7014 print '<!--</div>-->';
7015 }
7016 if ($nextpage > 0) {
7017 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>';
7018 }
7019 if ($afterarrows) {
7020 print '<li class="paginationafterarrows">';
7021 print $afterarrows;
7022 print '</li>';
7023 }
7024 }
7025 print '</ul></div>'."\n";
7026}
7027
7028
7040function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7041{
7042 $morelabel = '';
7043
7044 if (preg_match('/%/', $rate)) {
7045 $rate = str_replace('%', '', $rate);
7046 $addpercent = true;
7047 }
7048 $reg = array();
7049 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7050 $morelabel = ' ('.$reg[1].')';
7051 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
7052 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
7053 }
7054 if (preg_match('/\*/', $rate)) {
7055 $rate = str_replace('*', '', $rate);
7056 $info_bits |= 1;
7057 }
7058
7059 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7060 if (!preg_match('/\//', $rate)) {
7061 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
7062 } else {
7063 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7064 $ret = $rate.($addpercent ? '%' : '');
7065 }
7066 if (($info_bits & 1) && $usestarfornpr >= 0) {
7067 $ret .= ' *';
7068 }
7069 $ret .= $morelabel;
7070 return $ret;
7071}
7072
7073
7089function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7090{
7091 global $langs, $conf;
7092
7093 // Clean parameters
7094 if (empty($amount)) {
7095 $amount = 0; // To have a numeric value if amount not defined or = ''
7096 }
7097 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7098 if ($rounding == -1) {
7099 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7100 }
7101 $nbdecimal = $rounding;
7102
7103 if ($outlangs === 'none') {
7104 // Use international separators
7105 $dec = '.';
7106 $thousand = '';
7107 } else {
7108 // Output separators by default (french)
7109 $dec = ',';
7110 $thousand = ' ';
7111
7112 // If $outlangs not forced, we use use language
7113 if (!($outlangs instanceof Translate)) {
7114 $outlangs = $langs;
7115 }
7116
7117 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7118 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7119 }
7120 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7121 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7122 }
7123 if ($thousand == 'None') {
7124 $thousand = '';
7125 } elseif ($thousand == 'Space') {
7126 $thousand = ' ';
7127 }
7128 }
7129 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7130
7131 //print "amount=".$amount."-";
7132 $amount = str_replace(',', '.', $amount); // should be useless
7133 //print $amount."-";
7134 $data = explode('.', $amount);
7135 $decpart = isset($data[1]) ? $data[1] : '';
7136 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
7137 //print "decpart=".$decpart."<br>";
7138 $end = '';
7139
7140 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7141 if (dol_strlen($decpart) > $nbdecimal) {
7142 $nbdecimal = dol_strlen($decpart);
7143 }
7144
7145 // If nbdecimal is higher than max to show
7146 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7147 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7148 $nbdecimal = $nbdecimalmaxshown;
7149 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7150 // If output is truncated, we show ...
7151 $end = '...';
7152 }
7153 }
7154
7155 // If force rounding
7156 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7157 if ($forcerounding === 'MU') {
7158 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7159 } elseif ($forcerounding === 'MT') {
7160 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7161 } elseif ($forcerounding >= 0) {
7162 $nbdecimal = (int) $forcerounding;
7163 }
7164 }
7165
7166 // Format number
7167 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7168 // Add symbol of currency if requested
7169 $cursymbolbefore = $cursymbolafter = '';
7170 if ($currency_code && is_object($outlangs)) {
7171 if ($currency_code == 'auto') {
7172 $currency_code = $conf->currency;
7173 }
7174
7175 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7176 $listoflanguagesbefore = array('nl_NL');
7177 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7178 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7179 } else {
7180 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7181 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
7182 }
7183 }
7184 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
7185 if ($form) {
7186 $output = preg_replace('/\s/', '&nbsp;', $output);
7187 $output = preg_replace('/\'/', '&#039;', $output);
7188 }
7189
7190 return $output;
7191}
7192
7217function price2num($amount, $rounding = '', $option = 0)
7218{
7219 global $langs, $conf;
7220
7221 // Clean parameters
7222 if (is_null($amount)) {
7223 $amount = '';
7224 }
7225
7226 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7227 // Numbers must be '1234.56'
7228 // Decimal delimiter for PHP and database SQL requests must be '.'
7229 $dec = ',';
7230 $thousand = ' ';
7231 if (is_null($langs)) { // $langs is not defined, we use english values.
7232 $dec = '.';
7233 $thousand = ',';
7234 } else {
7235 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7236 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7237 }
7238 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7239 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7240 }
7241 }
7242 if ($thousand == 'None') {
7243 $thousand = '';
7244 } elseif ($thousand == 'Space') {
7245 $thousand = ' ';
7246 }
7247 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7248
7249 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7250 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7251 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7252 if (!is_numeric($amount)) {
7253 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7254 }
7255
7256 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
7257 $amount = str_replace($thousand, '', $amount);
7258 }
7259
7260 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7261 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7262 // So if number was already a good number, it is converted into local Dolibarr setup.
7263 if (is_numeric($amount)) {
7264 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7265 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7266 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7267 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7268 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7269 }
7270 //print "QQ".$amount."<br>\n";
7271
7272 // Now make replaceents (the main goal of function)
7273
7274 if ($thousand != ',' && $thousand != '.') {
7275 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7276
7277 // REGEX: Find the integral and decimal parts.
7278 //
7279 // We require that the decimal point only appears once in $amount.
7280 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7281 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7282 // It covers two cases:
7283 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7284 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7285 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7286 $matches = array();
7287 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7288 $intPart = $matches['int'];
7289 $decPart = $matches['dec'];
7290
7291 // Remove all commas and dots from intPart
7292 $intPart = str_replace(['.', ','], '', $intPart);
7293
7294 // Combine intPart and decPart with a dot
7295 $amount = $intPart . $dec . $decPart;
7296 }
7297 }
7298
7299 $amount = str_replace(' ', '', $amount); // To avoid spaces
7300 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7301 $amount = str_replace($dec, '.', $amount);
7302
7303 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7304 }
7305 //print ' XX'.$amount.' '.$rounding;
7306
7307 // Now, $amount is a real PHP float number. We make a rounding if required.
7308 if ($rounding) {
7309 $nbofdectoround = '';
7310 if ($rounding == 'MU') {
7311 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7312 } elseif ($rounding == 'MT') {
7313 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7314 } elseif ($rounding == 'MS') {
7315 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK') : 5;
7316 } elseif ($rounding == 'CU') {
7317 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7318 } elseif ($rounding == 'CT') {
7319 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7320 } elseif (is_numeric($rounding)) {
7321 $nbofdectoround = (int) $rounding;
7322 }
7323
7324 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7325 if (dol_strlen($nbofdectoround)) {
7326 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7327 } else {
7328 return 'ErrorBadParameterProvidedToFunction';
7329 }
7330 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7331
7332 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7333 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7334 if (is_numeric($amount)) {
7335 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7336 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7337 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7338 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7339 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7340 }
7341 //print "TT".$amount.'<br>';
7342
7343 // Always make replace because each math function (like round) replace
7344 // with local values and we want a number that has a SQL string format x.y
7345 if ($thousand != ',' && $thousand != '.') {
7346 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7347 }
7348
7349 $amount = str_replace(' ', '', $amount); // To avoid spaces
7350 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7351 $amount = str_replace($dec, '.', $amount);
7352
7353 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7354 }
7355
7356 return $amount;
7357}
7358
7371function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7372{
7373 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7374
7375 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7376 $dimension *= 1000000;
7377 $unit -= 6;
7378 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7379 $dimension *= 1000;
7380 $unit -= 3;
7381 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7382 $dimension /= 1000000;
7383 $unit += 6;
7384 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7385 $dimension /= 1000;
7386 $unit += 3;
7387 }
7388 // Special case when we want output unit into pound or ounce
7389 /* TODO
7390 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7391 {
7392 $dimension = // convert dimension from standard unit into ounce or pound
7393 $unit = $forceunitoutput;
7394 }
7395 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7396 {
7397 $dimension = // convert dimension from standard unit into ounce or pound
7398 $unit = $forceunitoutput;
7399 }*/
7400
7401 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7402 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7403 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
7404
7405 return $ret;
7406}
7407
7408
7421function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7422{
7423 global $db, $conf, $mysoc;
7424
7425 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7426 $thirdparty_seller = $mysoc;
7427 }
7428
7429 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);
7430
7431 $vatratecleaned = $vatrate;
7432 $reg = array();
7433 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7434 $vatratecleaned = trim($reg[1]);
7435 $vatratecode = $reg[2];
7436 }
7437
7438 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7439 {
7440 return 0;
7441 }*/
7442
7443 // Some test to guess with no need to make database access
7444 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7445 if ($local == 1) {
7446 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7447 return 0;
7448 }
7449 if ($thirdparty_seller->id == $mysoc->id) {
7450 if (!$thirdparty_buyer->localtax1_assuj) {
7451 return 0;
7452 }
7453 } else {
7454 if (!$thirdparty_seller->localtax1_assuj) {
7455 return 0;
7456 }
7457 }
7458 }
7459
7460 if ($local == 2) {
7461 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7462 if (!$mysoc->localtax2_assuj) {
7463 return 0; // If main vat is 0, IRPF may be different than 0.
7464 }
7465 if ($thirdparty_seller->id == $mysoc->id) {
7466 if (!$thirdparty_buyer->localtax2_assuj) {
7467 return 0;
7468 }
7469 } else {
7470 if (!$thirdparty_seller->localtax2_assuj) {
7471 return 0;
7472 }
7473 }
7474 }
7475 } else {
7476 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7477 return 0;
7478 }
7479 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7480 return 0;
7481 }
7482 }
7483
7484 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7485 if (in_array($mysoc->country_code, array('ES'))) {
7486 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7487 }
7488
7489 // Search local taxes
7490 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7491 if ($local == 1) {
7492 if ($thirdparty_seller != $mysoc) {
7493 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7494 return $thirdparty_seller->localtax1_value;
7495 }
7496 } else { // i am the seller
7497 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7498 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7499 }
7500 }
7501 }
7502 if ($local == 2) {
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 // TODO We should also return value defined on thirdparty only if defined
7506 return $thirdparty_seller->localtax2_value;
7507 }
7508 } else { // i am the seller
7509 if (in_array($mysoc->country_code, array('ES'))) {
7510 return $thirdparty_buyer->localtax2_value;
7511 } else {
7512 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7513 }
7514 }
7515 }
7516 }
7517
7518 // By default, search value of local tax on line of common tax
7519 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7520 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7521 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7522 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7523 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7524 if (!empty($vatratecode)) {
7525 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7526 } else {
7527 $sql .= " AND t.recuperableonly = '".$db->escape((string) $vatnpr)."'";
7528 }
7529
7530 $resql = $db->query($sql);
7531
7532 if ($resql) {
7533 $obj = $db->fetch_object($resql);
7534 if ($obj) {
7535 if ($local == 1) {
7536 return $obj->localtax1;
7537 } elseif ($local == 2) {
7538 return $obj->localtax2;
7539 }
7540 }
7541 }
7542
7543 return 0;
7544}
7545
7546
7555function isOnlyOneLocalTax($local)
7556{
7557 $tax = get_localtax_by_third($local);
7558
7559 $valors = explode(":", $tax);
7560
7561 if (count($valors) > 1) {
7562 return false;
7563 } else {
7564 return true;
7565 }
7566}
7567
7574function get_localtax_by_third($local)
7575{
7576 global $db, $mysoc;
7577
7578 $sql = " SELECT t.localtax".$local." as localtax";
7579 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7580 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7581 $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";
7582 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7583 $sql .= " AND t.localtax".$local."_type <> '0'";
7584 $sql .= " ORDER BY t.rowid DESC";
7585
7586 $resql = $db->query($sql);
7587 if ($resql) {
7588 $obj = $db->fetch_object($resql);
7589 if ($obj) {
7590 return $obj->localtax;
7591 } else {
7592 return '0';
7593 }
7594 }
7595
7596 return 'Error';
7597}
7598
7599
7611function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7612{
7613 global $db;
7614
7615 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7616
7617 // Search local taxes
7618 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7619 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7620 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7621 if ($firstparamisid) {
7622 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7623 } else {
7624 $vatratecleaned = $vatrate;
7625 $vatratecode = '';
7626 $reg = array();
7627 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7628 $vatratecleaned = $reg[1];
7629 $vatratecode = $reg[2];
7630 }
7631
7632 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7633 /*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 ??
7634 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7635 $sql .= " WHERE t.fk_pays = c.rowid";
7636 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7637 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7638 } else {
7639 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7640 }
7641 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7642 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7643 if ($vatratecode) {
7644 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7645 }
7646 }
7647
7648 $resql = $db->query($sql);
7649 if ($resql) {
7650 $obj = $db->fetch_object($resql);
7651 if ($obj) {
7652 return array(
7653 'rowid' => $obj->rowid,
7654 'code' => $obj->code,
7655 'rate' => $obj->rate,
7656 'localtax1' => $obj->localtax1,
7657 'localtax1_type' => $obj->localtax1_type,
7658 'localtax2' => $obj->localtax2,
7659 'localtax2_type' => $obj->localtax2_type,
7660 'npr' => $obj->npr,
7661 'accountancy_code_sell' => $obj->accountancy_code_sell,
7662 'accountancy_code_buy' => $obj->accountancy_code_buy
7663 );
7664 } else {
7665 return array();
7666 }
7667 } else {
7668 dol_print_error($db);
7669 }
7670
7671 return array();
7672}
7673
7690function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7691{
7692 global $db, $mysoc;
7693
7694 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7695
7696 // Search local taxes
7697 $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";
7698 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7699 if ($firstparamisid) {
7700 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7701 } else {
7702 $vatratecleaned = $vatrate;
7703 $vatratecode = '';
7704 $reg = array();
7705 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7706 $vatratecleaned = $reg[1];
7707 $vatratecode = $reg[2];
7708 }
7709
7710 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7711 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7712 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7713 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7714 } else {
7715 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7716 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7717 }
7718 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7719 if ($vatratecode) {
7720 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7721 }
7722 }
7723
7724 $resql = $db->query($sql);
7725 if ($resql) {
7726 $obj = $db->fetch_object($resql);
7727
7728 if ($obj) {
7729 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7730
7731 if ($local == 1) {
7732 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7733 } elseif ($local == 2) {
7734 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7735 } else {
7736 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);
7737 }
7738 }
7739 }
7740
7741 return array();
7742}
7743
7754function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7755{
7756 global $db, $mysoc;
7757
7758 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7759
7760 $ret = 0;
7761 $found = 0;
7762
7763 if ($idprod > 0) {
7764 // Load product
7765 $product = new Product($db);
7766 $product->fetch($idprod);
7767
7768 if (($mysoc->country_code == $thirdpartytouse->country_code)
7769 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7770 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7771 ) {
7772 // If country of thirdparty to consider is ours
7773 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7774 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
7775 if ($result > 0) {
7776 $ret = $product->vatrate_supplier;
7777 if ($product->default_vat_code_supplier) {
7778 $ret .= ' ('.$product->default_vat_code_supplier.')';
7779 }
7780 $found = 1;
7781 }
7782 }
7783 if (!$found) {
7784 $ret = $product->tva_tx; // Default sales vat of product
7785 if ($product->default_vat_code) {
7786 $ret .= ' ('.$product->default_vat_code.')';
7787 }
7788 $found = 1;
7789 }
7790 } else {
7791 // TODO Read default product vat according to product and an other countrycode.
7792 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7793 }
7794 }
7795
7796 if (!$found) {
7797 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7798 // 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).
7799 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7800 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7801 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7802 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7803 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7804 $sql .= $db->plimit(1);
7805
7806 $resql = $db->query($sql);
7807 if ($resql) {
7808 $obj = $db->fetch_object($resql);
7809 if ($obj) {
7810 $ret = $obj->vat_rate;
7811 if ($obj->default_vat_code) {
7812 $ret .= ' ('.$obj->default_vat_code.')';
7813 }
7814 }
7815 $db->free($resql);
7816 } else {
7817 dol_print_error($db);
7818 }
7819 } else {
7820 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7821 // '1.23'
7822 // or '1.23 (CODE)'
7823 $defaulttx = '';
7824 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7825 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7826 }
7827 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7828 $defaultcode = $reg[1];
7829 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7830 }*/
7831
7832 $ret = $defaulttx;
7833 }
7834 }
7835
7836 dol_syslog("get_product_vat_for_country: ret=".$ret);
7837 return $ret;
7838}
7839
7849function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7850{
7851 global $db, $mysoc;
7852
7853 if (!class_exists('Product')) {
7854 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7855 }
7856
7857 $ret = 0;
7858 $found = 0;
7859
7860 if ($idprod > 0) {
7861 // Load product
7862 $product = new Product($db);
7863 $result = $product->fetch($idprod);
7864
7865 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7866 /* Not defined yet, so we don't use this
7867 if ($local==1) $ret=$product->localtax1_tx;
7868 elseif ($local==2) $ret=$product->localtax2_tx;
7869 $found=1;
7870 */
7871 } else {
7872 // TODO Read default product vat according to product and another countrycode.
7873 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7874 }
7875 }
7876
7877 if (!$found) {
7878 // If vat of product for the country not found or not defined, we return higher vat of country.
7879 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7880 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7881 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7882 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7883 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7884 $sql .= $db->plimit(1);
7885
7886 $resql = $db->query($sql);
7887 if ($resql) {
7888 $obj = $db->fetch_object($resql);
7889 if ($obj) {
7890 if ($local == 1) {
7891 $ret = $obj->localtax1;
7892 } elseif ($local == 2) {
7893 $ret = $obj->localtax2;
7894 }
7895 }
7896 } else {
7897 dol_print_error($db);
7898 }
7899 }
7900
7901 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7902 return $ret;
7903}
7904
7922function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7923{
7924 global $mysoc, $db;
7925
7926 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7927
7928 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7929 $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;
7930
7931 if (empty($thirdparty_seller->country_code)) {
7932 $thirdparty_seller->country_code = $mysoc->country_code;
7933 }
7934 $seller_country_code = $thirdparty_seller->country_code;
7935 $seller_in_cee = isInEEC($thirdparty_seller);
7936
7937 if (empty($thirdparty_buyer->country_code)) {
7938 $thirdparty_buyer->country_code = $mysoc->country_code;
7939 }
7940 $buyer_country_code = $thirdparty_buyer->country_code;
7941 $buyer_in_cee = isInEEC($thirdparty_buyer);
7942
7943 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'));
7944
7945 // 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)
7946 // we use the buyer VAT.
7947 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7948 if ($seller_in_cee && $buyer_in_cee) {
7949 $isacompany = $thirdparty_buyer->isACompany();
7950 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7951 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7952 if (!isValidVATID($thirdparty_buyer)) {
7953 $isacompany = 0;
7954 }
7955 }
7956
7957 if (!$isacompany) {
7958 //print 'VATRULE 0';
7959 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7960 }
7961 }
7962 }
7963
7964 // If seller does not use VAT, default VAT is 0. End of rule.
7965 if (!$seller_use_vat) {
7966 //print 'VATRULE 1';
7967 return 0;
7968 }
7969
7970 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
7971 if (!empty($thirdparty_buyer->state_id)) {
7972 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
7973 $sql .= " FROM ".$db->prefix()."c_tva as t";
7974 $sql .= " INNER JOIN ".$db->prefix()."c_departements as d ON t.fk_department_buyer = d.rowid";
7975 $sql .= " WHERE d.rowid = ".((int) $thirdparty_buyer->state_id);
7976 $sql .= " AND t.active > 0";
7977 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7978 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7979
7980 $res = $db->query($sql);
7981 if ($res) {
7982 if ($db->num_rows($res)) {
7983 $obj = $db->fetch_object($res);
7984 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7985 }
7986 $db->free($res);
7987 }
7988 }
7989
7990 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7991 if (($seller_country_code == $buyer_country_code)
7992 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7993 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7994 ) { // Warning ->country_code not always defined
7995 //print 'VATRULE 3';
7996 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7997
7998 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7999 // Special case for india.
8000 //print 'VATRULE 3b';
8001 $reg = array();
8002 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
8003 // we must revert the C+S into I
8004 $tmpvat = str_replace("C+S", "I", $tmpvat);
8005 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8006 // we must revert the I into C+S
8007 $tmpvat = str_replace("I", "C+S", $tmpvat);
8008 }
8009 }
8010
8011 return $tmpvat;
8012 }
8013
8014 // 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.
8015 // 'VATRULE 4' - Not supported
8016
8017 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8018 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8019 if (($seller_in_cee && $buyer_in_cee)) {
8020 $isacompany = $thirdparty_buyer->isACompany();
8021 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
8022 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
8023 if (!isValidVATID($thirdparty_buyer)) {
8024 $isacompany = 0;
8025 }
8026 }
8027
8028 if (!$isacompany) {
8029 //print 'VATRULE 5';
8030 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8031 } else {
8032 //print 'VATRULE 6';
8033 return 0;
8034 }
8035 }
8036
8037 // 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
8038 // I don't see any use case that need this rule.
8039 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8040 $isacompany = $thirdparty_buyer->isACompany();
8041 if (!$isacompany) {
8042 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8043 //print 'VATRULE extra';
8044 }
8045 }
8046
8047 // Otherwise the VAT proposed by default=0. End of rule.
8048 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8049 //print 'VATRULE 7';
8050 return 0;
8051}
8052
8053
8064function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8065{
8066 global $db;
8067
8068 if ($idprodfournprice > 0) {
8069 if (!class_exists('ProductFournisseur')) {
8070 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8071 }
8072 $prodprice = new ProductFournisseur($db);
8073 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8074 return $prodprice->fourn_tva_npr;
8075 } elseif ($idprod > 0) {
8076 if (!class_exists('Product')) {
8077 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8078 }
8079 $prod = new Product($db);
8080 $prod->fetch($idprod);
8081 return $prod->tva_npr;
8082 }
8083
8084 return 0;
8085}
8086
8100function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8101{
8102 global $mysoc;
8103
8104 if (!is_object($thirdparty_seller)) {
8105 return -1;
8106 }
8107 if (!is_object($thirdparty_buyer)) {
8108 return -1;
8109 }
8110
8111 if (empty($thirdparty_seller->country_code)) {
8112 $thirdparty_seller->country_code = $mysoc->country_code;
8113 }
8114 $seller_country_code = $thirdparty_seller->country_code;
8115 //$seller_in_cee = isInEEC($thirdparty_seller);
8116
8117 if (empty($thirdparty_buyer->country_code)) {
8118 $thirdparty_buyer->country_code = $mysoc->country_code;
8119 }
8120 $buyer_country_code = $thirdparty_buyer->country_code;
8121 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8122
8123 if ($local == 1) { // Localtax 1
8124 if ($mysoc->country_code == 'ES') {
8125 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8126 return 0;
8127 }
8128 } else {
8129 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8130 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8131 return 0;
8132 }
8133 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8134 return 0;
8135 }
8136 }
8137 } elseif ($local == 2) { //I Localtax 2
8138 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8139 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8140 return 0;
8141 }
8142 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8143 return 0;
8144 }
8145 }
8146
8147 if ($seller_country_code == $buyer_country_code) {
8148 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8149 }
8150
8151 return 0;
8152}
8153
8162function yn($yesno, $format = 1, $color = 0)
8163{
8164 global $langs;
8165
8166 $result = 'unknown';
8167 $classname = '';
8168 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
8169 $result = $langs->trans('yes');
8170 if ($format == 1 || $format == 3) {
8171 $result = $langs->trans("Yes");
8172 }
8173 if ($format == 2) {
8174 $result = '<input type="checkbox" value="1" checked disabled>';
8175 }
8176 if ($format == 3) {
8177 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
8178 }
8179 if ($format == 4 || !is_numeric($format)) {
8180 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8181 }
8182
8183 $classname = 'ok';
8184 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
8185 $result = $langs->trans("no");
8186 if ($format == 1 || $format == 3) {
8187 $result = $langs->trans("No");
8188 }
8189 if ($format == 2) {
8190 $result = '<input type="checkbox" value="0" disabled>';
8191 }
8192 if ($format == 3) {
8193 $result = '<input type="checkbox" value="0" disabled> '.$result;
8194 }
8195 if ($format == 4 || !is_numeric($format)) {
8196 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8197 }
8198
8199 if ($color == 2) {
8200 $classname = 'ok';
8201 } else {
8202 $classname = 'error';
8203 }
8204 }
8205 if ($color) {
8206 return '<span class="'.$classname.'">'.$result.'</span>';
8207 }
8208 return $result;
8209}
8210
8229function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8230{
8231 if (empty($modulepart) && is_object($object)) {
8232 if (!empty($object->module)) {
8233 $modulepart = $object->module;
8234 } elseif (!empty($object->element)) {
8235 $modulepart = $object->element;
8236 }
8237 }
8238
8239 $path = '';
8240
8241 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8242 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8243 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8244 $arrayforoldpath['product'] = 2;
8245 }
8246
8247 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8248 $level = $arrayforoldpath[$modulepart];
8249 }
8250 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8251 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8252 if (empty($num) && is_object($object)) {
8253 $num = $object->id;
8254 }
8255 if (empty($alpha)) {
8256 $num = preg_replace('/([^0-9])/i', '', $num);
8257 } else {
8258 $num = preg_replace('/^.*\-/i', '', $num);
8259 }
8260 $num = substr("000".$num, -$level);
8261 if ($level == 1) {
8262 $path = substr($num, 0, 1);
8263 }
8264 if ($level == 2) {
8265 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
8266 }
8267 if ($level == 3) {
8268 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
8269 }
8270 } else {
8271 // We will enhance here a common way of forging path for document storage.
8272 // In a future, we may distribute directories on several levels depending on setup and object.
8273 // Here, $object->id, $object->ref and $modulepart are required.
8274 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
8275 // 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
8276 $path = dol_sanitizeFileName((string) $object->id);
8277 } else {
8278 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8279 }
8280 }
8281
8282 if (empty($withoutslash) && !empty($path)) {
8283 $path .= '/';
8284 }
8285
8286 return $path;
8287}
8288
8297function dol_mkdir($dir, $dataroot = '', $newmask = '')
8298{
8299 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
8300
8301 $dir = dol_sanitizePathName($dir, '_', 0);
8302
8303 $dir_osencoded = dol_osencode($dir);
8304 if (@is_dir($dir_osencoded)) {
8305 return 0;
8306 }
8307
8308 $nberr = 0;
8309 $nbcreated = 0;
8310
8311 $ccdir = '';
8312 if (!empty($dataroot)) {
8313 // Remove data root from loop
8314 $dir = str_replace($dataroot.'/', '', $dir);
8315 $ccdir = $dataroot.'/';
8316 }
8317
8318 $cdir = explode("/", $dir);
8319 $num = count($cdir);
8320 for ($i = 0; $i < $num; $i++) {
8321 if ($i > 0) {
8322 $ccdir .= '/'.$cdir[$i];
8323 } else {
8324 $ccdir .= $cdir[$i];
8325 }
8326 $regs = array();
8327 if (preg_match("/^.:$/", $ccdir, $regs)) {
8328 continue; // If the Windows path is incomplete, continue with next directory
8329 }
8330
8331 // Attention, is_dir() can fail event if the directory exists
8332 // (i.e. according the open_basedir configuration)
8333 if ($ccdir) {
8334 $ccdir_osencoded = dol_osencode($ccdir);
8335 if (!@is_dir($ccdir_osencoded)) {
8336 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8337
8338 umask(0);
8339 $dirmaskdec = octdec((string) $newmask);
8340 if (empty($newmask)) {
8341 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8342 }
8343 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8344 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8345 // If the is_dir has returned a false information, we arrive here
8346 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' (no permission to write into parent or directory already exists).", LOG_WARNING);
8347 $nberr++;
8348 } else {
8349 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
8350 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8351 $nbcreated++;
8352 }
8353 } else {
8354 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8355 }
8356 }
8357 }
8358 return ($nberr ? -$nberr : $nbcreated);
8359}
8360
8361
8369function dolChmod($filepath, $newmask = '')
8370{
8371 if (!empty($newmask)) {
8372 @chmod($filepath, octdec($newmask));
8373 } elseif (getDolGlobalString('MAIN_UMASK')) {
8374 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8375 }
8376}
8377
8378
8384function picto_required()
8385{
8386 return '<span class="fieldrequired">*</span>';
8387}
8388
8389
8406function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8407{
8408 if (is_null($stringtoclean)) {
8409 return '';
8410 }
8411
8412 if ($removelinefeed == 2) {
8413 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8414 }
8415 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8416
8417 // 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)
8418 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8419
8420 $temp = str_replace('< ', '__ltspace__', $temp);
8421 $temp = str_replace('<:', '__lttwopoints__', $temp);
8422
8423 if ($strip_tags) {
8424 $temp = strip_tags($temp);
8425 } else {
8426 // 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).
8427 $pattern = "/<[^<>]+>/";
8428 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8429 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8430 // pass 2 - $temp after pass 2: 0000-021
8431 $tempbis = $temp;
8432 do {
8433 $temp = $tempbis;
8434 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8435 $tempbis = preg_replace($pattern, '', $tempbis);
8436 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8437 } while ($tempbis != $temp);
8438
8439 $temp = $tempbis;
8440
8441 // 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).
8442 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8443 }
8444
8445 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8446
8447 // Remove also carriage returns
8448 if ($removelinefeed == 1) {
8449 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8450 }
8451
8452 // And double spaces
8453 if ($removedoublespaces) {
8454 while (strpos($temp, " ") !== false) {
8455 $temp = str_replace(" ", " ", $temp);
8456 }
8457 }
8458
8459 $temp = str_replace('__ltspace__', '< ', $temp);
8460 $temp = str_replace('__lttwopoints__', '<:', $temp);
8461
8462 return trim($temp);
8463}
8464
8484function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
8485{
8486 if (empty($allowed_tags)) {
8487 $allowed_tags = array(
8488 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8489 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8490 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8491 );
8492 }
8493 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8494 if ($allowiframe) {
8495 if (!in_array('iframe', $allowed_tags)) {
8496 $allowed_tags[] = "iframe";
8497 }
8498 }
8499 if ($allowlink) {
8500 if (!in_array('link', $allowed_tags)) {
8501 $allowed_tags[] = "link";
8502 }
8503 }
8504 if ($allowscript) {
8505 if (!in_array('script', $allowed_tags)) {
8506 $allowed_tags[] = "script";
8507 }
8508 }
8509 if ($allowstyle) {
8510 if (!in_array('style', $allowed_tags)) {
8511 $allowed_tags[] = "style";
8512 }
8513 }
8514
8515 $allowed_tags_string = implode("><", $allowed_tags);
8516 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8517
8518 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8519
8520 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8521
8522 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8523 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8524
8525 if ($allowphp) {
8526 $allowed_tags[] = "commentphp";
8527 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
8528 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
8529 }
8530
8531 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8532 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8533
8534 // Remove all HTML tags
8535 $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
8536
8537 if ($cleanalsosomestyles) { // Clean for remaining html tags
8538 $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
8539 }
8540 if ($removeclassattribute) { // Clean for remaining html tags
8541 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8542 }
8543
8544 // Remove 'javascript:' that we should not find into a text
8545 // 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)).
8546 if ($cleanalsojavascript) {
8547 $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);
8548 }
8549
8550 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8551
8552 if ($allowphp) {
8553 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
8554 }
8555
8556 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8557
8558
8559 return $temp;
8560}
8561
8562
8575function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8576{
8577 if (is_null($allowed_attributes)) {
8578 $allowed_attributes = array(
8579 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8580 // HTML5
8581 "header", "footer", "nav", "section", "menu", "menuitem"
8582 );
8583 }
8584 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8585 if (!in_array("content", $allowed_attributes)) {
8586 $allowed_attributes[] = "content";
8587 }
8588 if (!in_array("http-equiv", $allowed_attributes)) {
8589 $allowed_attributes[] = "http-equiv";
8590 }
8591
8592 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8593 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8594 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8595
8596 // Warning: loadHTML does not support HTML5 on old libxml versions.
8597 $dom = new DOMDocument('', 'UTF-8');
8598 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8599 $savwarning = error_reporting();
8600 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8601 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8602 error_reporting($savwarning);
8603
8604 if ($dom instanceof DOMDocument) {
8605 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8606 $el = $els->item($i);
8607 if (!$el instanceof DOMElement) {
8608 continue;
8609 }
8610 $attrs = $el->attributes;
8611 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8612 //var_dump($attrs->item($ii));
8613 if (!empty($attrs->item($ii)->name)) {
8614 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8615 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8616 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8617 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8618 // If attribute is 'style'
8619 $valuetoclean = $attrs->item($ii)->value;
8620
8621 if (isset($valuetoclean)) {
8622 do {
8623 $oldvaluetoclean = $valuetoclean;
8624 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8625 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8626 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8627 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8628 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8629 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8630 }
8631
8632 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8633 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8634 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8635 } while ($oldvaluetoclean != $valuetoclean);
8636 }
8637
8638 $attrs->item($ii)->value = $valuetoclean;
8639 }
8640 }
8641 }
8642 }
8643 }
8644
8645 $dom->encoding = 'UTF-8';
8646
8647 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8648 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8649
8650 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8651 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8652 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8653
8654 return trim($return);
8655 } else {
8656 return $stringtoclean;
8657 }
8658}
8659
8671function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8672{
8673 $temp = $stringtoclean;
8674 foreach ($disallowed_tags as $tagtoremove) {
8675 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8676 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8677 }
8678
8679 if ($cleanalsosomestyles) {
8680 $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
8681 }
8682
8683 return $temp;
8684}
8685
8686
8696function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8697{
8698 if ($nboflines == 1) {
8699 if (dol_textishtml($text)) {
8700 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8701 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8702 } else {
8703 if (isset($text)) {
8704 $firstline = preg_replace('/[\n\r].*/', '', $text);
8705 } else {
8706 $firstline = '';
8707 }
8708 }
8709 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8710 } else {
8711 $ishtml = 0;
8712 if (dol_textishtml($text)) {
8713 $text = preg_replace('/\n/', '', $text);
8714 $ishtml = 1;
8715 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8716 } else {
8717 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8718 }
8719
8720 $text = strtr($text, $repTable);
8721 if ($charset == 'UTF-8') {
8722 $pattern = '/(<br[^>]*>)/Uu';
8723 } else {
8724 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8725 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8726 }
8727 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8728
8729 $firstline = '';
8730 $i = 0;
8731 $countline = 0;
8732 $lastaddediscontent = 1;
8733 while ($countline < $nboflines && isset($a[$i])) {
8734 if (preg_match('/<br[^>]*>/', $a[$i])) {
8735 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8736 $firstline .= ($ishtml ? "<br>\n" : "\n");
8737 // Is it a br for a new line of after a printed line ?
8738 if (!$lastaddediscontent) {
8739 $countline++;
8740 }
8741 $lastaddediscontent = 0;
8742 }
8743 } else {
8744 $firstline .= $a[$i];
8745 $lastaddediscontent = 1;
8746 $countline++;
8747 }
8748 $i++;
8749 }
8750
8751 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8752 //unset($a);
8753 $ret = $firstline.($adddots ? '...' : '');
8754 //exit;
8755 return $ret;
8756 }
8757}
8758
8759
8771function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8772{
8773 if (is_null($stringtoencode)) {
8774 return '';
8775 }
8776
8777 if (!$nl2brmode) {
8778 return nl2br($stringtoencode, $forxml);
8779 } else {
8780 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8781 return $ret;
8782 }
8783}
8784
8794function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8795{
8796 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8797 // TODO using sandbox on inline html content is not possible yet with current browsers
8798 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8799 //$s .= $stringtoencode;
8800 //$s .= '</body></html></iframe>';
8801 return $stringtoencode;
8802 } else {
8803 $out = $stringtoencode;
8804
8805 // First clean HTML content
8806 do {
8807 $oldstringtoclean = $out;
8808
8809 $outishtml = 0;
8810 if (dol_textishtml($out)) {
8811 $outishtml = 1;
8812 }
8813
8814 // HTML sanitizer by DOMDocument
8815 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8816 try {
8817 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8818 if (LIBXML_VERSION < 20900) {
8819 // Avoid load of external entities (security problem).
8820 // Required only if LIBXML_VERSION < 20900
8821 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8822 libxml_disable_entity_loader(true);
8823 }
8824
8825 $dom = new DOMDocument();
8826 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
8827 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8828 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8829 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
8830 // I don't know what the xml encoding is the trick for
8831 if ($outishtml) {
8832 //$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>';
8833 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8834 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8835 } else {
8836 //$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>';
8837 $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>';
8838 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
8839 }
8840
8841 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8842
8843 $dom->encoding = 'UTF-8';
8844
8845 $out = trim($dom->saveHTML());
8846
8847 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8848 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8849 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8850 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8851 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8852 // $out = preg_replace('/<\/div>$/', '', $out);
8853 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8854
8855 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
8856 $out = str_replace('<br>', '', $out);
8857 }
8858 } catch (Exception $e) {
8859 // If error, invalid HTML string with no way to clean it
8860 //print $e->getMessage();
8861 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8862 }
8863 }
8864
8865 // HTML sanitizer by Tidy
8866 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8867 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
8868 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
8869 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8870 try {
8871 //var_dump($out);
8872
8873 // Try cleaning using tidy
8874 if (extension_loaded('tidy') && class_exists("tidy")) {
8875 //print "aaa".$out."\n";
8876
8877 // See options at https://tidy.sourceforge.net/docs/quickref.html
8878 $config = array(
8879 'clean' => false,
8880 // 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;
8881 'quote-marks' => false,
8882 'doctype' => 'strict',
8883 'show-body-only' => true,
8884 "indent-attributes" => false,
8885 "vertical-space" => false,
8886 //'ident' => false, // Not always supported
8887 "wrap" => 0,
8888 'preserve-entities' => true
8889 // HTML5 tags
8890 //'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',
8891 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8892 //'new-empty-tags' => 'command embed keygen source track wbr',
8893 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8894 );
8895
8896 // Tidy
8897 $tidy = new tidy();
8898 $out = $tidy->repairString($out, $config, 'utf8');
8899
8900 //print "xxx".$out;exit;
8901 }
8902
8903 //var_dump($out);
8904 } catch (Exception $e) {
8905 // If error, invalid HTML string with no way to clean it
8906 //print $e->getMessage();
8907 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8908 }
8909 }
8910
8911 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8912 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8913
8914 // Clean some html entities that are useless so text is cleaner
8915 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8916
8917 // Ckeditor uses the numeric entity for apostrophe, so we force it to
8918 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
8919 $out = preg_replace('/&#39;/i', '&apos;', $out);
8920
8921 // 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).
8922 // 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
8923 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8924 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
8925 $out = preg_replace_callback(
8926 '/&#(x?[0-9][0-9a-f]+;?)/i',
8931 static function ($m) {
8932 return realCharForNumericEntities($m);
8933 },
8934 $out
8935 );
8936 }
8937
8938 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8939 $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'.
8940
8941 // Keep only some html tags and remove also some 'javascript:' strings
8942 if ($check == 'restricthtmlallowlinkscript') {
8943 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
8944 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8945 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8946 } elseif ($check == 'restricthtmlallowiframe') {
8947 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
8948 } else {
8949 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8950 }
8951
8952 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8953 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8955 }
8956
8957 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
8958 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
8959 $out = preg_replace('/&apos;/i', "&#39;", $out);
8960
8961 // Now remove js
8962 // 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
8963 $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)>
8964 $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);
8965 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8966 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8967 $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);
8968 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8969 // More not into the previous list
8970 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8971 } while ($oldstringtoclean != $out);
8972
8973 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8974 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8975 // 'url(' to avoid inline style like background: url(http...
8976 // '<link' to avoid <link href="http...">
8977 $reg = array();
8978 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8979 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8980 $nblinks = count($reg[0]);
8981 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8982 $out = 'ErrorTooManyLinksIntoHTMLString';
8983 }
8984
8985 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8986 if ($nblinks > 0) {
8987 $out = 'ErrorHTMLLinksNotAllowed';
8988 }
8989 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8990 $nblinks = 0;
8991 // Loop on each url in src= and url(
8992 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8993
8994 $matches = array();
8995 if (preg_match_all($pattern, $out, $matches)) {
8996 // URLs are into $matches[1]
8997 $urls = $matches[1];
8998
8999 // Affiche les URLs
9000 foreach ($urls as $url) {
9001 $nblinks++;
9002 echo "Found url = ".$url . "\n";
9003 }
9004 if ($nblinks > 0) {
9005 $out = 'ErrorHTMLExternalLinksNotAllowed';
9006 }
9007 }
9008 }
9009
9010 return $out;
9011 }
9012}
9013
9034function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9035{
9036 if (is_null($stringtoencode)) {
9037 return '';
9038 }
9039
9040 $newstring = $stringtoencode;
9041 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9042 $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.
9043 if ($removelasteolbr) {
9044 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9045 }
9046 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9047 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9048 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9049 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9050 } else {
9051 if ($removelasteolbr) {
9052 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9053 }
9054 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9055 }
9056 // Other substitutions that htmlentities does not do
9057 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9058 return $newstring;
9059}
9060
9068function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9069{
9070 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9071 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9072 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
9073 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
9074 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9075 return $ret;
9076}
9077
9084function dol_htmlcleanlastbr($stringtodecode)
9085{
9086 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9087 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
9088 return $ret;
9089}
9090
9100function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9101{
9102 $newstring = $a;
9103 if ($keepsomeentities) {
9104 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9105 }
9106 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9107 if ($keepsomeentities) {
9108 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9109 }
9110 return $newstring;
9111}
9112
9124function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9125{
9126 return htmlentities($string, $flags, $encoding, $double_encode);
9127}
9128
9140function dol_string_is_good_iso($s, $clean = 0)
9141{
9142 $len = dol_strlen($s);
9143 $out = '';
9144 $ok = 1;
9145 for ($scursor = 0; $scursor < $len; $scursor++) {
9146 $ordchar = ord($s[$scursor]);
9147 //print $scursor.'-'.$ordchar.'<br>';
9148 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9149 $ok = 0;
9150 break;
9151 } elseif ($ordchar > 126 && $ordchar < 160) {
9152 $ok = 0;
9153 break;
9154 } elseif ($clean) {
9155 $out .= $s[$scursor];
9156 }
9157 }
9158 if ($clean) {
9159 return $out;
9160 }
9161 return $ok;
9162}
9163
9172function dol_nboflines($s, $maxchar = 0)
9173{
9174 if ($s == '') {
9175 return 0;
9176 }
9177 $arraystring = explode("\n", $s);
9178 $nb = count($arraystring);
9179
9180 return $nb;
9181}
9182
9183
9193function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9194{
9195 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9196 if (dol_textishtml($text)) {
9197 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9198 }
9199
9200 $text = strtr($text, $repTable);
9201 if ($charset == 'UTF-8') {
9202 $pattern = '/(<br[^>]*>)/Uu';
9203 } else {
9204 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9205 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9206 }
9207 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9208
9209 $nblines = (int) floor((count($a) + 1) / 2);
9210 // count possible auto line breaks
9211 if ($maxlinesize) {
9212 foreach ($a as $line) {
9213 if (dol_strlen($line) > $maxlinesize) {
9214 //$line_dec = html_entity_decode(strip_tags($line));
9215 $line_dec = html_entity_decode($line);
9216 if (dol_strlen($line_dec) > $maxlinesize) {
9217 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9218 $nblines += substr_count($line_dec, '\n');
9219 }
9220 }
9221 }
9222 }
9223
9224 unset($a);
9225 return $nblines;
9226}
9227
9236function dol_textishtml($msg, $option = 0)
9237{
9238 if (is_null($msg)) {
9239 return false;
9240 }
9241
9242 if ($option == 1) {
9243 if (preg_match('/<(html|link|script)/i', $msg)) {
9244 return true;
9245 } elseif (preg_match('/<body/i', $msg)) {
9246 return true;
9247 } elseif (preg_match('/<\/textarea/i', $msg)) {
9248 return true;
9249 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9250 return true;
9251 } elseif (preg_match('/<br/i', $msg)) {
9252 return true;
9253 }
9254 return false;
9255 } else {
9256 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9257 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9258 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9259 return true;
9260 } elseif (preg_match('/<\/textarea/i', $msg)) {
9261 return true;
9262 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9263 return true;
9264 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9265 return true;
9266 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9267 return true;
9268 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9269 return true;
9270 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9271 return true; // must accept <img src="http://example.com/aaa.png" />
9272 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9273 return true; // must accept <a href="http://example.com/aaa.png" />
9274 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9275 return true;
9276 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9277 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9278 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9279 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9280 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9281 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
9282 return true; // Html entities numbers in hexa
9283 }
9284
9285 return false;
9286 }
9287}
9288
9303function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
9304{
9305 if (!empty($invert)) {
9306 $tmp = $text1;
9307 $text1 = $text2;
9308 $text2 = $tmp;
9309 }
9310
9311 $ret = '';
9312 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
9313 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
9314 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
9315 return $ret;
9316}
9317
9318
9319
9333function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
9334{
9335 global $db, $conf, $mysoc, $user, $extrafields;
9336
9337 $substitutionarray = array();
9338
9339 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
9340 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
9341 // this will include signature content first and then replace var found into content of signature
9342 //var_dump($onlykey);
9343 $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()
9344 $usersignature = $user->signature;
9345 $substitutionarray = array_merge($substitutionarray, array(
9346 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
9347 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
9348 ));
9349
9350 if (is_object($user) && ($user instanceof User)) {
9351 $substitutionarray = array_merge($substitutionarray, array(
9352 '__USER_ID__' => (string) $user->id,
9353 '__USER_LOGIN__' => (string) $user->login,
9354 '__USER_EMAIL__' => (string) $user->email,
9355 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
9356 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
9357 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
9358 '__USER_FAX__' => (string) $user->office_fax,
9359 '__USER_LASTNAME__' => (string) $user->lastname,
9360 '__USER_FIRSTNAME__' => (string) $user->firstname,
9361 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
9362 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
9363 '__USER_JOB__' => (string) $user->job,
9364 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
9365 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
9366 ));
9367 }
9368 }
9369 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
9370 $substitutionarray = array_merge($substitutionarray, array(
9371 '__MYCOMPANY_NAME__' => $mysoc->name,
9372 '__MYCOMPANY_EMAIL__' => $mysoc->email,
9373 '__MYCOMPANY_URL__' => $mysoc->url,
9374 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
9375 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
9376 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
9377 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
9378 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
9379 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
9380 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
9381 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
9382 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
9383 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
9384 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
9385 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
9386 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
9387 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
9388 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
9389 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
9390 '__MYCOMPANY_ZIP__' => $mysoc->zip,
9391 '__MYCOMPANY_TOWN__' => $mysoc->town,
9392 '__MYCOMPANY_STATE__' => $mysoc->state,
9393 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
9394 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
9395 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
9396 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
9397 ));
9398 }
9399
9400 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
9401 if ($onlykey) {
9402 $substitutionarray['__ID__'] = '__ID__';
9403 $substitutionarray['__REF__'] = '__REF__';
9404 $substitutionarray['__NEWREF__'] = '__NEWREF__';
9405 $substitutionarray['__LABEL__'] = '__LABEL__';
9406 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
9407 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
9408 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
9409 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
9410 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
9411
9412 if (isModEnabled("societe")) { // Most objects are concerned
9413 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
9414 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
9415 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
9416 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
9417 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
9418 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
9419 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
9420 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
9421 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
9422 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
9423 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
9424 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
9425 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
9426 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
9427 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
9428 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
9429 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
9430 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
9431 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
9432 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
9433 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
9434 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
9435 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
9436 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
9437 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
9438 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
9439 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
9440 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
9441 }
9442 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9443 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9444 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9445 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9446 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9447 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9448 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9449 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9450 }
9451 // add substitution variables for ticket
9452 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9453 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9454 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9455 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9456 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9457 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9458 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9459 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9460 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9461 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9462 }
9463
9464 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9465 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9466 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9467 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9468 }
9469 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9470 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9471 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9472 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9473 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9474 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9475 }
9476 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9477 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9478 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9479 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9480 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9481 }
9482 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9483 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9484 }
9485 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9486 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9487 }
9488 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9489 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9490 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9491 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9492 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9493 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9494 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9495
9496 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9497 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9498 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9499 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9500 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9501
9502 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9503 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9504 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9505 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9506 }
9507 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9508 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9509 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9510 }
9511 } else {
9512 '@phan-var-force Adherent|Delivery $object';
9514 $substitutionarray['__ID__'] = $object->id;
9515 $substitutionarray['__REF__'] = $object->ref;
9516 $substitutionarray['__NEWREF__'] = $object->newref;
9517 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9518 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9519 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9520 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9521 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9522
9523 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9524 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9525 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9526
9527 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
9528 $date_delivery = null;
9529 if (property_exists($object, 'date_delivery')) {
9530 $date_delivery = $object->date_delivery;
9531 } elseif (property_exists($object, 'delivery_date')) {
9532 $date_delivery = $object->delivery_date;
9533 }
9534 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9535 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
9536 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
9537 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
9538 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
9539 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
9540 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
9541 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
9542 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
9543
9544 // For backward compatibility (deprecated)
9545 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9546 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9547
9548 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9549 $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 : '')) : '');
9550 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9551
9552 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9553 '@phan-var-force Adherent $object';
9555 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9556
9557 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9558 if (method_exists($object, 'getCivilityLabel')) {
9559 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9560 }
9561 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9562 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9563 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9564 if (method_exists($object, 'getFullName')) {
9565 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9566 }
9567 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9568 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9569 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9570 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9571 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9572 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9573 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9574 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9575 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9576 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9577 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9578 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9579 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9580 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9581 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9582 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9583
9584 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9585 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9586 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9587 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9588 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9589 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9590 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9591 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9592 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9593 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9594 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9595 }
9596
9597 if (is_object($object) && $object->element == 'societe') {
9599 '@phan-var-force Societe $object';
9600 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
9601 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
9602 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
9603 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
9604 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
9605 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
9606 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
9607 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
9608 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
9609 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
9610 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
9611 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
9612 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
9613 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
9614 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
9615 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ?: '');
9616 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
9617 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
9618 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
9619 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
9620 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
9621 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
9622 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
9623 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
9624 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
9625 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
9626 } elseif (is_object($object) && is_object($object->thirdparty)) {
9627 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
9628 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
9629 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
9630 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
9631 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
9632 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
9633 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
9634 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
9635 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
9636 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
9637 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
9638 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
9639 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
9640 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ?: '');
9641 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
9642 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
9643 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
9644 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
9645 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
9646 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
9647 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
9648 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
9649 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
9650 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
9651 }
9652
9653 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9654 '@phan-var-force RecruitmentCandidature $object';
9656 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9657 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9658 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9659 }
9660 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9661 '@phan-var-force ConferenceOrBoothAttendee $object';
9663 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9664 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9665 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9666 }
9667
9668 if (is_object($object) && $object->element == 'project') {
9669 '@phan-var-force Project $object';
9671 $substitutionarray['__PROJECT_ID__'] = $object->id;
9672 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9673 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9674 } elseif (is_object($object)) {
9675 $project = null;
9676 if (!empty($object->project)) {
9677 $project = $object->project;
9678 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9679 $project = $object->projet;
9680 }
9681 if (!is_null($project) && is_object($project)) {
9682 $substitutionarray['__PROJECT_ID__'] = $project->id;
9683 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9684 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9685 } else {
9686 // can substitute variables for project : uses lazy load in "make_substitutions" method
9687 $project_id = 0;
9688 if (!empty($object->fk_project) && $object->fk_project > 0) {
9689 $project_id = $object->fk_project;
9690 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9691 $project_id = $object->fk_project;
9692 }
9693 if ($project_id > 0) {
9694 // path:class:method:id
9695 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9696 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9697 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9698 }
9699 }
9700 }
9701
9702 if (is_object($object) && $object->element == 'facture') {
9703 '@phan-var-force Facture $object';
9705 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9706 }
9707 if (is_object($object) && $object->element == 'shipping') {
9708 '@phan-var-force Expedition $object';
9710 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9711 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9712 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9713 }
9714 if (is_object($object) && $object->element == 'reception') {
9715 '@phan-var-force Reception $object';
9717 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9718 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9719 }
9720
9721 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9722 '@phan-var-force Contrat $object';
9724 $dateplannedstart = '';
9725 $datenextexpiration = '';
9726 foreach ($object->lines as $line) {
9727 if ($line->date_start > $dateplannedstart) {
9728 $dateplannedstart = $line->date_start;
9729 }
9730 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9731 $datenextexpiration = $line->date_end;
9732 }
9733 }
9734 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9735 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9736 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9737
9738 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9739 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9740 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9741 }
9742 // add substitution variables for ticket
9743 if (is_object($object) && $object->element == 'ticket') {
9744 '@phan-var-force Ticket $object';
9746 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9747 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9748 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9749 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9750 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9751 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9752 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9753 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9754 $userstat = new User($db);
9755 if ($object->fk_user_assign > 0) {
9756 $userstat->fetch($object->fk_user_assign);
9757 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9758 }
9759
9760 if ($object->fk_user_create > 0) {
9761 $userstat->fetch($object->fk_user_create);
9762 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9763 }
9764 }
9765
9766 // Create dynamic tags for __EXTRAFIELD_FIELD__
9767 if ($object->table_element && $object->id > 0) {
9768 if (!is_object($extrafields)) {
9769 $extrafields = new ExtraFields($db);
9770 }
9771 $extrafields->fetch_name_optionals_label($object->table_element, true);
9772
9773 if ($object->fetch_optionals() > 0) {
9774 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9775 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9776 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9777 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9778 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9779 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9780 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9781 $datetime = $object->array_options['options_'.$key];
9782 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9783 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9784 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9785 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9786 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9787 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9788 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9789 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9790 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9791 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9792 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9793 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9794 }
9795 }
9796 }
9797 }
9798 }
9799
9800 // Complete substitution array with the url to make online payment
9801 if (empty($substitutionarray['__REF__'])) {
9802 $paymenturl = '';
9803 } else {
9804 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9805 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9806 $outputlangs->loadLangs(array('paypal', 'other'));
9807
9808 $amounttouse = 0;
9809 $typeforonlinepayment = 'free';
9810 if (is_object($object) && $object->element == 'commande') {
9811 $typeforonlinepayment = 'order';
9812 }
9813 if (is_object($object) && $object->element == 'facture') {
9814 $typeforonlinepayment = 'invoice';
9815 }
9816 if (is_object($object) && $object->element == 'member') {
9817 $typeforonlinepayment = 'member';
9818 if (!empty($object->last_subscription_amount)) {
9819 $amounttouse = $object->last_subscription_amount;
9820 }
9821 }
9822 if (is_object($object) && $object->element == 'contrat') {
9823 $typeforonlinepayment = 'contract';
9824 }
9825 if (is_object($object) && $object->element == 'fichinter') {
9826 $typeforonlinepayment = 'ficheinter';
9827 }
9828
9829 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9830 $paymenturl = $url;
9831 }
9832
9833 if ($object->id > 0) {
9834 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9835 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9836
9837 // Show structured communication
9838 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9839 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9840 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9841 }
9842
9843 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9844 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9845 } else {
9846 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9847 }
9848 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9849 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9850 } else {
9851 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9852 }
9853 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9854 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9855 } else {
9856 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9857 }
9858 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9859 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9860 } else {
9861 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9862 }
9863 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9864 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9865 } else {
9866 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9867 }
9868 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9869 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9870 } else {
9871 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9872 }
9873
9874 if (is_object($object) && $object->element == 'propal') {
9875 '@phan-var-force Propal $object';
9877 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9878 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9879 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9880 }
9881 if (is_object($object) && $object->element == 'commande') {
9882 '@phan-var-force Commande $object';
9884 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9885 }
9886 if (is_object($object) && $object->element == 'facture') {
9887 '@phan-var-force Facture $object';
9889 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9890 }
9891 if (is_object($object) && $object->element == 'contrat') {
9892 '@phan-var-force Contrat $object';
9894 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9895 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9896 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9897 }
9898 if (is_object($object) && $object->element == 'fichinter') {
9899 '@phan-var-force Fichinter $object';
9901 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9902 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9903 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9904 }
9905 if (is_object($object) && $object->element == 'supplier_proposal') {
9906 '@phan-var-force SupplierProposal $object';
9908 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9909 }
9910 if (is_object($object) && $object->element == 'invoice_supplier') {
9911 '@phan-var-force FactureFournisseur $object';
9913 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9914 }
9915 if (is_object($object) && $object->element == 'shipping') {
9916 '@phan-var-force Expedition $object';
9918 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9919 }
9920 }
9921
9922 if (is_object($object) && $object->element == 'action') {
9923 '@phan-var-force ActionComm $object';
9925 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9926 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9927 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9928 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9929 }
9930 }
9931 }
9932 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9933 '@phan-var-force Facture|FactureRec $object';
9935 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9936
9937 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9938 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9939 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9940 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9941
9942 $already_payed_all = 0;
9943 if (is_object($object) && ($object instanceof Facture)) {
9944 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9945 }
9946
9947 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9948 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9949 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9950
9951 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9952 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9953 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9954
9955 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9956
9957 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9958 $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)) : '';
9959 $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)) : '';
9960
9961 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9962 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9963 }
9964 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9965 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9966 }
9967
9968 // Amount keys formatted in a currency
9969 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9970 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9971 $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) : '';
9972 $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)) : '';
9973 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9974 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9975 }
9976 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9977 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9978 }
9979 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9980 if ($onlykey != 2) {
9981 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9982 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9983 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9984 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9985 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9986 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9987 }
9988 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9989 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9990 }
9991 }
9992
9993 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9994 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9995 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9996 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9997 // TODO Add other keys for foreign multicurrency
9998
9999 // For backward compatibility
10000 if ($onlykey != 2) {
10001 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
10002 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
10003 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10004 }
10005 }
10006
10007
10008 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10009 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
10010
10011 $now = dol_now();
10012
10013 $tmp = dol_getdate($now, true);
10014 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10015 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10016 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10017 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10018
10019 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
10020
10021 $substitutionarray = array_merge($substitutionarray, array(
10022 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10023 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10024 '__DAY__' => (string) $tmp['mday'],
10025 '__DAY_TEXT__' => $daytext, // Monday
10026 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10027 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10028 '__MONTH__' => (string) $tmp['mon'],
10029 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
10030 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
10031 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
10032 '__YEAR__' => (string) $tmp['year'],
10033 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10034 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10035 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10036 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10037 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp3['month'])),
10038 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp3['month'])),
10039 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp3['month'])),
10040 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10041 '__NEXT_DAY__' => (string) $tmp4['day'],
10042 '__NEXT_MONTH__' => (string) $tmp5['month'],
10043 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
10044 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
10045 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
10046 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10047 ));
10048 }
10049
10050 if (isModEnabled('multicompany')) {
10051 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10052 }
10053 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10054 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10055 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10056 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
10057 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10058 }
10059
10060 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10061
10062 return $substitutionarray;
10063}
10064
10081function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10082{
10083 global $conf, $db, $langs;
10084
10085 if (!is_array($substitutionarray)) {
10086 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10087 }
10088
10089 if (empty($outputlangs)) {
10090 $outputlangs = $langs;
10091 }
10092
10093 // Is initial text HTML or simple text ?
10094 $msgishtml = 0;
10095 if (dol_textishtml($text, 1)) {
10096 $msgishtml = 1;
10097 }
10098
10099 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10100 if (is_object($outputlangs)) {
10101 $reg = array();
10102 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10103 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10104 $tmp = explode('|', $reg[1]);
10105 if (!empty($tmp[1])) {
10106 $outputlangs->load($tmp[1]);
10107 }
10108
10109 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10110
10111 if (empty($converttextinhtmlifnecessary)) {
10112 // convert $newval into HTML is necessary
10113 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10114 } else {
10115 if (! $msgishtml) {
10116 $valueishtml = dol_textishtml($value, 1);
10117 //var_dump("valueishtml=".$valueishtml);
10118
10119 if ($valueishtml) {
10120 $text = dol_htmlentitiesbr($text);
10121 $msgishtml = 1;
10122 }
10123 } else {
10124 $value = dol_nl2br((string) $value);
10125 }
10126
10127 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
10128 }
10129 }
10130 }
10131
10132 // Make substitution for constant keys.
10133 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10134 $reg = array();
10135 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10136 $keyfound = $reg[1];
10137 if (isASecretKey($keyfound)) {
10138 $value = '*****forbidden*****';
10139 } else {
10140 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
10141 }
10142
10143 if (empty($converttextinhtmlifnecessary)) {
10144 // convert $newval into HTML is necessary
10145 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10146 } else {
10147 if (! $msgishtml) {
10148 $valueishtml = dol_textishtml($value, 1);
10149
10150 if ($valueishtml) {
10151 $text = dol_htmlentitiesbr($text);
10152 $msgishtml = 1;
10153 }
10154 } else {
10155 $value = dol_nl2br((string) $value);
10156 }
10157
10158 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
10159 }
10160 }
10161
10162 // Make substitution for array $substitutionarray
10163 foreach ($substitutionarray as $key => $value) {
10164 if (!isset($value)) {
10165 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10166 }
10167
10168 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
10169 $value = ''; // Protection
10170 }
10171
10172 if (empty($converttextinhtmlifnecessary)) {
10173 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10174 } else {
10175 if (! $msgishtml) {
10176 $valueishtml = dol_textishtml($value, 1);
10177
10178 if ($valueishtml) {
10179 $text = dol_htmlentitiesbr($text);
10180 $msgishtml = 1;
10181 }
10182 } else {
10183 $value = dol_nl2br((string) $value);
10184 }
10185 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10186 }
10187 }
10188
10189 // TODO Implement the lazyload substitution
10190 /*
10191 add a loop to scan $substitutionarray:
10192 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.
10193 If no, we don't need to make replacement, so we do nothing.
10194 If yes, we can make the substitution:
10195
10196 include_once $path;
10197 $tmpobj = new $class($db);
10198 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10199 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10200 */
10201 $memory_object_list = array();
10202 foreach ($substitutionarray as $key => $value) {
10203 $lazy_load_arr = array();
10204 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10205 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10206 $key_to_substitute = $lazy_load_arr[1];
10207 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10208 $param_arr = explode(':', (string) $value);
10209 // path:class:method:id
10210 if (count($param_arr) == 4) {
10211 $path = $param_arr[0];
10212 $class = $param_arr[1];
10213 $method = $param_arr[2];
10214 $id = (int) $param_arr[3];
10215
10216 // load class file and init object list in memory
10217 if (!isset($memory_object_list[$class])) {
10218 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10219 require_once DOL_DOCUMENT_ROOT . $path;
10220 if (class_exists($class)) {
10221 $memory_object_list[$class] = array(
10222 'list' => array(),
10223 );
10224 }
10225 }
10226 }
10227
10228 // fetch object and set substitution
10229 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
10230 if (method_exists($class, $method)) {
10231 if (!isset($memory_object_list[$class]['list'][$id])) {
10232 $tmpobj = new $class($db);
10233 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10234 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
10235 $memory_object_list[$class]['list'][$id] = $tmpobj;
10236 } else {
10237 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10238 $tmpobj = $memory_object_list[$class]['list'][$id];
10239 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10240 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
10241 }
10242
10243 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
10244 }
10245 }
10246 }
10247 }
10248 }
10249 }
10250 }
10251
10252 return $text;
10253}
10254
10267function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
10268{
10269 global $conf, $user;
10270
10271 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
10272
10273 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
10274
10275 // Check if there is external substitution to do, requested by plugins
10276 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
10277
10278 foreach ($dirsubstitutions as $reldir) {
10279 $dir = dol_buildpath($reldir, 0);
10280
10281 // Check if directory exists
10282 if (!dol_is_dir($dir)) {
10283 continue;
10284 }
10285
10286 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
10287 foreach ($substitfiles as $substitfile) {
10288 $reg = array();
10289 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
10290 $module = $reg[1];
10291
10292 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
10293 // Include the user's functions file
10294 require_once $dir.$substitfile['name'];
10295 // Call the user's function, and only if it is defined
10296 $function_name = $module."_".$callfunc;
10297 if (function_exists($function_name)) {
10298 $function_name($substitutionarray, $outputlangs, $object, $parameters);
10299 }
10300 }
10301 }
10302 }
10303 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
10304 // to list all tags in odt template
10305 $tags = '';
10306 foreach ($substitutionarray as $key => $value) {
10307 $tags .= '{'.$key.'} => '.$value."\n";
10308 }
10309 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
10310 }
10311}
10312
10322function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
10323{
10324 print get_date_range($date_start, $date_end, $format, $outputlangs);
10325}
10326
10337function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
10338{
10339 global $langs;
10340
10341 $out = '';
10342
10343 if (!is_object($outputlangs)) {
10344 $outputlangs = $langs;
10345 }
10346
10347 if ($date_start && $date_end) {
10348 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10349 }
10350 if ($date_start && !$date_end) {
10351 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10352 }
10353 if (!$date_start && $date_end) {
10354 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10355 }
10356
10357 return $out;
10358}
10359
10368function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
10369{
10370 global $conf;
10371
10372 $ret = '';
10373 // If order not defined, we use the setup
10374 if ($nameorder < 0) {
10375 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
10376 }
10377 if ($nameorder == 1) {
10378 $ret .= $firstname;
10379 if ($firstname && $lastname) {
10380 $ret .= ' ';
10381 }
10382 $ret .= $lastname;
10383 } elseif ($nameorder == 2 || $nameorder == 3) {
10384 $ret .= $firstname;
10385 if (empty($ret) && $nameorder == 3) {
10386 $ret .= $lastname;
10387 }
10388 } else { // 0, 4 or 5
10389 $ret .= $lastname;
10390 if (empty($ret) && $nameorder == 5) {
10391 $ret .= $firstname;
10392 }
10393 if ($nameorder == 0) {
10394 if ($firstname && $lastname) {
10395 $ret .= ' ';
10396 }
10397 $ret .= $firstname;
10398 }
10399 }
10400 return $ret;
10401}
10402
10403
10416function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
10417{
10418 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
10419 if (!is_array($mesgs)) {
10420 $mesgs = trim((string) $mesgs);
10421 // If mesgs is a not an empty string
10422 if ($mesgs) {
10423 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
10424 return;
10425 }
10426 if ($attop) {
10427 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10428 } else {
10429 $_SESSION['dol_events'][$style][] = $mesgs;
10430 }
10431 }
10432 } else {
10433 // If mesgs is an array
10434 foreach ($mesgs as $mesg) {
10435 $mesg = trim((string) $mesg);
10436 if ($mesg) {
10437 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
10438 return;
10439 }
10440 if ($attop) {
10441 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10442 } else {
10443 $_SESSION['dol_events'][$style][] = $mesg;
10444 }
10445 }
10446 }
10447 }
10448}
10449
10463function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
10464{
10465 if (empty($mesg) && empty($mesgs)) {
10466 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
10467 } else {
10468 if ($messagekey) {
10469 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
10470 // TODO
10471 $mesg .= '';
10472 }
10473 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
10474 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
10475 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
10476 }
10477 if (empty($mesgs)) {
10478 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
10479 } else {
10480 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10481 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10482 }
10483 setEventMessage($mesgs, $style, $noduplicate, $attop);
10484 }
10485 }
10486 }
10487}
10488
10498function dol_htmloutput_events($disabledoutputofmessages = 0)
10499{
10500 // Show mesgs
10501 if (isset($_SESSION['dol_events']['mesgs'])) {
10502 if (empty($disabledoutputofmessages)) {
10503 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10504 }
10505 unset($_SESSION['dol_events']['mesgs']);
10506 }
10507 // Show errors
10508 if (isset($_SESSION['dol_events']['errors'])) {
10509 if (empty($disabledoutputofmessages)) {
10510 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10511 }
10512 unset($_SESSION['dol_events']['errors']);
10513 }
10514
10515 // Show warnings
10516 if (isset($_SESSION['dol_events']['warnings'])) {
10517 if (empty($disabledoutputofmessages)) {
10518 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10519 }
10520 unset($_SESSION['dol_events']['warnings']);
10521 }
10522}
10523
10538function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10539{
10540 global $conf, $langs;
10541
10542 $ret = 0;
10543 $return = '';
10544 $out = '';
10545 $divstart = $divend = '';
10546
10547 // If inline message with no format, we add it.
10548 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10549 $divstart = '<div class="'.$style.' clearboth">';
10550 $divend = '</div>';
10551 }
10552
10553 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10554 $langs->load("errors");
10555 $out .= $divstart;
10556 if (is_array($mesgarray) && count($mesgarray)) {
10557 foreach ($mesgarray as $message) {
10558 $ret++;
10559 $out .= $langs->trans($message);
10560 if ($ret < count($mesgarray)) {
10561 $out .= "<br>\n";
10562 }
10563 }
10564 }
10565 if ($mesgstring) {
10566 $ret++;
10567 $out .= $langs->trans($mesgstring);
10568 }
10569 $out .= $divend;
10570 }
10571
10572 if ($out) {
10573 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10574 $return = '<script nonce="'.getNonce().'">
10575 $(document).ready(function() {
10576 /* jnotify(message, preset of message type, keepmessage) */
10577 $.jnotify("'.dol_escape_js($out).'", "'.($style == "ok" ? 3000 : $style).'", '.($style == "ok" ? "false" : "true").',{ remove: function (){} } );
10578 });
10579 </script>';
10580 } else {
10581 $return = $out;
10582 }
10583 }
10584
10585 return $return;
10586}
10587
10599function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10600{
10601 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10602}
10603
10617function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10618{
10619 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10620 return;
10621 }
10622
10623 $iserror = 0;
10624 $iswarning = 0;
10625 if (is_array($mesgarray)) {
10626 foreach ($mesgarray as $val) {
10627 if ($val && preg_match('/class="error"/i', $val)) {
10628 $iserror++;
10629 break;
10630 }
10631 if ($val && preg_match('/class="warning"/i', $val)) {
10632 $iswarning++;
10633 break;
10634 }
10635 }
10636 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10637 $iserror++;
10638 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10639 $iswarning++;
10640 }
10641 if ($style == 'error') {
10642 $iserror++;
10643 }
10644 if ($style == 'warning') {
10645 $iswarning++;
10646 }
10647
10648 if ($iserror || $iswarning) {
10649 // Remove div from texts
10650 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10651 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10652 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10653 // Remove div from texts array
10654 if (is_array($mesgarray)) {
10655 $newmesgarray = array();
10656 foreach ($mesgarray as $val) {
10657 if (is_string($val)) {
10658 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10659 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10660 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10661 $newmesgarray[] = $tmpmesgstring;
10662 } else {
10663 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10664 }
10665 }
10666 $mesgarray = $newmesgarray;
10667 }
10668 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10669 } else {
10670 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10671 }
10672}
10673
10685function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10686{
10687 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10688}
10689
10710function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10711{
10712 // Clean parameters
10713 $order = strtolower($order);
10714
10715 if (is_array($array)) {
10716 $sizearray = count($array);
10717 if ($sizearray > 0) {
10718 $temp = array();
10719 foreach (array_keys($array) as $key) {
10720 if (is_object($array[$key])) {
10721 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10722 } else {
10723 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10724 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10725 }
10726 if ($natsort == -1) {
10727 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10728 }
10729 }
10730
10731 if (empty($natsort) || $natsort == -1) {
10732 if ($order == 'asc') {
10733 asort($temp);
10734 } else {
10735 arsort($temp);
10736 }
10737 } else {
10738 if ($case_sensitive) {
10739 natsort($temp);
10740 } else {
10741 natcasesort($temp); // natecasesort is not sensible to case
10742 }
10743 if ($order != 'asc') {
10744 $temp = array_reverse($temp, true);
10745 }
10746 }
10747
10748 $sorted = array();
10749
10750 foreach (array_keys($temp) as $key) {
10751 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10752 }
10753
10754 return $sorted;
10755 }
10756 }
10757 return $array;
10758}
10759
10760
10768function utf8_check($str)
10769{
10770 $str = (string) $str; // Sometimes string is an int.
10771
10772 // We must use here a binary strlen function (so not dol_strlen)
10773 $strLength = strlen($str);
10774 for ($i = 0; $i < $strLength; $i++) {
10775 if (ord($str[$i]) < 0x80) {
10776 continue; // 0bbbbbbb
10777 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10778 $n = 1; // 110bbbbb
10779 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10780 $n = 2; // 1110bbbb
10781 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10782 $n = 3; // 11110bbb
10783 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10784 $n = 4; // 111110bb
10785 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10786 $n = 5; // 1111110b
10787 } else {
10788 return false; // Does not match any model
10789 }
10790 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10791 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10792 return false;
10793 }
10794 }
10795 }
10796 return true;
10797}
10798
10806function utf8_valid($str)
10807{
10808 /* 2 other methods to test if string is utf8
10809 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10810 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10811 */
10812 return preg_match('//u', $str) ? true : false;
10813}
10814
10815
10822function ascii_check($str)
10823{
10824 if (function_exists('mb_check_encoding')) {
10825 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10826 if (!mb_check_encoding($str, 'ASCII')) {
10827 return false;
10828 }
10829 } else {
10830 if (preg_match('/[^\x00-\x7f]/', $str)) {
10831 return false; // Contains a byte > 7f
10832 }
10833 }
10834
10835 return true;
10836}
10837
10838
10846function dol_osencode($str)
10847{
10848 $tmp = ini_get("unicode.filesystem_encoding");
10849 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10850 $tmp = 'iso-8859-1'; // By default for windows
10851 }
10852 if (empty($tmp)) {
10853 $tmp = 'utf-8'; // By default for other
10854 }
10855 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10856 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10857 }
10858
10859 if ($tmp == 'iso-8859-1') {
10860 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10861 }
10862 return $str;
10863}
10864
10865
10881function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10882{
10883 global $conf;
10884
10885 // If key empty
10886 if ($key == '') {
10887 return 0;
10888 }
10889
10890 // Check in cache
10891 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10892 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10893 }
10894
10895 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10896
10897 $sql = "SELECT ".$fieldid." as valuetoget";
10898 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10899 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10900 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10901 } else {
10902 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10903 }
10904 if (!empty($entityfilter)) {
10905 $sql .= " AND entity IN (".getEntity($tablename).")";
10906 }
10907 if ($filters) {
10908 $sql .= $filters;
10909 }
10910
10911 $resql = $db->query($sql);
10912 if ($resql) {
10913 $obj = $db->fetch_object($resql);
10914 $valuetoget = '';
10915 if ($obj) {
10916 $valuetoget = $obj->valuetoget;
10917 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10918 } else {
10919 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10920 }
10921 $db->free($resql);
10922
10923 return $valuetoget;
10924 } else {
10925 return -1;
10926 }
10927}
10928
10938function isStringVarMatching($var, $regextext, $matchrule = 1)
10939{
10940 if ($matchrule == 1) {
10941 if ($var == 'mainmenu') {
10942 global $mainmenu;
10943 return (preg_match('/^'.$regextext.'/', $mainmenu));
10944 } elseif ($var == 'leftmenu') {
10945 global $leftmenu;
10946 return (preg_match('/^'.$regextext.'/', $leftmenu));
10947 } else {
10948 return 'This variable is not accessible with dol_eval';
10949 }
10950 } else {
10951 return 'This value for matchrule is not implemented';
10952 }
10953}
10954
10955
10965function verifCond($strToEvaluate, $onlysimplestring = '1')
10966{
10967 //print $strToEvaluate."<br>\n";
10968 $rights = true;
10969 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10970 //var_dump($strToEvaluate);
10971 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10972 $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
10973 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10974 //var_dump($rights);
10975 }
10976 return $rights;
10977}
10978
10993function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10994{
10995 if ($returnvalue != 1) {
10996 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
10997 }
10998
10999 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
11000 return dol_eval_new($s);
11001 } else {
11002 return dol_eval_standard($s, $returnvalue, $hideerrors, $onlysimplestring);
11003 }
11004}
11005
11015function dol_eval_new($s)
11016{
11017 // Only this global variables can be read by eval function and returned to caller
11018 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11019 $db, $langs, $user, $website, $websitepage,
11020 $action, $mainmenu, $leftmenu,
11021 $mysoc,
11022 $objectoffield, // To allow the use of $objectoffield in computed fields
11023
11024 // Old variables used
11025 $object,
11026 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11027
11028 // PHP < 7.4.0
11029 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11030 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11031
11032 // PHP < 8.0.0
11033 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11034 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11035 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11036 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11037 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11038
11039 // PHP < 8.1.0
11040 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11041 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11042 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11043 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11044
11045 // PHP < 8.4.0
11046 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11047 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11048 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11049
11050 $prohibited_token_ids = [
11051 /*
11052 * Prohibited int tokens
11053 */
11054
11055 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11056 'T_ABSTRACT', 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', 'T_ATTRIBUTE',
11057 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11058 'T_BAD_CHARACTER',
11059 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11060 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11061 'T_CALLABLE', 'T_CATCH', 'T_CLASS', 'T_CLOSE_TAG', 'T_CONST',
11062 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11063 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11064 'T_DECLARE', 'T_DIR',
11065 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11066 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11067 'T_ECHO', 'T_ENDDECLARE', 'T_ENUM', 'T_EVAL', 'T_EXIT', 'T_EXTENDS',
11068 // 'T_FOR', 'T_FOREACH',
11069 'T_FILE', 'T_FINAL', 'T_FINALLY', 'T_FN', 'T_FUNCTION', 'T_FUNC_C',
11070 'T_GLOBAL', 'T_GOTO',
11071 'T_HALT_COMPILER',
11072 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11073 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11074 'T_IMPLEMENTS', 'T_INCLUDE', 'T_INCLUDE_ONCE', 'T_INSTEADOF', 'T_INTERFACE',
11075 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11076 'T_LINE',
11077 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11078 'T_METHOD_C',
11079 // 'T_NEW',
11080 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11081 'T_NAMESPACE',
11082 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11083 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11084 'T_OPEN_TAG', 'T_OPEN_TAG_WITH_ECHO',
11085 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11086 'T_PRINT', 'T_PRIVATE', 'T_PROTECTED', 'T_PUBLIC',
11087 // 'T_PROPERTY_C',
11088 'T_READONLY', 'T_REQUIRE', 'T_REQUIRE_ONCE', 'T_RETURN',
11089 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11090 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11091 'T_STATIC',
11092 'T_THROW', 'T_TRAIT', 'T_TRAIT_C', 'T_TRY',
11093 'T_UNSET', 'T_UNSET_CAST', 'T_USE',
11094 // 'T_VARIABLE',
11095 'T_VAR',
11096 // 'T_WHILE', 'T_WHITESPACE',
11097 // 'T_XOR_EQUAL',
11098 // 'T_YIELD', 'T_YIELD_FROM',
11099
11100 /*
11101 * Prohibited string tokens
11102 */
11103 ';', '`',
11104 ];
11105
11106 $prohibited_variables = [
11107 '$_COOKIE', '$_ENV', '$_FILES', '$GLOBALS', '$_GET', '$_POST', '$_REQUEST', '$_SERVER', '$_SESSION',
11108 ];
11109
11110 $prohibited_functions = [
11111 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11112 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11113 'override_function', 'session_id', 'session_create_id', 'session_regenerate_id',
11114 'call_user_func', 'call_user_func_array', // PREVENT calling forbidden functions
11115 'exec', 'passthru', 'shell_exec', 'system', 'proc_open', 'popen',
11116 'dol_eval', 'dol_eval_new', 'dol_eval_standard', 'dol_contctdesc', 'executeCLI', 'verifCond', 'GETPOST', // Native Dolibarr functions
11117 'create_function', 'assert', 'mb_ereg_replace', 'mb_eregi_replace', // function with eval capabilities
11118 'dol_compress_dir', 'dol_decode', 'dol_delete_file', 'dol_delete_dir', 'dol_delete_dir_recursive', 'dol_copy', 'archiveOrBackupFile', // more dolibarr functions
11119 'fopen', 'file_put_contents', 'fputs', 'fputscsv', 'fwrite', 'fpassthru', 'mkdir', 'rmdir', 'symlink', 'touch', 'unlink', 'umask', // PHP functions related to file operations
11120 'invoke', 'invokeArgs', // Method of ReflectionFunction to execute a function
11121 'filter_input', 'filter_input_array', 'GETPOST', // PREVENT CODE INJECTION
11122 ];
11123
11124 $prohibited_token_arrangements = [
11125 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11126 ' T_VARIABLE ( ', ' " ( ', ' \' ( ', ' T_CONSTANT_ENCAPSED_STRING ( ', ' ) ( ',
11127 ];
11128
11129 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
11130
11131 $tokens_arrangement = ' ';
11132
11133 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
11134 if (is_array($tokens[$i])) {
11135 $token_id = $tokens[$i][0];
11136 $token_value = $tokens[$i][1];
11137 $token_name = token_name($tokens[$i][0]);
11138 } else {
11139 $token_id = $tokens[$i];
11140 $token_value = $tokens[$i];
11141 $token_name = $tokens[$i];
11142 }
11143
11144 // Ignore whitespaces
11145 if (T_WHITESPACE === $token_id) {
11146 continue;
11147 }
11148
11149 // Keep history to check arrangements
11150 $tokens_arrangement .= "{$token_name} ";
11151
11152 // Prohibited Variables
11153 if (T_VARIABLE === $token_id
11154 && in_array($token_value, $prohibited_variables, true)
11155 ) {
11156 return "« {$token_value} » is prohibited in « {$s} »";
11157 }
11158
11159 // Prohibited Functions
11160 if (T_STRING === $token_id
11161 && in_array($token_value, $prohibited_functions, true)
11162 ) {
11163 return "« {$token_value} » is prohibited in « {$s} »";
11164 }
11165 }
11166
11167 // Prohibited Token IDs
11168 $maxi = count($prohibited_token_ids);
11169 for ($i = 0; $i < $maxi; ++$i) {
11170 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
11171 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
11172 }
11173 }
11174
11175 // Prohibited token arrangements
11176 $maxi = count($prohibited_token_arrangements);
11177 for ($i = 0; $i < $maxi; ++$i) {
11178 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
11179 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
11180 }
11181 }
11182
11183 // Return result
11184 try {
11185 return @eval("return {$s};") ?? '';
11186 } catch (Throwable $ex) {
11187 return "Exception during evaluation: ".$s." - ".$ex->getMessage();
11188 }
11189}
11190
11205function dol_eval_standard($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11206{
11207 // Only this global variables can be read by eval function and returned to caller
11208 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11209 global $db, $langs, $user, $website, $websitepage;
11210 global $action, $mainmenu, $leftmenu;
11211 global $mysoc;
11212 global $objectoffield; // To allow the use of $objectoffield in computed fields
11213
11214 // Old variables used (deprecated)
11215 global $object;
11216 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11217
11218 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
11219 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
11220 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
11221 }
11222 if (!is_scalar($s)) {
11223 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
11224 }
11225
11226 try {
11227 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
11228 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
11229 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
11230 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
11231 // 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"
11232
11233 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
11234 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
11235 if ($onlysimplestring == '2') {
11236 $specialcharsallowed .= '<[]';
11237 }
11238 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
11239 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
11240 }
11241 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
11242 if ($returnvalue) {
11243 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s;
11244 } else {
11245 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING);
11246 return '';
11247 }
11248 }
11249
11250 // Check if we found a ? without a space before and after
11251 $tmps = str_replace(' ? ', '__XXX__', $s);
11252 if (strpos($tmps, '?') !== false) {
11253 if ($returnvalue) {
11254 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s;
11255 } else {
11256 dol_syslog('Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s, LOG_WARNING);
11257 return '';
11258 }
11259 }
11260
11261 // Check if there is a < or <= without spaces before/after
11262 if (preg_match('/<=?[^\s]/', $s)) {
11263 if ($returnvalue) {
11264 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s;
11265 } else {
11266 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING);
11267 return '';
11268 }
11269 }
11270
11271 // Check if there is dynamic call (first we use black list patterns)
11272 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
11273 if ($returnvalue) {
11274 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;
11275 } else {
11276 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);
11277 return '';
11278 }
11279 }
11280
11281 // Now we check if we try dynamic call
11282 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
11283 $savescheck = '';
11284 $scheck = $s;
11285 while ($scheck && $savescheck != $scheck) {
11286 $savescheck = $scheck;
11287 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
11288 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
11289 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
11290 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11291 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11292 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
11293 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
11294 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
11295 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
11296 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
11297 }
11298 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
11299
11300 // Now test if it remains 1 open parenthesis.
11301 if (strpos($scheck, '(') !== false) {
11302 if ($returnvalue) {
11303 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
11304 } else {
11305 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);
11306 return '';
11307 }
11308 }
11309
11310 // TODO
11311 // We can exclude $ char that are not in dol_eval global, so that are not:
11312 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object, $obj, ...,
11313 }
11314 if ($s === 'Array') {
11315 if ($returnvalue) {
11316 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
11317 } else {
11318 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
11319 return '';
11320 }
11321 }
11322
11323 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
11324 if ($returnvalue) {
11325 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
11326 } else {
11327 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
11328 return '';
11329 }
11330 }
11331
11332 if (strpos($s, '`') !== false) {
11333 if ($returnvalue) {
11334 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
11335 } else {
11336 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
11337 return '';
11338 }
11339 }
11340
11341 // Disallow also concat
11342 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
11343 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
11344 if ($returnvalue) {
11345 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
11346 } else {
11347 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
11348 return '';
11349 }
11350 }
11351 }
11352
11353 // We block use of php exec or php file functions
11354 $forbiddenphpstrings = array('$$', '$_', '}[', ')(');
11355 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction'));
11356
11357 // 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)
11358 // 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
11359 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
11360 $forbiddenphpfunctions = array();
11361 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
11362 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
11363 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
11364
11365 $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"));
11366 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
11367 $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"));
11368 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
11369 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
11370
11371 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
11372
11373 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
11374 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
11375 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "dol_concatdesc", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
11376 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
11377 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
11378 $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
11379 $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"));
11380 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
11381 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) { // We disabllow all function that allow to obfuscate the real name of a function
11382 // @phpcs:ignore
11383 $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
11384 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concatdesc")); // native dolibarr functions
11385 }
11386
11387 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
11388
11389 $forbiddenphpregex = 'global\s*\$';
11390 $forbiddenphpregex .= '|';
11391 $forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
11392
11393 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
11394
11395 do {
11396 $oldstringtoclean = $s;
11397 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
11398 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
11399 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
11400 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
11401 } while ($oldstringtoclean != $s);
11402
11403
11404 if (strpos($s, '__forbiddenstring__') !== false) {
11405 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
11406 if ($returnvalue) {
11407 return 'Bad string syntax to evaluate: '.$s;
11408 } else {
11409 dol_syslog('Bad string syntax to evaluate: '.$s);
11410 return '';
11411 }
11412 }
11413
11414 //print $s."<br>\n";
11415 if ($returnvalue) {
11416 ob_start(); // An evaluation has no reason to output data
11417 $isObBufferActive = true;
11418 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
11419 $tmpo = ob_get_clean();
11420 $isObBufferActive = false;
11421 if ($tmpo) {
11422 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
11423 }
11424 return $tmps;
11425 } else {
11426 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
11427 if ($hideerrors) {
11428 @eval($s);
11429 } else {
11430 eval($s);
11431 }
11432 return '';
11433 }
11434 } catch (Error $e) {
11435 if ($isObBufferActive) {
11436 // Clean up buffer which was left behind due to exception.
11437 $tmpo = ob_get_clean();
11438 $isObBufferActive = false;
11439 }
11440 $error = 'dol_eval try/catch error : ';
11441 $error .= $e->getMessage();
11442 dol_syslog($error, LOG_WARNING);
11443 if ($returnvalue) {
11444 return 'Exception during evaluation: '.$s;
11445 } else {
11446 return '';
11447 }
11448 }
11449}
11450
11458function dol_validElement($element)
11459{
11460 return (trim($element) != '');
11461}
11462
11471function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
11472{
11473 if (empty($codelang)) {
11474 return '';
11475 }
11476
11477 if ($codelang == 'auto') {
11478 return '<span class="fa fa-language"></span>';
11479 }
11480
11481 $langtocountryflag = array(
11482 'ar_AR' => '',
11483 'ca_ES' => 'catalonia',
11484 'da_DA' => 'dk',
11485 'fr_CA' => 'mq',
11486 'sv_SV' => 'se',
11487 'sw_SW' => 'unknown',
11488 'AQ' => 'unknown',
11489 'CW' => 'unknown',
11490 'IM' => 'unknown',
11491 'JE' => 'unknown',
11492 'MF' => 'unknown',
11493 'BL' => 'unknown',
11494 'SX' => 'unknown'
11495 );
11496
11497 if (isset($langtocountryflag[$codelang])) {
11498 $flagImage = $langtocountryflag[$codelang];
11499 } else {
11500 $tmparray = explode('_', $codelang);
11501 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
11502 }
11503
11504 $morecss = '';
11505 $reg = array();
11506 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
11507 $morecss = $reg[1];
11508 $moreatt = "";
11509 }
11510
11511 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
11512 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
11513}
11514
11522function getLanguageCodeFromCountryCode($countrycode)
11523{
11524 global $mysoc;
11525
11526 if (empty($countrycode)) {
11527 return null;
11528 }
11529
11530 if (strtoupper($countrycode) == 'MQ') {
11531 return 'fr_CA';
11532 }
11533 if (strtoupper($countrycode) == 'SE') {
11534 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
11535 }
11536 if (strtoupper($countrycode) == 'CH') {
11537 if ($mysoc->country_code == 'FR') {
11538 return 'fr_CH';
11539 }
11540 if ($mysoc->country_code == 'DE') {
11541 return 'de_CH';
11542 }
11543 if ($mysoc->country_code == 'IT') {
11544 return 'it_CH';
11545 }
11546 }
11547
11548 // Locale list taken from:
11549 // http://stackoverflow.com/questions/3191664/
11550 // list-of-all-locales-and-their-short-codes
11551 $locales = array(
11552 'af-ZA',
11553 'am-ET',
11554 'ar-AE',
11555 'ar-BH',
11556 'ar-DZ',
11557 'ar-EG',
11558 'ar-IQ',
11559 'ar-JO',
11560 'ar-KW',
11561 'ar-LB',
11562 'ar-LY',
11563 'ar-MA',
11564 'ar-OM',
11565 'ar-QA',
11566 'ar-SA',
11567 'ar-SY',
11568 'ar-TN',
11569 'ar-YE',
11570 //'as-IN', // Moved after en-IN
11571 'ba-RU',
11572 'be-BY',
11573 'bg-BG',
11574 'bn-BD',
11575 //'bn-IN', // Moved after en-IN
11576 'bo-CN',
11577 'br-FR',
11578 'ca-ES',
11579 'co-FR',
11580 'cs-CZ',
11581 'cy-GB',
11582 'da-DK',
11583 'de-AT',
11584 'de-CH',
11585 'de-DE',
11586 'de-LI',
11587 'de-LU',
11588 'dv-MV',
11589 'el-GR',
11590 'en-AU',
11591 'en-BZ',
11592 'en-CA',
11593 'en-GB',
11594 'en-IE',
11595 'en-IN',
11596 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
11597 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
11598 'en-JM',
11599 'en-MY',
11600 'en-NZ',
11601 'en-PH',
11602 'en-SG',
11603 'en-TT',
11604 'en-US',
11605 'en-ZA',
11606 'en-ZW',
11607 'es-AR',
11608 'es-BO',
11609 'es-CL',
11610 'es-CO',
11611 'es-CR',
11612 'es-DO',
11613 'es-EC',
11614 'es-ES',
11615 'es-GT',
11616 'es-HN',
11617 'es-MX',
11618 'es-NI',
11619 'es-PA',
11620 'es-PE',
11621 'es-PR',
11622 'es-PY',
11623 'es-SV',
11624 'es-US',
11625 'es-UY',
11626 'es-VE',
11627 'et-EE',
11628 'eu-ES',
11629 'fa-IR',
11630 'fi-FI',
11631 'fo-FO',
11632 'fr-BE',
11633 'fr-CA',
11634 'fr-CH',
11635 'fr-FR',
11636 'fr-LU',
11637 'fr-MC',
11638 'fy-NL',
11639 'ga-IE',
11640 'gd-GB',
11641 'gl-ES',
11642 'gu-IN',
11643 'he-IL',
11644 'hi-IN',
11645 'hr-BA',
11646 'hr-HR',
11647 'hu-HU',
11648 'hy-AM',
11649 'id-ID',
11650 'ig-NG',
11651 'ii-CN',
11652 'is-IS',
11653 'it-CH',
11654 'it-IT',
11655 'ja-JP',
11656 'ka-GE',
11657 'kk-KZ',
11658 'kl-GL',
11659 'km-KH',
11660 'kn-IN',
11661 'ko-KR',
11662 'ky-KG',
11663 'lb-LU',
11664 'lo-LA',
11665 'lt-LT',
11666 'lv-LV',
11667 'mi-NZ',
11668 'mk-MK',
11669 'ml-IN',
11670 'mn-MN',
11671 'mr-IN',
11672 'ms-BN',
11673 'ms-MY',
11674 'mt-MT',
11675 'nb-NO',
11676 'ne-NP',
11677 'nl-BE',
11678 'nl-NL',
11679 'nn-NO',
11680 'oc-FR',
11681 'or-IN',
11682 'pa-IN',
11683 'pl-PL',
11684 'ps-AF',
11685 'pt-BR',
11686 'pt-PT',
11687 'rm-CH',
11688 'ro-MD',
11689 'ro-RO',
11690 'ru-RU',
11691 'rw-RW',
11692 'sa-IN',
11693 'se-FI',
11694 'se-NO',
11695 'se-SE',
11696 'si-LK',
11697 'sk-SK',
11698 'sl-SI',
11699 'sq-AL',
11700 'sv-FI',
11701 'sv-SE',
11702 'sw-KE',
11703 'ta-IN',
11704 'te-IN',
11705 'th-TH',
11706 'tk-TM',
11707 'tn-ZA',
11708 'tr-TR',
11709 'tt-RU',
11710 'ug-CN',
11711 'uk-UA',
11712 'ur-PK',
11713 'vi-VN',
11714 'wo-SN',
11715 'xh-ZA',
11716 'yo-NG',
11717 'zh-CN',
11718 'zh-HK',
11719 'zh-MO',
11720 'zh-SG',
11721 'zh-TW',
11722 'zu-ZA',
11723 );
11724
11725 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
11726 if (in_array($buildprimarykeytotest, $locales)) {
11727 return strtolower($countrycode).'_'.strtoupper($countrycode);
11728 }
11729
11730 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
11731 foreach ($locales as $locale) {
11732 $locale_language = locale_get_primary_language($locale);
11733 $locale_region = locale_get_region($locale);
11734 if (strtoupper($countrycode) == $locale_region) {
11735 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
11736 return strtolower($locale_language).'_'.strtoupper($locale_region);
11737 }
11738 }
11739 } else {
11740 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
11741 }
11742
11743 return null;
11744}
11745
11776function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11777{
11778 global $hookmanager, $db;
11779
11780 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11781 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11782 $values = explode(':', $value);
11783
11784 $reg = array();
11785 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11786 $newtab = array();
11787 $postab = $h;
11788 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11789 $str = $values[1];
11790 $posstart = strpos($str, '(');
11791 if ($posstart > 0) {
11792 $posend = strpos($str, ')');
11793 if ($posstart > 0) {
11794 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11795 if (is_numeric($res1)) {
11796 $postab = (int) $res1;
11797 $values[1] = '+' . substr($str, $posend + 1);
11798 }
11799 }
11800 }
11801 if (count($values) == 6) {
11802 // new declaration with permissions:
11803 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11804 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11805 if ($values[0] != $type) {
11806 continue;
11807 }
11808
11809 if (verifCond($values[4], '2')) {
11810 if ($values[3]) {
11811 if ($filterorigmodule) { // If a filter of module origin has been requested
11812 if (strpos($values[3], '@')) { // This is an external module
11813 if ($filterorigmodule != 'external') {
11814 continue;
11815 }
11816 } else { // This looks a core module
11817 if ($filterorigmodule != 'core') {
11818 continue;
11819 }
11820 }
11821 }
11822 $langs->load($values[3]);
11823 }
11824 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11825 // If label is "SUBSTITUION_..."
11826 $substitutionarray = array();
11827 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11828 $label = make_substitutions($reg[1], $substitutionarray);
11829 } else {
11830 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11831 $labeltemp = explode(',', $values[2]);
11832 $label = $langs->trans($labeltemp[0]);
11833
11834 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11835 dol_include_once($labeltemp[2]);
11836 $classtoload = $labeltemp[1];
11837 if (class_exists($classtoload)) {
11838 $obj = new $classtoload($db);
11839 $function = $labeltemp[3];
11840 if ($obj && $function && method_exists($obj, $function)) {
11841 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11842 $nbrec = $obj->$function($object->id, $obj);
11843 if (!empty($nbrec)) {
11844 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11845 }
11846 }
11847 }
11848 }
11849 }
11850
11851 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11852 $newtab[1] = $label;
11853 $newtab[2] = str_replace('+', '', $values[1]);
11854 $h++;
11855 } else {
11856 continue;
11857 }
11858 } elseif (count($values) == 5) { // case deprecated
11859 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11860
11861 if ($values[0] != $type) {
11862 continue;
11863 }
11864 if ($values[3]) {
11865 if ($filterorigmodule) { // If a filter of module origin has been requested
11866 if (strpos($values[3], '@')) { // This is an external module
11867 if ($filterorigmodule != 'external') {
11868 continue;
11869 }
11870 } else { // This looks a core module
11871 if ($filterorigmodule != 'core') {
11872 continue;
11873 }
11874 }
11875 }
11876 $langs->load($values[3]);
11877 }
11878 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11879 $substitutionarray = array();
11880 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11881 $label = make_substitutions($reg[1], $substitutionarray);
11882 } else {
11883 $label = $langs->trans($values[2]);
11884 }
11885
11886 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11887 $newtab[1] = $label;
11888 $newtab[2] = str_replace('+', '', $values[1]);
11889 $h++;
11890 }
11891 // set tab at its position
11892 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11893 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11894 if ($values[0] != $type) {
11895 continue;
11896 }
11897 $tabname = str_replace('-', '', $values[1]);
11898 foreach ($head as $key => $val) {
11899 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11900 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11901 if ($head[$key][2] == $tabname && $condition) {
11902 unset($head[$key]);
11903 break;
11904 }
11905 }
11906 }
11907 }
11908 }
11909
11910 // No need to make a return $head. Var is modified as a reference
11911 if (!empty($hookmanager)) {
11912 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11913 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
11914 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11915 if ($reshook > 0) { // Hook ask to replace completely the array
11916 $head = $hookmanager->resArray;
11917 } else { // Hook
11918 $head = array_merge($head, $hookmanager->resArray);
11919 }
11920 $h = count($head);
11921 }
11922}
11923
11935function printCommonFooter($zone = 'private')
11936{
11937 global $conf, $hookmanager, $user, $langs;
11938 global $debugbar;
11939 global $action;
11940 global $micro_start_time;
11941
11942 if ($zone == 'private') {
11943 print "\n".'<!-- Common footer for private page -->'."\n";
11944 } else {
11945 print "\n".'<!-- Common footer for public page -->'."\n";
11946 }
11947
11948 // A div to store page_y POST parameter so we can read it using javascript
11949 print "\n<!-- A div to store page_y POST parameter -->\n";
11950 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11951
11952 $parameters = array();
11953 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11954 if (empty($reshook)) {
11955 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11956 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11957 }
11958
11959 print "\n";
11960 if (!empty($conf->use_javascript_ajax)) {
11961 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11962 print '<script>'."\n";
11963 print 'jQuery(document).ready(function() {'."\n";
11964
11965 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11966 print "\n";
11967 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11968 print 'jQuery("li.menuhider").click(function(event) {';
11969 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11970 print ' console.log("We click on .menuhider");'."\n";
11971 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11972 print '});'."\n";
11973 }
11974
11975 // Management of focus and mandatory for fields
11976 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"])))) {
11977 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11978 $relativepathstring = $_SERVER["PHP_SELF"];
11979 // Clean $relativepathstring
11980 if (constant('DOL_URL_ROOT')) {
11981 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11982 }
11983 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11984 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11985 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11986
11987 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11988 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11989 $qualified = 0;
11990 if ($defkey != '_noquery_') {
11991 $tmpqueryarraytohave = explode('&', $defkey);
11992 $foundintru = 0;
11993 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11994 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11995 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11996 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11997 $foundintru = 1;
11998 }
11999 }
12000 if (!$foundintru) {
12001 $qualified = 1;
12002 }
12003 //var_dump($defkey.'-'.$qualified);
12004 } else {
12005 $qualified = 1;
12006 }
12007
12008 if ($qualified) {
12009 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
12010 foreach ($defval as $paramkey => $paramval) {
12011 // Set focus on field
12012 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
12013 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
12014 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
12015 }
12016 }
12017 }
12018 }
12019 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12020 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12021 $qualified = 0;
12022 if ($defkey != '_noquery_') {
12023 $tmpqueryarraytohave = explode('&', $defkey);
12024 $foundintru = 0;
12025 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12026 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12027 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12028 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12029 $foundintru = 1;
12030 }
12031 }
12032 if (!$foundintru) {
12033 $qualified = 1;
12034 }
12035 //var_dump($defkey.'-'.$qualified);
12036 } else {
12037 $qualified = 1;
12038 }
12039
12040 if ($qualified) {
12041 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
12042
12043 foreach ($defval as $paramkey => $paramval) {
12044 // Solution 1: Add handler on submit to check if mandatory fields are empty
12045 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12046 print "form.on('submit', function(event) {
12047 var submitter = \$(this).find(':submit:focus').get(0);
12048 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12049
12050 if (buttonName == 'cancel') {
12051 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12052 return true;
12053 }
12054
12055 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');
12056
12057 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12058 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12059
12060 if (tmptypefield == 'textarea') {
12061 // We must instead check the content of ckeditor
12062 var tmpeditor = (typeof CKEDITOR !== 'undefined') ? CKEDITOR.instances['".dol_escape_js($paramkey)."'] : null;
12063 if (tmpeditor) {
12064 tmpvalue = tmpeditor.getData();
12065 console.log('For textarea tmpvalue is '+tmpvalue);
12066 }
12067 }
12068
12069 let tmpvalueisempty = false;
12070 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1 || tmpvalue === '-1') {
12071 tmpvalueisempty = true;
12072 }
12073 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
12074 tmpvalueisempty = true;
12075 }
12076 if (tmpvalueisempty && buttonName !== 'cancel') {
12077 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
12078 event.preventDefault(); // Stop submission of form to allow custom code to decide.
12079 event.stopPropagation(); // Stop other handlers.
12080
12081 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
12082
12083 return false;
12084 }
12085 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
12086 return true;
12087 });
12088 \n";
12089
12090 // Solution 2: Add property 'required' on input
12091 // so browser will check value and try to focus on it when submitting the form.
12092 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
12093 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12094 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12095 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
12096 //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";
12097 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
12098 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
12099 // Add 'field required' class on closest td for all input elements : input, textarea and select
12100 //print '}, 500);'; // 500 milliseconds delay
12101
12102 // Now set the class "fieldrequired"
12103 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
12104 }
12105
12106 // If we submit using the cancel button, we remove the required attributes
12107 print 'jQuery("input[name=\'cancel\']").click(function() {
12108 console.log("We click on cancel button so removed all required attribute");
12109 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
12110 });'."\n";
12111 }
12112 }
12113 }
12114 }
12115
12116 print '});'."\n";
12117
12118 // End of tuning
12119 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
12120 print "\n";
12121 print "/* JS CODE TO ENABLE to add memory info */\n";
12122 print 'window.console && console.log("';
12123 if (getDolGlobalString('MEMCACHED_SERVER')) {
12124 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
12125 }
12126 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
12127 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
12128 $micro_end_time = microtime(true);
12129 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
12130 }
12131
12132 if (function_exists("memory_get_usage")) {
12133 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
12134 }
12135 if (function_exists("memory_get_peak_usage")) {
12136 print ' - Real mem peak: '.memory_get_peak_usage(true);
12137 }
12138 if (function_exists("zend_loader_file_encoded")) {
12139 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
12140 }
12141 print '");'."\n";
12142 }
12143
12144 print "\n".'</script>'."\n";
12145
12146 // Google Analytics
12147 // TODO Add a hook here
12148 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
12149 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
12150 foreach ($tmptagarray as $tmptag) {
12151 print "\n";
12152 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
12153 print '
12154 <!-- Global site tag (gtag.js) - Google Analytics -->
12155 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
12156 <script>
12157 window.dataLayer = window.dataLayer || [];
12158 function gtag(){dataLayer.push(arguments);}
12159 gtag(\'js\', new Date());
12160
12161 gtag(\'config\', \''.trim($tmptag).'\');
12162 </script>';
12163 print "\n";
12164 }
12165 }
12166 }
12167
12168 // Add Xdebug coverage of code
12169 if (defined('XDEBUGCOVERAGE')) {
12170 print_r(xdebug_get_code_coverage());
12171 }
12172
12173 // Add DebugBar data
12174 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
12175 if (isset($debugbar['time'])) {
12176 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12177 $debugbar['time']->stopMeasure('pageaftermaster');
12178 }
12179 print '<!-- Output debugbar data -->'."\n";
12180 $renderer = $debugbar->getJavascriptRenderer();
12181 print $renderer->render();
12182 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
12183 print "\n";
12184 print "<!-- Start of log output\n";
12185 //print '<div class="hidden">'."\n";
12186 foreach ($conf->logbuffer as $logline) {
12187 print $logline."<br>\n";
12188 }
12189 //print '</div>'."\n";
12190 print "End of log output -->\n";
12191 }
12192 }
12193}
12194
12204function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
12205{
12206 if (is_null($string)) {
12207 return array();
12208 }
12209
12210 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
12211 // This is a regex string
12212 $newdelimiter = $delimiter;
12213 } else {
12214 // This is a simple string
12215 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
12216 $newdelimiter = preg_quote($delimiter, '/');
12217 }
12218
12219 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
12220 $ka = array();
12221 foreach ($a as $s) { // each part
12222 if ($s) {
12223 if ($pos = strpos($s, $kv)) { // key/value delimiter
12224 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
12225 } else { // key delimiter not found
12226 $ka[] = trim($s);
12227 }
12228 }
12229 }
12230 return $ka;
12231 }
12232
12233 return array();
12234}
12235
12243function dolExplodeKeepIfQuotes($input)
12244{
12245 // Use regexp to capture words and section in quotes
12246 $matches = array();
12247 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
12248
12249 // Merge result and delete empty values
12250
12251 $result = array_map(
12258 static function ($a, $b, $c) {
12259 if ($a !== '') {
12260 return $a;
12261 }
12262 if ($b !== '') {
12263 return $b;
12264 }
12265 if ($c !== '') {
12266 return $c;
12267 }
12268 return '';
12269 },
12270 $matches[1],
12271 $matches[2],
12272 $matches[3]
12273 );
12274 return array_values(array_filter(
12275 $result,
12282 static function ($val) {
12283 return $val !== '';
12284 }
12285 ));
12286}
12287
12288
12295function dol_set_focus($selector)
12296{
12297 print "\n".'<!-- Set focus onto a specific field -->'."\n";
12298 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
12299}
12300
12301
12309function dol_getmypid()
12310{
12311 if (!function_exists('getmypid')) {
12312 return mt_rand(99900000, 99965535);
12313 } else {
12314 return getmypid(); // May be a number on 64 bits (depending on OS)
12315 }
12316}
12317
12339function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
12340{
12341 global $db, $langs;
12342
12343 $value = trim($value);
12344
12345 if ($mode == 0) {
12346 $value = preg_replace('/\*/', '%', $value); // Replace * with %
12347 }
12348 if ($mode == 1) {
12349 $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
12350 }
12351
12352 $value = preg_replace('/\s*\|\s*/', '|', $value);
12353
12354 // Split criteria on ' ' but not if we are inside quotes.
12355 // For mode 3, the split is done later on the , only and not on the ' '.
12356 if ($mode != -3 && $mode != 3) {
12357 $crits = dolExplodeKeepIfQuotes($value);
12358 } else {
12359 $crits = array($value);
12360 }
12361
12362 $res = '';
12363 if (!is_array($fields)) {
12364 $fields = array($fields);
12365 }
12366 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
12367 foreach ($crits as $crit) { // Loop on each AND criteria
12368 $crit = trim($crit);
12369 $i2 = 0; // count the nb of valid criteria added for this this first criteria
12370 $newres = '';
12371
12372 foreach ($fields as $field) {
12373 if ($mode == 1) {
12374 $tmpcrits = explode('|', $crit);
12375 $i3 = 0; // count the nb of valid criteria added for this current field
12376 foreach ($tmpcrits as $tmpcrit) {
12377 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12378 continue;
12379 }
12380 $tmpcrit = trim($tmpcrit);
12381
12382 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12383
12384 $operator = '=';
12385 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
12386
12387 $reg = array();
12388 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
12389 if (!empty($reg[1])) {
12390 $operator = $reg[1];
12391 }
12392 if ($newcrit != '') {
12393 $numnewcrit = price2num($newcrit);
12394 if (is_numeric($numnewcrit)) {
12395 $newres .= $db->sanitize($field).' '.$operator.' '.((float) $numnewcrit); // should be a numeric
12396 } else {
12397 $newres .= '1 = 2'; // force false, we received a corrupted data
12398 }
12399 $i3++; // a criteria was added to string
12400 }
12401 }
12402 $i2++; // a criteria for 1 more field was added to string
12403 } elseif ($mode == 2 || $mode == -2) {
12404 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
12405 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -2 ? 'NOT ' : '');
12406 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
12407 if ($mode == -2) {
12408 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12409 }
12410 $i2++; // a criteria for 1 more field was added to string
12411 } elseif ($mode == 3 || $mode == -3) {
12412 $tmparray = explode(',', $crit);
12413 if (count($tmparray)) {
12414 $listofcodes = '';
12415 foreach ($tmparray as $val) {
12416 $val = trim($val);
12417 if ($val) {
12418 $listofcodes .= ($listofcodes ? ',' : '');
12419 $listofcodes .= "'".$db->escape($val)."'";
12420 }
12421 }
12422 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
12423 $i2++; // a criteria for 1 more field was added to string
12424 }
12425 if ($mode == -3) {
12426 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12427 }
12428 } elseif ($mode == 4) {
12429 $tmparray = explode(',', $crit);
12430 if (count($tmparray)) {
12431 $listofcodes = '';
12432 foreach ($tmparray as $val) {
12433 $val = trim($val);
12434 if ($val) {
12435 $newres .= ($i2 > 0 ? " OR (" : "(").$db->sanitize($field)." LIKE '".$db->escape($val).",%'";
12436 $newres .= ' OR '.$db->sanitize($field)." = '".$db->escape($val)."'";
12437 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val)."'";
12438 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val).",%'";
12439 $newres .= ')';
12440 $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)
12441 }
12442 }
12443 }
12444 } else { // $mode=0
12445 $tmpcrits = explode('|', $crit);
12446 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
12447 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
12448 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12449 continue;
12450 }
12451 $tmpcrit = trim($tmpcrit);
12452
12453 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
12454 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
12455 } else {
12456 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12457 }
12458
12459 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
12460 $newres .= $db->sanitize($field)." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
12461 } else {
12462 $tmpcrit2 = $tmpcrit;
12463 $tmpbefore = '%';
12464 $tmpafter = '%';
12465 $tmps = '';
12466
12467 if (preg_match('/^!/', $tmpcrit)) {
12468 $tmps .= $db->sanitize($field)." NOT LIKE "; // ! as exclude character
12469 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
12470 } else {
12471 $tmps .= $db->sanitize($field)." LIKE ";
12472 }
12473 $tmps .= "'";
12474
12475 if (preg_match('/^[\^\$]/', $tmpcrit)) {
12476 $tmpbefore = '';
12477 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
12478 }
12479 if (preg_match('/[\^\$]$/', $tmpcrit)) {
12480 $tmpafter = '';
12481 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
12482 }
12483
12484 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12485 $tmps = "(".$tmps;
12486 }
12487 $newres .= $tmps;
12488 $newres .= $tmpbefore;
12489 $newres .= $db->escape($tmpcrit2);
12490 $newres .= $tmpafter;
12491 $newres .= "'";
12492 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12493 $newres .= " OR ".$field." IS NULL)";
12494 }
12495 }
12496
12497 $i3++;
12498 }
12499
12500 $i2++; // a criteria for 1 more field was added to string
12501 }
12502 }
12503 if ($newres) {
12504 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
12505 }
12506 $i1++;
12507 }
12508 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
12509
12510 return $res;
12511}
12512
12519function showDirectDownloadLink($object)
12520{
12521 global $langs;
12522
12523 $out = '';
12524 $url = $object->getLastMainDocLink($object->element);
12525
12526 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
12527 if ($url) {
12528 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
12529 $out .= ajax_autoselect("directdownloadlink", '');
12530 } else {
12531 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
12532 }
12533
12534 return $out;
12535}
12536
12545function getImageFileNameForSize($file, $extName, $extImgTarget = '')
12546{
12547 $dirName = dirname($file);
12548 if ($dirName == '.') {
12549 $dirName = '';
12550 }
12551
12552 if (!in_array($extName, array('', '_small', '_mini'))) {
12553 return 'Bad parameter extName';
12554 }
12555
12556 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
12557 $fileName = basename($fileName);
12558
12559 if (empty($extImgTarget)) {
12560 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
12561 }
12562 if (empty($extImgTarget)) {
12563 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
12564 }
12565 if (empty($extImgTarget)) {
12566 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
12567 }
12568 if (empty($extImgTarget)) {
12569 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
12570 }
12571 if (empty($extImgTarget)) {
12572 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
12573 }
12574 if (empty($extImgTarget)) {
12575 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
12576 }
12577
12578 if (!$extImgTarget) {
12579 return $file;
12580 }
12581
12582 $subdir = '';
12583 if ($extName) {
12584 $subdir = 'thumbs/';
12585 }
12586
12587 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
12588}
12589
12590
12600function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
12601{
12602 global $conf, $langs;
12603
12604 if (empty($conf->use_javascript_ajax)) {
12605 return '';
12606 }
12607
12608 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
12609
12610 if ($alldata == 1) {
12611 if ($isAllowedForPreview) {
12612 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));
12613 } else {
12614 return array();
12615 }
12616 }
12617
12618 // old behavior, return a string
12619 if ($isAllowedForPreview) {
12620 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
12621 $title = $langs->transnoentities("Preview");
12622 //$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().
12623 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
12624
12625 // 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,
12626 // 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.
12627 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
12628 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
12629 } else {
12630 return '';
12631 }
12632}
12633
12640function getLabelSpecialCode($idcode)
12641{
12642 global $langs;
12643
12644 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
12645 if ($idcode > 10) {
12646 return 'Module ID '.$idcode;
12647 }
12648 if (!empty($arrayspecialines[$idcode])) {
12649 return $langs->trans($arrayspecialines[$idcode]);
12650 }
12651 return '';
12652}
12653
12662function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
12663{
12664 global $langs;
12665 $out = '<script nonce="'.getNonce().'">
12666 jQuery(document).ready(function () {
12667 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
12668 });
12669 </script>';
12670 if ($addlink) {
12671 if ($textonlink === 'image') {
12672 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
12673 } else {
12674 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
12675 }
12676 }
12677 return $out;
12678}
12679
12687function dolIsAllowedForPreview($file)
12688{
12689 // Check .noexe extension in filename
12690 if (preg_match('/\.noexe$/i', $file)) {
12691 return 0;
12692 }
12693
12694 // Check mime types
12695 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
12696 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
12697 $mime_preview[] = 'svg+xml';
12698 }
12699 //$mime_preview[]='vnd.oasis.opendocument.presentation';
12700 //$mime_preview[]='archive';
12701 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
12702 if ($num_mime !== false) {
12703 return 1;
12704 }
12705
12706 // By default, not allowed for preview
12707 return 0;
12708}
12709
12710
12720function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
12721{
12722 $mime = $default;
12723 $imgmime = 'other.png';
12724 $famime = 'file-o';
12725 $srclang = '';
12726
12727 $tmpfile = preg_replace('/\.noexe$/', '', $file);
12728
12729 // Plain text files
12730 if (preg_match('/\.txt$/i', $tmpfile)) {
12731 $mime = 'text/plain';
12732 $imgmime = 'text.png';
12733 $famime = 'file-alt';
12734 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
12735 $mime = 'text/richtext';
12736 $imgmime = 'text.png';
12737 $famime = 'file-alt';
12738 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
12739 $mime = 'text/csv';
12740 $imgmime = 'text.png';
12741 $famime = 'file-csv';
12742 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
12743 $mime = 'text/tab-separated-values';
12744 $imgmime = 'text.png';
12745 $famime = 'file-alt';
12746 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
12747 $mime = 'text/plain';
12748 $imgmime = 'text.png';
12749 $famime = 'file-alt';
12750 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
12751 $mime = 'text/plain';
12752 $imgmime = 'text.png';
12753 $srclang = 'ini';
12754 $famime = 'file-alt';
12755 } elseif (preg_match('/\.md$/i', $tmpfile)) {
12756 $mime = 'text/plain';
12757 $imgmime = 'text.png';
12758 $srclang = 'md';
12759 $famime = 'file-alt';
12760 } elseif (preg_match('/\.css$/i', $tmpfile)) {
12761 $mime = 'text/css';
12762 $imgmime = 'css.png';
12763 $srclang = 'css';
12764 $famime = 'file-alt';
12765 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
12766 $mime = 'text/plain';
12767 $imgmime = 'text.png';
12768 $srclang = 'lang';
12769 $famime = 'file-alt';
12770 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
12771 $mime = 'text/plain';
12772 $imgmime = 'text.png';
12773 $famime = 'file-alt';
12774 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
12775 $mime = 'text/html';
12776 $imgmime = 'html.png';
12777 $srclang = 'html';
12778 $famime = 'file-alt';
12779 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
12780 $mime = 'text/xml';
12781 $imgmime = 'other.png';
12782 $srclang = 'xml';
12783 $famime = 'file-alt';
12784 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
12785 $mime = 'text/xml';
12786 $imgmime = 'other.png';
12787 $srclang = 'xaml';
12788 $famime = 'file-alt';
12789 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
12790 $mime = 'text/plain';
12791 $imgmime = 'text.png';
12792 $srclang = 'bas';
12793 $famime = 'file-code';
12794 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
12795 $mime = 'text/plain';
12796 $imgmime = 'text.png';
12797 $srclang = 'c';
12798 $famime = 'file-code';
12799 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
12800 $mime = 'text/plain';
12801 $imgmime = 'text.png';
12802 $srclang = 'cpp';
12803 $famime = 'file-code';
12804 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
12805 $mime = 'text/plain';
12806 $imgmime = 'text.png';
12807 $srclang = 'cs';
12808 $famime = 'file-code';
12809 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
12810 $mime = 'text/plain';
12811 $imgmime = 'text.png';
12812 $srclang = 'h';
12813 $famime = 'file-code';
12814 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
12815 $mime = 'text/plain';
12816 $imgmime = 'text.png';
12817 $srclang = 'java';
12818 $famime = 'file-code';
12819 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
12820 $mime = 'text/plain';
12821 $imgmime = 'php.png';
12822 $srclang = 'php';
12823 $famime = 'file-code';
12824 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12825 $mime = 'text/plain';
12826 $imgmime = 'php.png';
12827 $srclang = 'php';
12828 $famime = 'file-code';
12829 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12830 $mime = 'text/plain';
12831 $imgmime = 'pl.png';
12832 $srclang = 'perl';
12833 $famime = 'file-code';
12834 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12835 $mime = 'text/plain';
12836 $imgmime = 'text.png';
12837 $srclang = 'sql';
12838 $famime = 'file-code';
12839 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12840 $mime = 'text/x-javascript';
12841 $imgmime = 'jscript.png';
12842 $srclang = 'js';
12843 $famime = 'file-code';
12844 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12845 $mime = 'application/vnd.oasis.opendocument.presentation';
12846 $imgmime = 'ooffice.png';
12847 $famime = 'file-powerpoint';
12848 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12849 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12850 $imgmime = 'ooffice.png';
12851 $famime = 'file-excel';
12852 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12853 $mime = 'application/vnd.oasis.opendocument.text';
12854 $imgmime = 'ooffice.png';
12855 $famime = 'file-word';
12856 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12857 $mime = 'application/msaccess';
12858 $imgmime = 'mdb.png';
12859 $famime = 'file';
12860 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12861 $mime = 'application/msword';
12862 $imgmime = 'doc.png';
12863 $famime = 'file-word';
12864 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12865 $mime = 'application/msword';
12866 $imgmime = 'doc.png';
12867 $famime = 'file-word';
12868 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12869 $mime = 'application/vnd.ms-excel';
12870 $imgmime = 'xls.png';
12871 $famime = 'file-excel';
12872 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12873 $mime = 'application/vnd.ms-excel';
12874 $imgmime = 'xls.png';
12875 $famime = 'file-excel';
12876 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12877 $mime = 'application/vnd.ms-excel';
12878 $imgmime = 'xls.png';
12879 $famime = 'file-excel';
12880 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12881 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12882 $imgmime = 'xls.png';
12883 $famime = 'file-excel';
12884 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12885 $mime = 'application/vnd.ms-powerpoint';
12886 $imgmime = 'ppt.png';
12887 $famime = 'file-powerpoint';
12888 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12889 $mime = 'application/x-mspowerpoint';
12890 $imgmime = 'ppt.png';
12891 $famime = 'file-powerpoint';
12892 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12893 $mime = 'application/pdf';
12894 $imgmime = 'pdf.png';
12895 $famime = 'file-pdf';
12896 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12897 $mime = 'text/x-bat';
12898 $imgmime = 'script.png';
12899 $srclang = 'dos';
12900 $famime = 'file-code';
12901 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12902 $mime = 'text/x-sh';
12903 $imgmime = 'script.png';
12904 $srclang = 'bash';
12905 $famime = 'file-code';
12906 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12907 $mime = 'text/x-ksh';
12908 $imgmime = 'script.png';
12909 $srclang = 'bash';
12910 $famime = 'file-code';
12911 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12912 $mime = 'text/x-bash';
12913 $imgmime = 'script.png';
12914 $srclang = 'bash';
12915 $famime = 'file-code';
12916 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12917 $mime = 'image/x-icon';
12918 $imgmime = 'image.png';
12919 $famime = 'file-image';
12920 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12921 $mime = 'image/jpeg';
12922 $imgmime = 'image.png';
12923 $famime = 'file-image';
12924 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12925 $mime = 'image/png';
12926 $imgmime = 'image.png';
12927 $famime = 'file-image';
12928 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12929 $mime = 'image/gif';
12930 $imgmime = 'image.png';
12931 $famime = 'file-image';
12932 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12933 $mime = 'image/bmp';
12934 $imgmime = 'image.png';
12935 $famime = 'file-image';
12936 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12937 $mime = 'image/tiff';
12938 $imgmime = 'image.png';
12939 $famime = 'file-image';
12940 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12941 $mime = 'image/svg+xml';
12942 $imgmime = 'image.png';
12943 $famime = 'file-image';
12944 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12945 $mime = 'image/webp';
12946 $imgmime = 'image.png';
12947 $famime = 'file-image';
12948 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12949 $mime = 'text/calendar';
12950 $imgmime = 'other.png';
12951 $famime = 'file-alt';
12952 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12953 $mime = 'text/calendar';
12954 $imgmime = 'other.png';
12955 $famime = 'file-alt';
12956 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12957 $mime = 'application/x-bittorrent';
12958 $imgmime = 'other.png';
12959 $famime = 'file-o';
12960 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12961 $mime = 'audio';
12962 $imgmime = 'audio.png';
12963 $famime = 'file-audio';
12964 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12965 $mime = 'video/mp4';
12966 $imgmime = 'video.png';
12967 $famime = 'file-video';
12968 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12969 $mime = 'video/ogg';
12970 $imgmime = 'video.png';
12971 $famime = 'file-video';
12972 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12973 $mime = 'video/webm';
12974 $imgmime = 'video.png';
12975 $famime = 'file-video';
12976 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12977 $mime = 'video/x-msvideo';
12978 $imgmime = 'video.png';
12979 $famime = 'file-video';
12980 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12981 $mime = 'video/divx';
12982 $imgmime = 'video.png';
12983 $famime = 'file-video';
12984 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12985 $mime = 'video/xvid';
12986 $imgmime = 'video.png';
12987 $famime = 'file-video';
12988 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12989 $mime = 'video';
12990 $imgmime = 'video.png';
12991 $famime = 'file-video';
12992 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12993 // application/xxx where zzz is zip, ...
12994 $mime = 'archive';
12995 $imgmime = 'archive.png';
12996 $famime = 'file-archive';
12997 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12998 $mime = 'application/octet-stream';
12999 $imgmime = 'other.png';
13000 $famime = 'file-o';
13001 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
13002 $mime = 'library';
13003 $imgmime = 'library.png';
13004 $famime = 'file-o';
13005 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13006 $mime = 'error';
13007 $imgmime = 'error.png';
13008 $famime = 'file-alt';
13009 }
13010
13011 if ($famime == 'file-o') {
13012 // file-o seems to not work in fontawesome 5
13013 $famime = 'file';
13014 }
13015
13016 // Return mimetype string
13017 switch ((int) $mode) {
13018 case 1:
13019 $tmp = explode('/', $mime);
13020 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13021 case 2:
13022 return $imgmime;
13023 case 3:
13024 return $srclang;
13025 case 4:
13026 return $famime;
13027 }
13028 return $mime;
13029}
13030
13042function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
13043{
13044 global $conf, $db;
13045
13046 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
13047
13048 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
13049
13050 if (is_null($dictvalues)) {
13051 $dictvalues = array();
13052
13053 $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
13054 if ($checkentity) {
13055 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
13056 }
13057
13058 $resql = $db->query($sql);
13059 if ($resql) {
13060 while ($obj = $db->fetch_object($resql)) {
13061 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
13062 }
13063 } else {
13064 dol_print_error($db);
13065 }
13066
13067 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
13068 }
13069
13070 if (!empty($dictvalues[$id])) {
13071 // Found
13072 $tmp = $dictvalues[$id];
13073 return (property_exists($tmp, $field) ? $tmp->$field : '');
13074 } else {
13075 // Not found
13076 return '';
13077 }
13078}
13079
13086function colorIsLight($stringcolor)
13087{
13088 $stringcolor = str_replace('#', '', $stringcolor);
13089 $res = -1;
13090 if (!empty($stringcolor)) {
13091 $res = 0;
13092 $tmp = explode(',', $stringcolor);
13093 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
13094 $r = $tmp[0];
13095 $g = $tmp[1];
13096 $b = $tmp[2];
13097 } else {
13098 $hexr = $stringcolor[0].$stringcolor[1];
13099 $hexg = $stringcolor[2].$stringcolor[3];
13100 $hexb = $stringcolor[4].$stringcolor[5];
13101 $r = hexdec($hexr);
13102 $g = hexdec($hexg);
13103 $b = hexdec($hexb);
13104 }
13105 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
13106 if ($bright > 0.6) {
13107 $res = 1;
13108 }
13109 }
13110 return $res;
13111}
13112
13121function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
13122{
13123 global $conf;
13124
13125 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
13126 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
13127 if (empty($menuentry['enabled'])) {
13128 return 0; // Entry disabled by condition
13129 }
13130 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
13131 $tmploops = explode('|', $menuentry['module']);
13132 $found = 0;
13133 foreach ($tmploops as $tmploop) {
13134 if (in_array($tmploop, $listofmodulesforexternal)) {
13135 $found++;
13136 break;
13137 }
13138 }
13139 if (!$found) {
13140 return 0; // Entry is for menus all excluded to external users
13141 }
13142 }
13143 if (!$menuentry['perms'] && $type_user) {
13144 return 0; // No permissions and user is external
13145 }
13146 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
13147 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
13148 }
13149 if (!$menuentry['perms']) {
13150 return 2; // No permissions and user is external
13151 }
13152 return 1;
13153}
13154
13162function roundUpToNextMultiple($n, $x = 5)
13163{
13164 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
13165 return (int) $result;
13166}
13167
13179function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
13180{
13181 $csstouse = 'badge';
13182 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
13183 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
13184 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
13185
13186 $attr = array(
13187 'class' => $csstouse
13188 );
13189
13190 if (empty($html)) {
13191 $html = $label;
13192 }
13193
13194 if (!empty($url)) {
13195 $attr['href'] = $url;
13196 }
13197
13198 if ($mode === 'dot') {
13199 $attr['class'] .= ' classfortooltip';
13200 $attr['title'] = $html;
13201 $attr['aria-label'] = $label;
13202 $html = '';
13203 }
13204
13205 // Override attr
13206 if (!empty($params['attr']) && is_array($params['attr'])) {
13207 foreach ($params['attr'] as $key => $value) {
13208 if ($key == 'class') {
13209 $attr['class'] .= ' '.$value;
13210 } elseif ($key == 'classOverride') {
13211 $attr['class'] = $value;
13212 } else {
13213 $attr[$key] = $value;
13214 }
13215 }
13216 }
13217
13218 // TODO: add hook
13219
13220 // escape all attribute
13221 $attr = array_map('dolPrintHTMLForAttribute', $attr);
13222
13223 $TCompiledAttr = array();
13224 foreach ($attr as $key => $value) {
13225 $TCompiledAttr[] = $key.'="'.$value.'"';
13226 }
13227
13228 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
13229
13230 $tag = !empty($url) ? 'a' : 'span';
13231
13232 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
13233}
13234
13235
13248function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
13249{
13250 global $conf;
13251
13252 $return = '';
13253 $dolGetBadgeParams = array();
13254
13255 if (!empty($params['badgeParams'])) {
13256 $dolGetBadgeParams = $params['badgeParams'];
13257 }
13258
13259 // TODO : add a hook
13260 if ($displayMode == 0) {
13261 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
13262 } elseif ($displayMode == 1) {
13263 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13264 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
13265 // Use status with images (for backward compatibility)
13266 $return = '';
13267 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
13268 $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>' : '');
13269
13270 // For small screen, we always use the short label instead of long label.
13271 if (!empty($conf->dol_optimize_smallscreen)) {
13272 if ($displayMode == 0) {
13273 $displayMode = 1;
13274 } elseif ($displayMode == 4) {
13275 $displayMode = 2;
13276 } elseif ($displayMode == 6) {
13277 $displayMode = 5;
13278 }
13279 }
13280
13281 // For backward compatibility. Image's filename are still in French, so we use this array to convert
13282 $statusImg = array(
13283 'status0' => 'statut0',
13284 'status1' => 'statut1',
13285 'status2' => 'statut2',
13286 'status3' => 'statut3',
13287 'status4' => 'statut4',
13288 'status5' => 'statut5',
13289 'status6' => 'statut6',
13290 'status7' => 'statut7',
13291 'status8' => 'statut8',
13292 'status9' => 'statut9'
13293 );
13294
13295 if (!empty($statusImg[$statusType])) {
13296 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
13297 } else {
13298 $htmlImg = img_picto($statusLabel, $statusType);
13299 }
13300
13301 if ($displayMode === 2) {
13302 $return = $htmlImg.' '.$htmlLabelShort;
13303 } elseif ($displayMode === 3) {
13304 $return = $htmlImg;
13305 } elseif ($displayMode === 4) {
13306 $return = $htmlImg.' '.$htmlLabel;
13307 } elseif ($displayMode === 5) {
13308 $return = $htmlLabelShort.' '.$htmlImg;
13309 } else { // $displayMode >= 6
13310 $return = $htmlLabel.' '.$htmlImg;
13311 }
13312 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
13313 // Use new badge
13314 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13315
13316 $dolGetBadgeParams['attr']['class'] = 'badge-status';
13317 if (empty($dolGetBadgeParams['attr']['title'])) {
13318 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
13319 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
13320 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
13321 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
13322 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
13323 }
13324
13325 if ($displayMode == 3) {
13326 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
13327 } elseif ($displayMode === 5) {
13328 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
13329 } else {
13330 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
13331 }
13332 }
13333
13334 return $return;
13335}
13336
13337
13373function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
13374{
13375 global $hookmanager, $action, $object, $langs;
13376
13377 // If $url is an array, we must build a dropdown button or recursively iterate over each value
13378 if (is_array($url)) {
13379 // Loop on $url array to remove entries of disabled modules
13380 foreach ($url as $key => $subbutton) {
13381 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
13382 unset($url[$key]);
13383 }
13384 }
13385
13386 $out = '';
13387
13388 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
13389 foreach ($url as $button) {
13390 if (!empty($button['lang'])) {
13391 $langs->load($button['lang']);
13392 }
13393 $label = $langs->trans($button['label']);
13394 $text = $button['text'] ?? '';
13395 $actionType = $button['actionType'] ?? '';
13396 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
13397 $id = $button['id'] ?? '';
13398 $userRight = $button['perm'] ?? 1;
13399 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
13400
13401 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
13402 }
13403 return $out;
13404 }
13405
13406 if (count($url) > 1) {
13407 $out .= '<div class="dropdown inline-block dropdown-holder">';
13408 $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>';
13409 $out .= '<div class="dropdown-content">';
13410 foreach ($url as $subbutton) {
13411 if (!empty($subbutton['lang'])) {
13412 $langs->load($subbutton['lang']);
13413 }
13414
13415 if (!empty($subbutton['urlraw'])) {
13416 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13417 } else {
13418 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13419 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13420 }
13421
13422 $subbuttonparam = array();
13423 if (!empty($subbutton['attr'])) {
13424 $subbuttonparam['attr'] = $subbutton['attr'];
13425 }
13426 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
13427
13428 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
13429 }
13430 $out .= "</div>";
13431 $out .= "</div>";
13432 } else {
13433 foreach ($url as $subbutton) { // Should loop on 1 record only
13434 if (!empty($subbutton['lang'])) {
13435 $langs->load($subbutton['lang']);
13436 }
13437
13438 if (!empty($subbutton['urlraw'])) {
13439 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13440 } else {
13441 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13442 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13443 }
13444
13445 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
13446 }
13447 }
13448
13449 return $out;
13450 }
13451
13452 // Here, $url is a simple link
13453
13454 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
13455 $class = "dropdown-item";
13456 } else {
13457 $class = 'butAction';
13458 if ($actionType == 'danger' || $actionType == 'delete') {
13459 $class = 'butActionDelete';
13460 if (!empty($url) && strpos($url, 'token=') === false) {
13461 $url .= '&token='.newToken();
13462 }
13463 }
13464 }
13465 $attr = array(
13466 'class' => $class,
13467 'href' => empty($url) ? '' : $url,
13468 'title' => $label
13469 );
13470
13471 if (empty($text)) {
13472 $text = $label;
13473 $attr['title'] = ''; // if html not set, leave label on title is redundant
13474 } else {
13475 $attr['title'] = $label;
13476 $attr['aria-label'] = $label;
13477 }
13478
13479 if (empty($userRight) || $userRight < 0) {
13480 $attr['class'] = 'butActionRefused';
13481 $attr['href'] = '';
13482 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
13483 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '').(empty($userRight) ? $langs->trans('NotEnoughPermissions') : '');
13484 }
13485
13486 if (!empty($id)) {
13487 $attr['id'] = $id;
13488 }
13489
13490 // Override attr
13491 if (!empty($params['attr']) && is_array($params['attr'])) {
13492 foreach ($params['attr'] as $key => $value) {
13493 if ($key == 'class') {
13494 $attr['class'] .= ' '.$value;
13495 } elseif ($key == 'classOverride') {
13496 $attr['class'] = $value;
13497 } else {
13498 $attr[$key] = $value;
13499 }
13500 }
13501 }
13502
13503 // automatic add tooltip when title is detected
13504 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13505 $attr['class'] .= ' classfortooltip';
13506 }
13507
13508 // Js Confirm button
13509 if ($userRight && !empty($params['confirm'])) {
13510 if (!is_array($params['confirm'])) {
13511 $params['confirm'] = array();
13512 }
13513
13514 if (empty($params['confirm']['url'])) {
13515 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
13516 }
13517
13518 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
13519 $attr['data-confirm-url'] = $params['confirm']['url'];
13520 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
13521 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
13522 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
13523 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
13524 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
13525 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
13526
13527 $attr['class'] .= ' butActionConfirm';
13528 }
13529
13530 if (isset($attr['href']) && empty($attr['href'])) {
13531 unset($attr['href']);
13532 }
13533
13534 $TCompiledAttr = array();
13535 foreach ($attr as $key => $value) {
13536 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
13537 // Not recommended
13538 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13539 } elseif ($key == 'href') {
13540 $value = dolPrintHTMLForAttributeUrl($value);
13541 } else {
13542 $value = dolPrintHTMLForAttribute($value);
13543 }
13544
13545 $TCompiledAttr[] = $key.'="'.$value.'"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13546 }
13547
13548 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
13549
13550 $tag = !empty($attr['href']) ? 'a' : 'span';
13551
13552
13553 $parameters = array(
13554 'TCompiledAttr' => $TCompiledAttr, // array
13555 'compiledAttributes' => $compiledAttributes, // string
13556 'attr' => $attr,
13557 'tag' => $tag,
13558 'label' => $label,
13559 'html' => $text,
13560 'actionType' => $actionType,
13561 'url' => $url,
13562 'id' => $id,
13563 'userRight' => $userRight,
13564 'params' => $params
13565 );
13566
13567 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
13568 if ($reshook < 0) {
13569 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
13570 }
13571
13572 if (empty($reshook)) {
13573 if (dol_textishtml($text)) { // If content already HTML encoded
13574 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
13575 } else {
13576 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
13577 }
13578 } else {
13579 return $hookmanager->resPrint;
13580 }
13581}
13582
13583
13592function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
13593{
13594 if (empty($url)) {
13595 return '';
13596 }
13597
13598 $parsedUrl = parse_url($url);
13599 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
13600 return $url;
13601 }
13602
13603 if (!empty($parsedUrl['query'])) {
13604 // Use parse_str() function to parse the string passed via URL
13605 parse_str($parsedUrl['query'], $urlQuery);
13606 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
13607 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
13608 }
13609 }
13610
13611 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
13612 $url = DOL_URL_ROOT.$url;
13613 }
13614
13615 return $url;
13616}
13617
13618
13625function dolGetButtonTitleSeparator($moreClass = "")
13626{
13627 return '<span class="button-title-separator '.$moreClass.'" ></span>';
13628}
13629
13636function getFieldErrorIcon($fieldValidationErrorMsg)
13637{
13638 $out = '';
13639 if (!empty($fieldValidationErrorMsg)) {
13640 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
13641 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
13642 $out .= '</span>';
13643 }
13644
13645 return $out;
13646}
13647
13660function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
13661{
13662 global $langs, $user;
13663
13664 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
13665 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
13666 return '';
13667 }
13668 // Fix old picto fa-th-list to use fa-grid-vertical instead
13669 if ($iconClass == 'fa fa-th-list imgforviewmode') {
13670 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
13671 }
13672
13673 $class = 'btnTitle';
13674 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
13675 $class .= ' btnTitlePlus';
13676 }
13677 $useclassfortooltip = 1;
13678
13679 if (!empty($params['morecss'])) {
13680 $class .= ' '.$params['morecss'];
13681 }
13682
13683 $attr = array(
13684 'class' => $class,
13685 'href' => empty($url) ? '' : $url
13686 );
13687
13688 if (!empty($helpText)) {
13689 $attr['title'] = $helpText;
13690 } elseif ($label) { // empty($attr['title']) &&
13691 $attr['title'] = $label;
13692 $useclassfortooltip = 0;
13693 }
13694
13695 if ($status == 2) {
13696 $attr['class'] .= ' btnTitleSelected';
13697 } elseif ($status <= 0) {
13698 $attr['class'] .= ' refused';
13699
13700 $attr['href'] = '';
13701
13702 if ($status == -1) { // disable
13703 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
13704 } elseif ($status == 0) { // Not enough permissions
13705 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
13706 }
13707 }
13708
13709 if (!empty($attr['title']) && $useclassfortooltip) {
13710 $attr['class'] .= ' classfortooltip';
13711 }
13712
13713 if (!empty($id)) {
13714 $attr['id'] = $id;
13715 }
13716
13717 // Override attr
13718 if (!empty($params['attr']) && is_array($params['attr'])) {
13719 foreach ($params['attr'] as $key => $value) {
13720 if ($key == 'class') {
13721 $attr['class'] .= ' '.$value;
13722 } elseif ($key == 'classOverride') {
13723 $attr['class'] = $value;
13724 } else {
13725 $attr[$key] = $value;
13726 }
13727 }
13728 }
13729
13730 if (isset($attr['href']) && empty($attr['href'])) {
13731 unset($attr['href']);
13732 }
13733
13734 // TODO : add a hook
13735
13736 // Generate attributes with escapement
13737 $TCompiledAttr = array();
13738 foreach ($attr as $key => $value) {
13739 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
13740 }
13741
13742 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
13743
13744 $tag = (empty($attr['href']) ? 'span' : 'a');
13745
13746 $button = '<'.$tag.' '.$compiledAttributes.'>';
13747 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
13748 if (!empty($params['forcenohideoftext'])) {
13749 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
13750 }
13751 $button .= '</'.$tag.'>';
13752
13753 return $button;
13754}
13755
13765function getElementProperties($elementType)
13766{
13767 global $conf, $db, $hookmanager;
13768
13769 $regs = array();
13770
13771 //$element_type='facture';
13772
13773 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
13774
13775 // Parse element/subelement
13776 $module = $elementType;
13777 $element = $elementType;
13778 $subelement = $elementType;
13779 $table_element = $elementType;
13780
13781 // If we ask a resource form external module (instead of default path)
13782 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
13783 $element = $subelement = $regs[1];
13784 $module = $regs[2];
13785 }
13786
13787 // If we ask a resource for a string with an element and a subelement
13788 // Example 'project_task'
13789 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
13790 $module = $element = $regs[1];
13791 $subelement = $regs[2];
13792 }
13793
13794 // Object lines will use parent classpath and module ref
13795 if (substr($elementType, -3) == 'det') {
13796 $module = preg_replace('/det$/', '', $element);
13797 $subelement = preg_replace('/det$/', '', $subelement);
13798 $classpath = $module.'/class';
13799 $classfile = $module;
13800 $classname = preg_replace('/det$/', 'Line', $element);
13801 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
13802 $classname = preg_replace('/det$/', 'Ligne', $element);
13803 }
13804 }
13805 // For compatibility and to work with non standard path
13806 if ($elementType == "action" || $elementType == "actioncomm") {
13807 $classpath = 'comm/action/class';
13808 $subelement = 'Actioncomm';
13809 $module = 'agenda';
13810 $table_element = 'actioncomm';
13811 } elseif ($elementType == 'cronjob') {
13812 $classpath = 'cron/class';
13813 $module = 'cron';
13814 $table_element = 'cron';
13815 } elseif ($elementType == 'adherent_type') {
13816 $classpath = 'adherents/class';
13817 $classfile = 'adherent_type';
13818 $module = 'adherent';
13819 $subelement = 'adherent_type';
13820 $classname = 'AdherentType';
13821 $table_element = 'adherent_type';
13822 } elseif ($elementType == 'bank_account') {
13823 $classpath = 'compta/bank/class';
13824 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13825 $classfile = 'account';
13826 $classname = 'Account';
13827 } elseif ($elementType == 'bank_line') {
13828 $classpath = 'compta/bank/class';
13829 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13830 $classfile = 'account';
13831 $classname = 'AccountLine';
13832 } elseif ($elementType == 'category') {
13833 $classpath = 'categories/class';
13834 $module = 'categorie';
13835 $subelement = 'categorie';
13836 $table_element = 'categorie';
13837 } elseif ($elementType == 'contact') {
13838 $classpath = 'contact/class';
13839 $classfile = 'contact';
13840 $module = 'societe';
13841 $subelement = 'contact';
13842 $table_element = 'socpeople';
13843 } elseif ($elementType == 'inventory') {
13844 $module = 'product';
13845 $classpath = 'product/inventory/class';
13846 } elseif ($elementType == 'inventoryline') {
13847 $module = 'product';
13848 $classpath = 'product/inventory/class';
13849 $table_element = 'inventorydet';
13850 $parent_element = 'inventory';
13851 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
13852 $module = 'stock';
13853 $classpath = 'product/stock/class';
13854 $classfile = 'entrepot';
13855 $classname = 'Entrepot';
13856 $table_element = 'entrepot';
13857 } elseif ($elementType == 'project') {
13858 $classpath = 'projet/class';
13859 $module = 'projet';
13860 $table_element = 'projet';
13861 } elseif ($elementType == 'project_task') {
13862 $classpath = 'projet/class';
13863 $module = 'projet';
13864 $subelement = 'task';
13865 $table_element = 'projet_task';
13866 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13867 $classpath = 'compta/facture/class';
13868 $module = 'facture';
13869 $subelement = 'facture';
13870 $table_element = 'facture';
13871 } elseif ($elementType == 'facturedet') {
13872 $classpath = 'compta/facture/class';
13873 $classfile = 'facture';
13874 $classname = 'FactureLigne';
13875 $module = 'facture';
13876 $table_element = 'facturedet';
13877 $parent_element = 'facture';
13878 } elseif ($elementType == 'facturerec') {
13879 $classpath = 'compta/facture/class';
13880 $classfile = 'facture-rec';
13881 $module = 'facture';
13882 $classname = 'FactureRec';
13883 } elseif ($elementType == 'commande' || $elementType == 'order') {
13884 $classpath = 'commande/class';
13885 $module = 'commande';
13886 $subelement = 'commande';
13887 $table_element = 'commande';
13888 } elseif ($elementType == 'commandedet') {
13889 $classpath = 'commande/class';
13890 $classfile = 'commande';
13891 $classname = 'OrderLine';
13892 $module = 'commande';
13893 $table_element = 'commandedet';
13894 $parent_element = 'commande';
13895 } elseif ($elementType == 'propal') {
13896 $classpath = 'comm/propal/class';
13897 $table_element = 'propal';
13898 } elseif ($elementType == 'propaldet') {
13899 $classpath = 'comm/propal/class';
13900 $classfile = 'propal';
13901 $subelement = 'propaleligne';
13902 $module = 'propal';
13903 $table_element = 'propaldet';
13904 $parent_element = 'propal';
13905 } elseif ($elementType == 'shipping') {
13906 $classpath = 'expedition/class';
13907 $classfile = 'expedition';
13908 $classname = 'Expedition';
13909 $module = 'expedition';
13910 $table_element = 'expedition';
13911 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13912 $classpath = 'expedition/class';
13913 $classfile = 'expedition';
13914 $classname = 'ExpeditionLigne';
13915 $module = 'expedition';
13916 $table_element = 'expeditiondet';
13917 $parent_element = 'expedition';
13918 } elseif ($elementType == 'delivery_note') {
13919 $classpath = 'delivery/class';
13920 $subelement = 'delivery';
13921 $module = 'expedition';
13922 } elseif ($elementType == 'delivery') {
13923 $classpath = 'delivery/class';
13924 $subelement = 'delivery';
13925 $module = 'expedition';
13926 } elseif ($elementType == 'deliverydet') {
13927 // @todo
13928 } elseif ($elementType == 'supplier_proposal') {
13929 $classpath = 'supplier_proposal/class';
13930 $module = 'supplier_proposal';
13931 $element = 'supplierproposal';
13932 $classfile = 'supplier_proposal';
13933 $subelement = 'supplierproposal';
13934 } elseif ($elementType == 'supplier_proposaldet') {
13935 $classpath = 'supplier_proposal/class';
13936 $module = 'supplier_proposal';
13937 $classfile = 'supplier_proposal';
13938 $classname = 'SupplierProposalLine';
13939 $table_element = 'supplier_proposaldet';
13940 $parent_element = 'supplier_proposal';
13941 } elseif ($elementType == 'contract') {
13942 $classpath = 'contrat/class';
13943 $module = 'contrat';
13944 $subelement = 'contrat';
13945 $table_element = 'contract';
13946 } elseif ($elementType == 'contratdet') {
13947 $classpath = 'contrat/class';
13948 $module = 'contrat';
13949 $table_element = 'contratdet';
13950 $parent_element = 'contrat';
13951 } elseif ($elementType == 'mailing') {
13952 $classpath = 'comm/mailing/class';
13953 $module = 'mailing';
13954 $classfile = 'mailing';
13955 $classname = 'Mailing';
13956 $subelement = '';
13957 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13958 $classpath = 'adherents/class';
13959 $module = 'adherent';
13960 $subelement = 'adherent';
13961 $table_element = 'adherent';
13962 } elseif ($elementType == 'subscription') {
13963 $classpath = 'adherents/class';
13964 $classfile = 'subscription';
13965 $module = 'adherent';
13966 $subelement = 'subscription';
13967 $classname = 'Subscription';
13968 $table_element = 'subscription';
13969 } elseif ($elementType == 'usergroup') {
13970 $classpath = 'user/class';
13971 $module = 'user';
13972 } elseif ($elementType == 'mo') {
13973 $classpath = 'mrp/class';
13974 $classfile = 'mo';
13975 $classname = 'Mo';
13976 $module = 'mrp';
13977 $subelement = '';
13978 $table_element = 'mrp_mo';
13979 } elseif ($elementType == 'mrp_production') {
13980 $classpath = 'mrp/class';
13981 $classfile = 'mo';
13982 $classname = 'MoLine';
13983 $module = 'mrp';
13984 $subelement = '';
13985 $table_element = 'mrp_production';
13986 $parent_element = 'mo';
13987 } elseif ($elementType == 'cabinetmed_cons') {
13988 $classpath = 'cabinetmed/class';
13989 $module = 'cabinetmed';
13990 $subelement = 'cabinetmedcons';
13991 $table_element = 'cabinetmedcons';
13992 } elseif ($elementType == 'fichinter') {
13993 $classpath = 'fichinter/class';
13994 $module = 'ficheinter';
13995 $subelement = 'fichinter';
13996 $table_element = 'fichinter';
13997 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13998 $classpath = 'resource/class';
13999 $module = 'resource';
14000 $subelement = 'dolresource';
14001 $table_element = 'resource';
14002 } elseif ($elementType == 'opensurvey_sondage') {
14003 $classpath = 'opensurvey/class';
14004 $module = 'opensurvey';
14005 $subelement = 'opensurveysondage';
14006 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' ||$elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
14007 $classpath = 'fourn/class';
14008 $module = 'fournisseur';
14009 $classfile = 'fournisseur.commande';
14010 $element = 'order_supplier';
14011 $subelement = '';
14012 $classname = 'CommandeFournisseur';
14013 $table_element = 'commande_fournisseur';
14014 } elseif ($elementType == 'commande_fournisseurdet') {
14015 $classpath = 'fourn/class';
14016 $module = 'fournisseur';
14017 $classfile = 'fournisseur.commande';
14018 $element = 'commande_fournisseurdet';
14019 $subelement = '';
14020 $classname = 'CommandeFournisseurLigne';
14021 $table_element = 'commande_fournisseurdet';
14022 $parent_element = 'commande_fournisseur';
14023 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
14024 $classpath = 'fourn/class';
14025 $module = 'fournisseur';
14026 $classfile = 'fournisseur.facture';
14027 $element = 'invoice_supplier';
14028 $subelement = '';
14029 $classname = 'FactureFournisseur';
14030 $table_element = 'facture_fourn';
14031 } elseif ($elementType == 'facture_fourn_det') {
14032 $classpath = 'fourn/class';
14033 $module = 'fournisseur';
14034 $classfile = 'fournisseur.facture';
14035 $element = 'facture_fourn_det';
14036 $subelement = '';
14037 $classname = 'SupplierInvoiceLine';
14038 $table_element = 'facture_fourn_det';
14039 $parent_element = 'invoice_supplier';
14040 } elseif ($elementType == "service") {
14041 $classpath = 'product/class';
14042 $subelement = 'product';
14043 $table_element = 'product';
14044 } elseif ($elementType == 'salary') {
14045 $classpath = 'salaries/class';
14046 $module = 'salaries';
14047 } elseif ($elementType == 'payment_salary') {
14048 $classpath = 'salaries/class';
14049 $classfile = 'paymentsalary';
14050 $classname = 'PaymentSalary';
14051 $module = 'salaries';
14052 } elseif ($elementType == 'productlot') {
14053 $module = 'productbatch';
14054 $classpath = 'product/stock/class';
14055 $classfile = 'productlot';
14056 $classname = 'Productlot';
14057 $element = 'productlot';
14058 $subelement = '';
14059 $table_element = 'product_lot';
14060 } elseif ($elementType == 'societeaccount') {
14061 $classpath = 'societe/class';
14062 $classfile = 'societeaccount';
14063 $classname = 'SocieteAccount';
14064 $module = 'societe';
14065 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
14066 $classpath = 'website/class';
14067 $classfile = 'websitepage';
14068 $classname = 'Websitepage';
14069 $module = 'website';
14070 $subelement = 'websitepage';
14071 $table_element = 'website_page';
14072 } elseif ($elementType == 'fiscalyear') {
14073 $classpath = 'core/class';
14074 $module = 'accounting';
14075 $subelement = 'fiscalyear';
14076 } elseif ($elementType == 'chargesociales') {
14077 $classpath = 'compta/sociales/class';
14078 $module = 'tax';
14079 $table_element = 'chargesociales';
14080 } elseif ($elementType == 'tva') {
14081 $classpath = 'compta/tva/class';
14082 $module = 'tax';
14083 $subdir = '/vat';
14084 $table_element = 'tva';
14085 } elseif ($elementType == 'emailsenderprofile') {
14086 $module = '';
14087 $classpath = 'core/class';
14088 $classfile = 'emailsenderprofile';
14089 $classname = 'EmailSenderProfile';
14090 $table_element = 'c_email_senderprofile';
14091 $subelement = '';
14092 } elseif ($elementType == 'conferenceorboothattendee') {
14093 $classpath = 'eventorganization/class';
14094 $classfile = 'conferenceorboothattendee';
14095 $classname = 'ConferenceOrBoothAttendee';
14096 $module = 'eventorganization';
14097 } elseif ($elementType == 'conferenceorbooth') {
14098 $classpath = 'eventorganization/class';
14099 $classfile = 'conferenceorbooth';
14100 $classname = 'ConferenceOrBooth';
14101 $module = 'eventorganization';
14102 } elseif ($elementType == 'ccountry') {
14103 $module = '';
14104 $classpath = 'core/class';
14105 $classfile = 'ccountry';
14106 $classname = 'Ccountry';
14107 $table_element = 'c_country';
14108 $subelement = '';
14109 } elseif ($elementType == 'ecmfiles') {
14110 $module = 'ecm';
14111 $classpath = 'ecm/class';
14112 $classfile = 'ecmfiles';
14113 $classname = 'Ecmfiles';
14114 $table_element = 'ecmfiles';
14115 $subelement = '';
14116 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
14117 $module = 'knowledgemanagement';
14118 $classpath = 'knowledgemanagement/class';
14119 $classfile = 'knowledgerecord';
14120 $classname = 'KnowledgeRecord';
14121 $table_element = 'knowledgemanagement_knowledgerecord';
14122 $subelement = '';
14123 } elseif ($elementType == 'customer') {
14124 $module = 'societe';
14125 $classpath = 'societe/class';
14126 $classfile = 'client';
14127 $classname = 'Client';
14128 $table_element = 'societe';
14129 $subelement = '';
14130 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
14131 $module = 'societe';
14132 $classpath = 'fourn/class';
14133 $classfile = 'fournisseur';
14134 $classname = 'Fournisseur';
14135 $table_element = 'societe';
14136 $subelement = '';
14137 }
14138
14139
14140 if (empty($classfile)) {
14141 $classfile = strtolower($subelement);
14142 }
14143 if (empty($classname)) {
14144 $classname = ucfirst($subelement);
14145 }
14146 if (empty($classpath)) {
14147 $classpath = $module.'/class';
14148 }
14149
14150 //print 'getElementProperties subdir='.$subdir;
14151
14152 // Set dir_output
14153 if ($module && isset($conf->$module)) { // The generic case
14154 if (!empty($conf->$module->multidir_output[$conf->entity])) {
14155 $dir_output = $conf->$module->multidir_output[$conf->entity];
14156 } elseif (!empty($conf->$module->output[$conf->entity])) {
14157 $dir_output = $conf->$module->output[$conf->entity];
14158 } elseif (!empty($conf->$module->dir_output)) {
14159 $dir_output = $conf->$module->dir_output;
14160 }
14161 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
14162 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
14163 } elseif (!empty($conf->$module->temp[$conf->entity])) {
14164 $dir_temp = $conf->$module->temp[$conf->entity];
14165 } elseif (!empty($conf->$module->dir_temp)) {
14166 $dir_temp = $conf->$module->dir_temp;
14167 }
14168 }
14169
14170 // Overwrite value for special cases
14171 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
14172 $dir_output = $conf->fournisseur->commande->dir_output;
14173 $dir_temp = $conf->fournisseur->commande->dir_temp;
14174 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
14175 $dir_output = $conf->fournisseur->facture->dir_output;
14176 $dir_temp = $conf->fournisseur->facture->dir_temp;
14177 }
14178 $dir_output .= $subdir;
14179 $dir_temp .= $subdir;
14180
14181 $elementProperties = array(
14182 'module' => $module,
14183 'element' => $element,
14184 'table_element' => $table_element,
14185 'subelement' => $subelement,
14186 'classpath' => $classpath,
14187 'classfile' => $classfile,
14188 'classname' => $classname,
14189 'dir_output' => $dir_output,
14190 'dir_temp' => $dir_temp,
14191 'parent_element' => $parent_element,
14192 );
14193
14194
14195 // Add hook
14196 if (!is_object($hookmanager)) {
14197 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14198 $hookmanager = new HookManager($db);
14199 }
14200 $hookmanager->initHooks(array('elementproperties'));
14201
14202
14203 // Hook params
14204 $parameters = array(
14205 'elementType' => $elementType,
14206 'elementProperties' => $elementProperties
14207 );
14208
14209 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
14210
14211 if ($reshook) {
14212 $elementProperties = $hookmanager->resArray;
14213 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
14214 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
14215 }
14216
14217 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
14218 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
14219 unset($hookmanager->contextarray[$key]);
14220 }
14221
14222 return $elementProperties;
14223}
14224
14237function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
14238{
14239 global $db, $conf;
14240
14241 $ret = 0;
14242
14243 $element_prop = getElementProperties($element_type);
14244
14245 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
14246 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
14247 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
14248 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
14249 // of service and we will return properties of a product.
14250 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
14251 } elseif ($element_prop['module'] == 'societeaccount') {
14252 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
14253 } else {
14254 $ismodenabled = isModEnabled($element_prop['module']);
14255 }
14256 //var_dump('element_type='.$element_type);
14257 //var_dump($element_prop);
14258 //var_dump($element_prop['module'].' '.$ismodenabled);
14259 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
14260 if ($useCache === 1 && $element_id > 0
14261 && !empty($conf->cache['fetchObjectByElement'][$element_type])
14262 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14263 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14264 ) {
14265 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
14266 }
14267
14268 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14269
14270 if (class_exists($element_prop['classname'])) {
14271 $className = $element_prop['classname'];
14272 $objecttmp = new $className($db);
14273 '@phan-var-force CommonObject $objecttmp';
14274
14275 if ($element_id > 0 || !empty($element_ref)) {
14276 $ret = $objecttmp->fetch($element_id, $element_ref);
14277 if ($ret >= 0) {
14278 if (empty($objecttmp->module)) {
14279 $objecttmp->module = $element_prop['module'];
14280 }
14281
14282 if ($useCache > 0) {
14283 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
14284 $conf->cache['fetchObjectByElement'][$element_type] = [];
14285 }
14286
14287 // Manage cache limit
14288 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
14289 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
14290 }
14291
14292 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
14293 }
14294
14295 return $objecttmp;
14296 }
14297 } else {
14298 return $objecttmp; // returned an object without fetch
14299 }
14300 } else {
14301 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14302 return -1;
14303 }
14304 }
14305
14306 return $ret;
14307}
14308
14314function getExecutableContent()
14315{
14316 $arrayofregexextension = array(
14317 'htm', 'html', 'shtml', 'js', 'phar', 'php', 'php3', 'php4', 'php5', 'phtml', 'pht', 'pl', 'py', 'cgi', 'ksh', 'sh', 'shtml',
14318 'bash', 'bat', 'cmd', 'wpk', 'exe', 'dmg', 'appimage'
14319 );
14320
14321 return $arrayofregexextension;
14322}
14323
14330function isAFileWithExecutableContent($filename)
14331{
14332 $arrayofregexextension = getExecutableContent();
14333
14334 foreach ($arrayofregexextension as $fileextension) {
14335 if (preg_match('/\.'.preg_quote($fileextension, '/').'$/i', $filename)) {
14336 return true;
14337 }
14338 }
14339
14340 return false;
14341}
14342
14350function newToken()
14351{
14352 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
14353}
14354
14362function currentToken()
14363{
14364 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
14365}
14366
14372function getNonce()
14373{
14374 global $conf;
14375
14376 if (empty($conf->cache['nonce'])) {
14377 $conf->cache['nonce'] = dolGetRandomBytes(8);
14378 }
14379
14380 return $conf->cache['nonce'];
14381}
14382
14383
14397function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
14398{
14399 global $langs;
14400
14401 print '<div class="div-table-responsive-no-min">';
14402 print '<table class="noborder centpercent">';
14403 print '<tr class="liste_titre">';
14404
14405 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
14406
14407 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
14408
14409 if (!empty($link)) {
14410 if (!empty($arguments)) {
14411 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
14412 } else {
14413 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
14414 }
14415 }
14416
14417 if ($number > -1) {
14418 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
14419 } elseif (!empty($link)) {
14420 print '<span class="badge marginleftonlyshort">...</span>';
14421 }
14422
14423 if (!empty($link)) {
14424 print '</a>';
14425 }
14426
14427 print '</th>';
14428
14429 if ($number < 0 && !empty($link)) {
14430 print '<th class="right">';
14431 print '</th>';
14432 }
14433
14434 print '</tr>';
14435}
14436
14445function finishSimpleTable($addLineBreak = false)
14446{
14447 print '</table>';
14448 print '</div>';
14449
14450 if ($addLineBreak) {
14451 print '<br>';
14452 }
14453}
14454
14466function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
14467{
14468 global $langs;
14469
14470 if ($num === 0) {
14471 print '<tr class="oddeven">';
14472 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
14473 print '</tr>';
14474 return;
14475 }
14476
14477 if ($nbofloop === 0) {
14478 // don't show a summary line
14479 return;
14480 }
14481
14482 /* Case already handled above, commented to satisfy phpstan.
14483 if ($num === 0) {
14484 $colspan = $tableColumnCount;
14485 } else
14486 */
14487 if ($num > $nbofloop) {
14488 $colspan = $tableColumnCount;
14489 } else {
14490 $colspan = $tableColumnCount - 1;
14491 }
14492
14493 if ($extraRightColumn) {
14494 $colspan--;
14495 }
14496
14497 print '<tr class="liste_total">';
14498
14499 if ($nbofloop > 0 && $num > $nbofloop) {
14500 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
14501 } else {
14502 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
14503 print '<td class="right centpercent">'.price($total).'</td>';
14504 }
14505
14506 if ($extraRightColumn) {
14507 print '<td></td>';
14508 }
14509
14510 print '</tr>';
14511}
14512
14521function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
14522{
14523 if ($method == -1) {
14524 $method = 0;
14525 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
14526 $method = 1;
14527 }
14528 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
14529 $method = 2;
14530 }
14531 }
14532
14533 // Be sure we don't have output buffering enabled to have readfile working correctly
14534 while (ob_get_level()) {
14535 ob_end_flush();
14536 }
14537
14538 // Solution 0
14539 if ($method == 0) {
14540 readfile($fullpath_original_file_osencoded);
14541 } elseif ($method == 1) {
14542 // Solution 1
14543 $handle = fopen($fullpath_original_file_osencoded, "rb");
14544 while (!feof($handle)) {
14545 print fread($handle, 8192);
14546 }
14547 fclose($handle);
14548 } elseif ($method == 2) {
14549 // Solution 2
14550 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
14551 $handle2 = fopen("php://output", "wb");
14552 stream_copy_to_stream($handle1, $handle2);
14553 fclose($handle1);
14554 fclose($handle2);
14555 }
14556}
14557
14567function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
14568{
14569 global $langs;
14570
14571 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
14572
14573 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
14574 if ($texttoshow === 'none') {
14575 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14576 $result .= '<span class="clipboardCPValueToPrint"></span>';
14577 } elseif ($texttoshow) {
14578 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14579 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
14580 } else {
14581 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14582 }
14583 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
14584 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
14585 $result .= '<span class="clipboardCPText"></span>';
14586 $result .= '</span>';
14587
14588 return $result;
14589}
14590
14591
14598function jsonOrUnserialize($stringtodecode)
14599{
14600 $result = json_decode($stringtodecode);
14601 if ($result === null) {
14602 $result = unserialize($stringtodecode);
14603 }
14604
14605 return $result;
14606}
14607
14608
14625function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
14626{
14627 global $db, $user;
14628
14629 if (is_null($filter) || !is_string($filter) || $filter === '') {
14630 return '';
14631 }
14632 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
14633 $filter = '(' . $filter . ')';
14634 }
14635
14636 $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'
14637 $firstandlastparenthesis = 0;
14638
14639 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
14640 if ($noerror) {
14641 return '1 = 2';
14642 } else {
14643 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
14644 }
14645 }
14646
14647 // Test the filter syntax
14648 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
14649 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
14650 // If the string result contains something else than '()', the syntax was wrong
14651
14652 if (preg_match('/[^\‍(\‍)]/', $t)) {
14653 $tmperrorstr = 'Bad syntax of the search string';
14654 $errorstr = 'Bad syntax of the search string: '.$filter;
14655 if ($noerror) {
14656 return '1 = 2';
14657 } else {
14658 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
14659 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
14660 }
14661 }
14662
14663 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
14664
14665 if (is_object($db)) {
14666 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
14667 }
14668 if (is_object($user)) {
14669 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
14670 }
14671
14672 return $ret;
14673}
14674
14682function dolForgeExplodeAnd($sqlfilters)
14683{
14684 $arrayofandtags = array();
14685 $nbofchars = dol_strlen($sqlfilters);
14686
14687 $error = '';
14688 $parenthesislevel = 0;
14689 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
14690 if (!$result) {
14691 return array();
14692 }
14693 if ($parenthesislevel >= 1) {
14694 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
14695 }
14696
14697 $i = 0;
14698 $s = '';
14699 $countparenthesis = 0;
14700 while ($i < $nbofchars) {
14701 $char = dol_substr($sqlfilters, $i, 1);
14702
14703 if ($char == '(') {
14704 $countparenthesis++;
14705 } elseif ($char == ')') {
14706 $countparenthesis--;
14707 }
14708
14709 if ($countparenthesis == 0) {
14710 $char2 = dol_substr($sqlfilters, $i + 1, 1);
14711 $char3 = dol_substr($sqlfilters, $i + 2, 1);
14712 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
14713 // We found a AND
14714 $s = trim($s);
14715 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14716 $s = '('.$s.')';
14717 }
14718 $arrayofandtags[] = $s;
14719 $s = '';
14720 $i += 2;
14721 } else {
14722 $s .= $char;
14723 }
14724 } else {
14725 $s .= $char;
14726 }
14727 $i++;
14728 }
14729 if ($s) {
14730 $s = trim($s);
14731 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14732 $s = '('.$s.')';
14733 }
14734 $arrayofandtags[] = $s;
14735 }
14736
14737 return $arrayofandtags;
14738}
14739
14749function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
14750{
14751 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
14752 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
14753 $tmp = $sqlfilters;
14754
14755 $nb = dol_strlen($tmp);
14756 $counter = 0;
14757 $parenthesislevel = 0;
14758
14759 $error = '';
14760
14761 $i = 0;
14762 while ($i < $nb) {
14763 $char = dol_substr($tmp, $i, 1);
14764
14765 if ($char == '(') {
14766 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
14767 // We open a parenthesis and it is the first char
14768 $parenthesislevel++;
14769 }
14770 $counter++;
14771 } elseif ($char == ')') {
14772 $nbcharremaining = ($nb - $i - 1);
14773 if ($nbcharremaining >= $counter) {
14774 $parenthesislevel = min($parenthesislevel, $counter - 1);
14775 }
14776 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
14777 $parenthesislevel = $counter;
14778 }
14779 $counter--;
14780 }
14781
14782 if ($counter < 0) {
14783 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14784 $parenthesislevel = 0;
14785 dol_syslog($error, LOG_WARNING);
14786 return false;
14787 }
14788
14789 $i++;
14790 }
14791
14792 if ($counter > 0) {
14793 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14794 $parenthesislevel = 0;
14795 dol_syslog($error, LOG_WARNING);
14796 return false;
14797 }
14798
14799 return true;
14800}
14801
14809function dolForgeDummyCriteriaCallback($matches)
14810{
14811 //dol_syslog("Convert matches ".$matches[1]);
14812 if (empty($matches[1])) {
14813 return '';
14814 }
14815 $tmp = explode(':', $matches[1]);
14816 if (count($tmp) < 3) {
14817 return '';
14818 }
14819
14820 return '()'; // An empty criteria
14821}
14822
14831function dolForgeSQLCriteriaCallback($matches)
14832{
14833 global $db;
14834
14835 //dol_syslog("Convert matches ".$matches[1]);
14836 if (empty($matches[1])) {
14837 return '';
14838 }
14839 $tmp = explode(':', $matches[1], 3);
14840 if (count($tmp) < 3) {
14841 return '';
14842 }
14843
14844 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
14845
14846 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
14847
14848 $realOperator = [
14849 'NOTLIKE' => 'NOT LIKE',
14850 'ISNOT' => 'IS NOT',
14851 'NOTIN' => 'NOT IN',
14852 '!=' => '<>',
14853 ];
14854
14855 if (array_key_exists($operator, $realOperator)) {
14856 $operator = $realOperator[$operator];
14857 }
14858
14859 $tmpescaped = $tmp[2];
14860
14861 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
14862
14863 $regbis = array();
14864
14865 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)
14866 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
14867 $tmpescaped2 = '(';
14868 // Explode and sanitize each element in list
14869 $tmpelemarray = explode(',', $tmpescaped);
14870 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
14871 $reg = array();
14872 $tmpelem = trim($tmpelem);
14873 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
14874 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
14875 } elseif (preg_match('/^[0-9]+$/', (string) $tmpelem)) { // if only 0-9 chars, no .
14876 $tmpelemarray[$tmpkey] = (int) $tmpelem;
14877 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
14878 $tmpelemarray[$tmpkey] = (float) $tmpelem;
14879 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
14880 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
14881 } else {
14882 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
14883 }
14884 }
14885 $tmpescaped2 .= implode(',', $tmpelemarray);
14886 $tmpescaped2 .= ')';
14887
14888 $tmpescaped = $tmpescaped2;
14889 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14890 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14891 $tmpescaped = $regbis[1];
14892 }
14893 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14894 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14895 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14896 // TODO Retrieve type of field for $operand field name.
14897 // So we can complete format. For example we could complete a year with month and day.
14898 $tmpescaped = "'".$db->escape($regbis[1])."'";
14899 } else {
14900 if (strtoupper($tmpescaped) == 'NULL') {
14901 $tmpescaped = 'NULL';
14902 } elseif (preg_match('/^[0-9]+$/', (string) $tmpescaped)) { // if only 0-9 chars, no .
14903 $tmpescaped = (int) $tmpescaped;
14904 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
14905 $tmpescaped = (float) $tmpescaped;
14906 } else {
14907 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
14908 }
14909 }
14910
14911 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14912}
14913
14914
14924function getTimelineIcon($actionstatic, &$histo, $key)
14925{
14926 global $langs;
14927
14928 $out = '<!-- timeline icon -->'."\n";
14929 $iconClass = 'fa fa-comments';
14930 $img_picto = '';
14931 $colorClass = '';
14932 $pictoTitle = '';
14933
14934 if ($histo[$key]['percent'] == -1) {
14935 $colorClass = 'timeline-icon-not-applicble';
14936 $pictoTitle = $langs->trans('StatusNotApplicable');
14937 } elseif ($histo[$key]['percent'] == 0) {
14938 $colorClass = 'timeline-icon-todo';
14939 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14940 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14941 $colorClass = 'timeline-icon-in-progress';
14942 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14943 } elseif ($histo[$key]['percent'] >= 100) {
14944 $colorClass = 'timeline-icon-done';
14945 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14946 }
14947
14948 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14949 $iconClass = 'fa fa-ticket';
14950 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14951 $iconClass = 'fa fa-pencilxxx';
14952 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14953 $iconClass = 'fa fa-comments';
14954 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14955 $iconClass = 'fa fa-mask';
14956 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14957 if ($actionstatic->type_picto) {
14958 $img_picto = img_picto('', $actionstatic->type_picto);
14959 } else {
14960 if ($actionstatic->type_code == 'AC_RDV') {
14961 $iconClass = 'fa fa-handshake';
14962 } elseif ($actionstatic->type_code == 'AC_TEL') {
14963 $iconClass = 'fa fa-phone';
14964 } elseif ($actionstatic->type_code == 'AC_FAX') {
14965 $iconClass = 'fa fa-fax';
14966 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14967 $iconClass = 'fa fa-envelope';
14968 } elseif ($actionstatic->type_code == 'AC_INT') {
14969 $iconClass = 'fa fa-shipping-fast';
14970 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14971 $iconClass = 'fa fa-robot';
14972 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14973 $iconClass = 'fa fa-robot';
14974 }
14975 }
14976 }
14977
14978 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14979 return $out;
14980}
14981
14989{
14990 global $conf, $db;
14991
14992 $documents = array();
14993
14994 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
14995 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14996 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14997 //$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
14998 $sql.= ' OR ecm.agenda_id = '.(int) $object->id;
14999 $sql .= ' ORDER BY ecm.position ASC';
15000
15001 $resql = $db->query($sql);
15002 if ($resql) {
15003 if ($db->num_rows($resql)) {
15004 while ($obj = $db->fetch_object($resql)) {
15005 $documents[$obj->id] = $obj;
15006 }
15007 }
15008 }
15009
15010 return $documents;
15011}
15012
15013
15031function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
15032{
15033 global $user, $conf;
15034 global $form;
15035
15036 global $param, $massactionbutton;
15037
15038 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
15039
15040 // Check parameters
15041 if (!is_object($filterobj) && !is_object($objcon)) {
15042 dol_print_error(null, 'BadParameter');
15043 }
15044
15045 $histo = array();
15046 '@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';
15047
15048 $numaction = 0;
15049 $now = dol_now();
15050
15051 $sortfield_list = explode(',', $sortfield);
15052 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
15053 $sortfield_new_list = array();
15054 foreach ($sortfield_list as $sortfield_value) {
15055 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
15056 }
15057 $sortfield_new = implode(',', $sortfield_new_list);
15058
15059 $sql = null;
15060 $sql2 = null;
15061
15062 if (isModEnabled('agenda')) {
15063 // Search histo on actioncomm
15064 if (is_object($objcon) && $objcon->id > 0) {
15065 $sql = "SELECT DISTINCT a.id, a.label as label,";
15066 } else {
15067 $sql = "SELECT a.id, a.label as label,";
15068 }
15069 $sql .= " a.datep as dp,";
15070 $sql .= " a.note as message,";
15071 $sql .= " a.datep2 as dp2,";
15072 $sql .= " a.percent as percent, 'action' as type,";
15073 $sql .= " a.fk_element, a.elementtype,";
15074 $sql .= " a.fk_contact,";
15075 $sql .= " a.email_from as msg_from,";
15076 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
15077 $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";
15078 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15079 $sql .= ", sp.lastname, sp.firstname";
15080 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15081 $sql .= ", m.lastname, m.firstname";
15082 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
15083 $sql .= ", o.ref";
15084 }
15085 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
15086 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
15087 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
15088
15089 $force_filter_contact = $filterobj instanceof User;
15090
15091 if (is_object($objcon) && $objcon->id > 0) {
15092 $force_filter_contact = true;
15093 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
15094 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
15095 }
15096
15097 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
15098 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
15099 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
15100 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
15101 $sql .= " ON er.resource_type = 'dolresource'";
15102 $sql .= " AND er.element_id = a.id";
15103 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
15104 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15105 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
15106 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15107 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
15108 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15109 $sql .= ", ".MAIN_DB_PREFIX."product as o";
15110 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15111 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
15112 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15113 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
15114 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15115 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
15116 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15117 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
15118 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15119 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
15120 }
15121
15122 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
15123 if (!$force_filter_contact) {
15124 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
15125 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
15126 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
15127 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
15128 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15129 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
15130 if ($filterobj->id) {
15131 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15132 }
15133 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
15134 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
15135 if ($filterobj->id) {
15136 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15137 }
15138 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15139 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
15140 if ($filterobj->id) {
15141 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15142 }
15143 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15144 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
15145 if ($filterobj->id) {
15146 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15147 }
15148 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15149 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
15150 if ($filterobj->id) {
15151 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15152 }
15153 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15154 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
15155 if ($filterobj->id) {
15156 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15157 }
15158 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15159 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
15160 if ($filterobj->id) {
15161 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15162 }
15163 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
15164 $sql .= " AND a.fk_contact = sp.rowid";
15165 if ($filterobj->id) {
15166 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
15167 }
15168 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15169 $sql .= " AND a.fk_element = o.rowid";
15170 if ($filterobj->id) {
15171 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
15172 }
15173 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15174 $sql .= " AND a.fk_element = o.rowid";
15175 if ($filterobj->id) {
15176 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
15177 }
15178 }
15179 } else {
15180 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
15181 }
15182
15183 // Condition on actioncode
15184 if (!empty($actioncode) && $actioncode != '-1') {
15185 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
15186 if ($actioncode == 'AC_NON_AUTO') {
15187 $sql .= " AND c.type != 'systemauto'";
15188 } elseif ($actioncode == 'AC_ALL_AUTO') {
15189 $sql .= " AND c.type = 'systemauto'";
15190 } else {
15191 if ($actioncode == 'AC_OTH') {
15192 $sql .= " AND c.type != 'systemauto'";
15193 } elseif ($actioncode == 'AC_OTH_AUTO') {
15194 $sql .= " AND c.type = 'systemauto'";
15195 }
15196 }
15197 } else {
15198 if ($actioncode == 'AC_NON_AUTO') {
15199 $sql .= " AND c.type != 'systemauto'";
15200 } elseif ($actioncode == 'AC_ALL_AUTO') {
15201 $sql .= " AND c.type = 'systemauto'";
15202 } else {
15203 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
15204 }
15205 }
15206 }
15207 if ($donetodo == 'todo') {
15208 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15209 } elseif ($donetodo == 'done') {
15210 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15211 }
15212 if (is_array($filters) && $filters['search_agenda_label']) {
15213 $sql .= natural_search('a.label', $filters['search_agenda_label']);
15214 }
15215 }
15216
15217 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
15218 if (isModEnabled('mailing') && !empty($objcon->email)
15219 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
15220 $langs->load("mails");
15221
15222 $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";
15223 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
15224 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
15225 $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
15226 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15227 $sql2 .= ", '' as lastname, '' as firstname";
15228 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15229 $sql2 .= ", '' as lastname, '' as firstname";
15230 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15231 $sql2 .= ", '' as ref";
15232 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15233 $sql2 .= ", '' as ref";
15234 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15235 $sql2 .= ", '' as ref";
15236 }
15237 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
15238 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
15239 $sql2 .= " AND mc.statut = 1";
15240 $sql2 .= " AND u.rowid = m.fk_user_valid";
15241 $sql2 .= " AND mc.fk_mailing=m.rowid";
15242 }
15243
15244 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
15245 if (!empty($sql) && !empty($sql2)) {
15246 $sql = $sql." UNION ".$sql2;
15247 } elseif (empty($sql) && !empty($sql2)) {
15248 $sql = $sql2;
15249 }
15250
15251 //TODO Add navigation with this limits...
15252 $offset = 0;
15253 $limit = 1000;
15254
15255 // Complete request and execute it with limit
15256 $sql .= $db->order($sortfield_new, $sortorder);
15257 if ($limit) {
15258 $sql .= $db->plimit($limit + 1, $offset);
15259 }
15260
15261 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
15262 $resql = $db->query($sql);
15263 if ($resql) {
15264 $i = 0;
15265 $num = $db->num_rows($resql);
15266
15267 $imaxinloop = ($limit ? min($num, $limit) : $num);
15268 while ($i < $imaxinloop) {
15269 $obj = $db->fetch_object($resql);
15270
15271 if ($obj->type == 'action') {
15272 $contactaction = new ActionComm($db);
15273 $contactaction->id = $obj->id;
15274 $result = $contactaction->fetchResources();
15275 if ($result < 0) {
15276 dol_print_error($db);
15277 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
15278 }
15279
15280 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15281 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15282 $tododone = '';
15283 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
15284 $tododone = 'todo';
15285 }
15286
15287 $histo[$numaction] = array(
15288 'type' => $obj->type,
15289 'tododone' => $tododone,
15290 'id' => $obj->id,
15291 'datestart' => $db->jdate($obj->dp),
15292 'dateend' => $db->jdate($obj->dp2),
15293 'note' => $obj->label,
15294 'message' => dol_htmlentitiesbr($obj->message),
15295 'percent' => $obj->percent,
15296
15297 'userid' => $obj->user_id,
15298 'login' => $obj->user_login,
15299 'userfirstname' => $obj->user_firstname,
15300 'userlastname' => $obj->user_lastname,
15301 'userphoto' => $obj->user_photo,
15302 'msg_from' => $obj->msg_from,
15303
15304 'contact_id' => $obj->fk_contact,
15305 'socpeopleassigned' => $contactaction->socpeopleassigned,
15306 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
15307 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
15308 'fk_element' => $obj->fk_element,
15309 'elementtype' => $obj->elementtype,
15310 // Type of event
15311 'acode' => $obj->acode,
15312 'alabel' => $obj->alabel,
15313 'libelle' => $obj->alabel, // deprecated
15314 'apicto' => $obj->apicto
15315 );
15316 } else {
15317 $histo[$numaction] = array(
15318 'type' => $obj->type,
15319 'tododone' => 'done',
15320 'id' => $obj->id,
15321 'datestart' => $db->jdate($obj->dp),
15322 'dateend' => $db->jdate($obj->dp2),
15323 'note' => $obj->label,
15324 'message' => dol_htmlentitiesbr($obj->message),
15325 'percent' => $obj->percent,
15326 'acode' => $obj->acode,
15327
15328 'userid' => $obj->user_id,
15329 'login' => $obj->user_login,
15330 'userfirstname' => $obj->user_firstname,
15331 'userlastname' => $obj->user_lastname,
15332 'userphoto' => $obj->user_photo
15333 );
15334 }
15335
15336 $numaction++;
15337 $i++;
15338 }
15339 } else {
15340 dol_print_error($db);
15341 }
15342 }
15343
15344 // Set $out to show events
15345 $out = '';
15346
15347 if (!isModEnabled('agenda')) {
15348 $langs->loadLangs(array("admin", "errors"));
15349 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
15350 }
15351
15352 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
15353 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
15354
15355 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
15356 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
15357 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
15358 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
15359
15360 $formactions = new FormActions($db);
15361
15362 $actionstatic = new ActionComm($db);
15363 $userstatic = new User($db);
15364 $contactstatic = new Contact($db);
15365 $userGetNomUrlCache = array();
15366 $contactGetNomUrlCache = array();
15367
15368 $out .= '<div class="filters-container" >';
15369 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
15370 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
15371
15372 if ($objcon && get_class($objcon) == 'Contact' &&
15373 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
15374 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
15375 } else {
15376 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
15377 }
15378 if (($filterobj && get_class($filterobj) == 'Societe')) {
15379 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
15380 } else {
15381 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
15382 }
15383
15384 $out .= "\n";
15385
15386 $out .= '<div class="div-table-responsive-no-min">';
15387 $out .= '<table class="noborder borderbottom centpercent">';
15388
15389 $out .= '<tr class="liste_titre">';
15390
15391 // Action column
15392 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15393 $out .= '<th class="liste_titre width50 middle">';
15394 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15395 $out .= $searchpicto;
15396 $out .= '</th>';
15397 }
15398
15399 // Date
15400 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
15401
15402 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
15403 if ($donetodo) {
15404 $out .= '<th class="liste_titre"></th>';
15405 }
15406 // Type of event
15407 $out .= '<th class="liste_titre">';
15408 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
15409 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
15410 $out .= '</th>';
15411 // Label
15412 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
15413 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
15414 $out .= '</th>';
15415
15416 // Action column
15417 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15418 $out .= '<th class="liste_titre width50 middle">';
15419 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15420 $out .= $searchpicto;
15421 $out .= '</th>';
15422 }
15423
15424 $out .= '</tr>';
15425
15426 $out .= '</table>';
15427
15428 $out .= '</form>';
15429 $out .= '</div>';
15430
15431 $out .= "\n";
15432
15433 $out .= '<ul class="timeline">';
15434
15435 if ($donetodo) {
15436 $tmp = '';
15437 if ($filterobj instanceof Societe) {
15438 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15439 }
15440 if ($filterobj instanceof User) {
15441 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15442 }
15443 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
15444 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
15445 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
15446 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
15447 if ($filterobj instanceof Societe) {
15448 $tmp .= '</a>';
15449 }
15450 if ($filterobj instanceof User) {
15451 $tmp .= '</a>';
15452 }
15453 $out .= getTitleFieldOfList($tmp);
15454 }
15455
15456 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
15457 $caction = new CActionComm($db);
15458 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
15459
15460 $actualCycleDate = false;
15461
15462 // Loop on each event to show it
15463 foreach ($histo as $key => $value) {
15464 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
15465
15466 $actionstatic->type_picto = $histo[$key]['apicto'];
15467 $actionstatic->type_code = $histo[$key]['acode'];
15468
15469 $labeltype = $actionstatic->type_code;
15470 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
15471 $labeltype = 'AC_OTH';
15472 }
15473 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15474 $labeltype = $langs->trans("Message");
15475 } else {
15476 if (!empty($arraylist[$labeltype])) {
15477 $labeltype = $arraylist[$labeltype];
15478 }
15479 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
15480 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
15481 }
15482 }
15483
15484 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
15485
15486 $tmpa = dol_getdate($histo[$key]['datestart'], false);
15487
15488 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
15489 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
15490 $out .= '<!-- timeline time label -->';
15491 $out .= '<li class="time-label">';
15492 $out .= '<span class="timeline-badge-date">';
15493 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
15494 $out .= '</span>';
15495 $out .= '</li>';
15496 $out .= '<!-- /.timeline-label -->';
15497 }
15498
15499
15500 $out .= '<!-- timeline item -->'."\n";
15501 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
15502
15503 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
15504 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
15505 //$out .= $timelineicon;
15506 //var_dump($timelineicon);
15507 $out .= $typeicon;
15508
15509 $out .= '<div class="timeline-item">'."\n";
15510
15511 $out .= '<span class="time timeline-header-action2">';
15512
15513 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
15514 $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").' ';
15515 $out .= $histo[$key]['id'];
15516 $out .= '</a> ';
15517 } else {
15518 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
15519 }
15520
15521 if ($user->hasRight('agenda', 'allactions', 'create') ||
15522 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
15523 $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).'">';
15524 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
15525 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
15526 $out .= '</a>';
15527 }
15528
15529 $out .= '</span>';
15530
15531 // Date
15532 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
15533 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
15534 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
15535 $tmpa = dol_getdate($histo[$key]['datestart'], true);
15536 $tmpb = dol_getdate($histo[$key]['dateend'], true);
15537 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
15538 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
15539 } else {
15540 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
15541 }
15542 }
15543 $late = 0;
15544 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15545 $late = 1;
15546 }
15547 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15548 $late = 1;
15549 }
15550 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
15551 $late = 1;
15552 }
15553 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15554 $late = 1;
15555 }
15556 if ($late) {
15557 $out .= img_warning($langs->trans("Late")).' ';
15558 }
15559 $out .= "</span></span>\n";
15560
15561 $out .= '<span class="time">';
15562 $out .= $actionstatic->getLibStatut(2);
15563 $out .= '</span>';
15564
15565 // Ref
15566 $out .= '<h3 class="timeline-header">';
15567
15568 // Author of event
15569 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
15570 if ($histo[$key]['userid'] > 0) {
15571 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
15572 $userstatic->fetch($histo[$key]['userid']);
15573 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
15574 }
15575 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
15576 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
15577 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
15578 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
15579 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
15580 } else {
15581 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
15582 }
15583 }
15584 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
15585 }
15586 $out .= '</div>';
15587
15588 // Title
15589 $out .= ' <div class="messaging-title inline-block">';
15590 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
15591 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
15592 $out .= $labeltype.' - ';
15593 }
15594
15595 $libelle = '';
15596
15597 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
15598 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
15599 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15600 $out .= $langs->trans('TicketNewMessage');
15601 } elseif (isset($histo[$key]['type'])) {
15602 if ($histo[$key]['type'] == 'action') {
15603 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15604 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
15605 $libelle = $histo[$key]['note'];
15606 $actionstatic->id = $histo[$key]['id'];
15607 if ($libelle != $labeltype) {
15608 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15609 }
15610 } elseif ($histo[$key]['type'] == 'mailing') {
15611 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
15612 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15613 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
15614 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15615 } else {
15616 $libelle .= $histo[$key]['note'];
15617 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15618 }
15619 }
15620 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
15621
15622 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
15623 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
15624 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
15625 } else {
15626 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
15627 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
15628 }
15629 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
15630 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
15631 }
15632 if ($link) {
15633 $out .= ' - '.$link;
15634 }
15635 }
15636
15637 $out .= '</div>';
15638
15639 $out .= '</h3>';
15640
15641 // Message
15642 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
15643 && $actionstatic->code != 'AC_TICKET_CREATE'
15644 && $actionstatic->code != 'AC_TICKET_MODIFY'
15645 ) {
15646 $out .= '<div class="timeline-body wordbreak small">';
15647 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
15648 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
15649 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
15650 $out .= '<div class="readmore-block --closed" >';
15651 $out .= ' <div class="readmore-block__excerpt">';
15652 $out .= dolPrintHTML($truncatedText);
15653 $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>';
15654 $out .= ' </div>';
15655 $out .= ' <div class="readmore-block__full-text" >';
15656 $out .= dolPrintHTML($histo[$key]['message']);
15657 $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>';
15658 $out .= ' </div>';
15659 $out .= '</div>';
15660 } else {
15661 $out .= dolPrintHTML($histo[$key]['message']);
15662 }
15663
15664 $out .= '</div>';
15665 }
15666
15667 // Timeline footer
15668 $footer = '';
15669
15670 // Contact for this action
15671 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
15672 $contactList = '';
15673 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
15674 if (empty($conf->cache['contact'][$cid])) {
15675 $contact = new Contact($db);
15676 $contact->fetch($cid);
15677 $conf->cache['contact'][$cid] = $contact;
15678 } else {
15679 $contact = $conf->cache['contact'][$cid];
15680 }
15681
15682 if ($contact) {
15683 $contactList .= !empty($contactList) ? ', ' : '';
15684 $contactList .= $contact->getNomUrl(1);
15685 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15686 if (!empty($contact->phone_pro)) {
15687 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
15688 }
15689 }
15690 }
15691 }
15692
15693 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
15694 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
15695 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
15696 $contact = new Contact($db);
15697 $result = $contact->fetch($histo[$key]['contact_id']);
15698 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
15699 } else {
15700 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
15701 $result = ($contact instanceof Contact) ? $contact->id : 0;
15702 }
15703
15704 if ($result > 0) {
15705 $footer .= $contact->getNomUrl(1);
15706 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15707 if (!empty($contact->phone_pro)) {
15708 $footer .= '('.dol_print_phone($contact->phone_pro).')';
15709 }
15710 }
15711 }
15712 }
15713
15714 $documents = getActionCommEcmList($actionstatic);
15715 if (!empty($documents)) {
15716 $footer .= '<div class="timeline-documents-container">';
15717 foreach ($documents as $doc) {
15718 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
15719 $footer .= ' data-id="'.$doc->id.'" ';
15720 $footer .= ' data-path="'.$doc->filepath.'"';
15721 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
15722 $footer .= '>';
15723
15724 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
15725 $mime = dol_mimetype($filePath);
15726 if (empty($doc->agenda_id)) {
15727 $dir_ref = $actionstatic->id;
15728 $modulepart = 'actions';
15729 } else {
15730 $split_dir = explode('/', $doc->filepath);
15731 $modulepart = array_shift($split_dir);
15732 $dir_ref = implode('/', $split_dir);
15733 }
15734
15735 $file = $dir_ref.'/'.$doc->filename;
15736 $thumb = $dir_ref.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
15737 $doclink = dol_buildpath('document.php', 1).'?modulepart='.$modulepart.'&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
15738 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart='.$modulepart.'&file='.urlencode($thumb).'&entity='.$conf->entity;
15739
15740
15741
15742 $mimeAttr = ' mime="'.$mime.'" ';
15743 $class = '';
15744 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
15745 $class .= ' documentpreview';
15746 }
15747
15748 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
15749 $footer .= img_mime($filePath).' '.$doc->filename;
15750 $footer .= '</a>';
15751
15752 $footer .= '</span>';
15753 }
15754 $footer .= '</div>';
15755 }
15756
15757 if (!empty($footer)) {
15758 $out .= '<div class="timeline-footer">'.$footer.'</div>';
15759 }
15760
15761 $out .= '</div>'."\n"; // end timeline-item
15762
15763 $out .= '</li>';
15764 $out .= '<!-- END timeline item -->';
15765 }
15766
15767 $out .= "</ul>\n";
15768
15769 // Code to manage the click on button data-read-more-action to show full description of an event
15770 $out .= '<script>
15771 jQuery(document).ready(function () {
15772 $(document).on("click", "[data-read-more-action]", function(e){
15773 console.log("We click on data-read-more-action");
15774 let readMoreBloc = $(this).closest(".readmore-block");
15775 if(readMoreBloc.length > 0){
15776 e.preventDefault();
15777 if($(this).attr("data-read-more-action") == "close"){
15778 readMoreBloc.addClass("--closed").removeClass("--open");
15779 $("html, body").animate({
15780 scrollTop: readMoreBloc.offset().top - 200
15781 }, 100);
15782 }else{
15783 readMoreBloc.addClass("--open").removeClass("--closed");
15784 }
15785 }
15786 });
15787 });
15788 </script>';
15789
15790
15791 if (empty($histo)) {
15792 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
15793 }
15794 }
15795
15796 if ($noprint) {
15797 return $out;
15798 } else {
15799 print $out;
15800 return null;
15801 }
15802}
15803
15815function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
15816{
15817 if ($timestamp === null) {
15818 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
15819 }
15820 $TParam = array(
15821 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
15822 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
15823 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
15824 );
15825 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
15826 $TParam = array_merge($TParam, array(
15827 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
15828 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
15829 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
15830 ));
15831 }
15832
15833 return '&' . http_build_query($TParam);
15834}
15835
15854function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
15855{
15856 global $conf, $db, $langs, $hookmanager;
15857 global $action, $object;
15858
15859 if (!is_object($langs)) {
15860 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
15861 $langs = new Translate('', $conf);
15862 $langs->setDefaultLang();
15863 }
15864
15865 $langs->load("errors");
15866
15867 if ($printheader) {
15868 if (function_exists("llxHeader")) {
15869 llxHeader('');
15870 } elseif (function_exists("llxHeaderVierge")) {
15871 llxHeaderVierge('');
15872 }
15873 }
15874
15875 print '<div class="error">';
15876 if (empty($message)) {
15877 print $langs->trans("ErrorRecordNotFound");
15878 } else {
15879 print $langs->trans($message);
15880 }
15881 print '</div>';
15882 print '<br>';
15883
15884 if (empty($showonlymessage)) {
15885 if (empty($hookmanager)) {
15886 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
15887 $hookmanager = new HookManager($db);
15888 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
15889 $hookmanager->initHooks(array('main'));
15890 }
15891
15892 $parameters = array('message' => $message, 'params' => $params);
15893 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
15894 print $hookmanager->resPrint;
15895 }
15896
15897 if ($printfooter && function_exists("llxFooter")) {
15898 llxFooter();
15899 if (is_object($db)) {
15900 $db->close();
15901 }
15902 }
15903 exit(0);
15904}
$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