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 return $out;
1178}
1179
1189function GETPOSTINT($paramname, $method = 0)
1190{
1191 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1192}
1193
1202function GETPOSTFLOAT($paramname, $rounding = '')
1203{
1204 // 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.)
1205 return (float) price2num(GETPOST($paramname), $rounding, 2);
1206}
1207
1223function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto', $saverestore = '')
1224{
1225 $m = array();
1226 if ($hourTime === 'getpost' || $hourTime === 'getpostend') {
1227 $hour = (GETPOSTISSET($prefix . 'hour') && GETPOSTINT($prefix . 'hour') >= 0) ? GETPOSTINT($prefix . 'hour') : ($hourTime === 'getpostend' ? 23 : 0);
1228 $minute = (GETPOSTISSET($prefix . 'min') && GETPOSTINT($prefix . 'min') >= 0) ? GETPOSTINT($prefix . 'min') : ($hourTime === 'getpostend' ? 59 : 0);
1229 $second = (GETPOSTISSET($prefix . 'sec') && GETPOSTINT($prefix . 'sec') >= 0) ? GETPOSTINT($prefix . 'sec') : ($hourTime === 'getpostend' ? 59 : 0);
1230 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1231 $hour = intval($m[1]);
1232 $minute = intval($m[2]);
1233 $second = intval($m[3]);
1234 } elseif ($hourTime === 'end') {
1235 $hour = 23;
1236 $minute = 59;
1237 $second = 59;
1238 } else {
1239 $hour = $minute = $second = 0;
1240 }
1241
1242 if (
1243 $saverestore
1244 && !GETPOSTISSET($prefix . 'day')
1245 && !GETPOSTISSET($prefix . 'month')
1246 && !GETPOSTISSET($prefix . 'year')
1247 && isset($_SESSION['DOLDATE_' . $saverestore . '_day'])
1248 && isset($_SESSION['DOLDATE_' . $saverestore . '_month'])
1249 && isset($_SESSION['DOLDATE_' . $saverestore . '_year'])
1250 ) {
1251 $day = $_SESSION['DOLDATE_'.$saverestore.'_day'];
1252 $month = $_SESSION['DOLDATE_'.$saverestore.'_month'];
1253 $year = $_SESSION['DOLDATE_'.$saverestore.'_year'];
1254 } else {
1255 $month = GETPOSTINT($prefix . 'month');
1256 $day = GETPOSTINT($prefix . 'day');
1257 $year = GETPOSTINT($prefix . 'year');
1258 }
1259
1260 // normalize out of range values
1261 $hour = (int) min($hour, 23);
1262 $minute = (int) min($minute, 59);
1263 $second = (int) min($second, 59);
1264
1265 if ($saverestore) {
1266 $_SESSION['DOLDATE_'.$saverestore.'_day'] = $day;
1267 $_SESSION['DOLDATE_'.$saverestore.'_month'] = $month;
1268 $_SESSION['DOLDATE_'.$saverestore.'_year'] = $year;
1269 }
1270
1271 //print "$hour, $minute, $second, $month, $day, $year, $gm<br>";
1272 return dol_mktime($hour, $minute, $second, $month, $day, $year, $gm);
1273}
1274
1275
1286function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1287{
1288 return sanitizeVal($out, $check, $filter, $options);
1289}
1290
1300function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1301{
1302 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1303 // Check is done after replacement
1304 if ($out === null) {
1305 $out = '';
1306 }
1307 switch ($check) {
1308 case 'none':
1309 case 'password':
1310 break;
1311 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1312 if (!is_numeric($out)) {
1313 $out = '';
1314 }
1315 break;
1316 case 'intcomma':
1317 if (is_array($out)) {
1318 $out = implode(',', $out);
1319 }
1320 if (preg_match('/[^0-9,-]+/i', $out)) {
1321 $out = '';
1322 }
1323 break;
1324 case 'san_alpha':
1325 $out = filter_var($out, FILTER_SANITIZE_STRING);
1326 break;
1327 case 'email':
1328 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1329 break;
1330 case 'url':
1331 //$out = filter_var($out, FILTER_SANITIZE_URL); // Not reliable, replaced with FILTER_VALIDATE_URL
1332 $out = preg_replace('/[^:\/\[\]a-z0-9@\$\'\*\~\.\-_,;\?\!=%&+#]+/i', '', $out);
1333 // TODO Allow ( ) but only into password of https://login:password@domain...
1334 break;
1335 case 'aZ':
1336 if (!is_array($out)) {
1337 $out = trim($out);
1338 if (preg_match('/[^a-z]+/i', $out)) {
1339 $out = '';
1340 }
1341 }
1342 break;
1343 case 'aZ09':
1344 if (!is_array($out)) {
1345 $out = trim($out);
1346 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1347 $out = '';
1348 }
1349 }
1350 break;
1351 case 'aZ09arobase': // great to sanitize $objecttype parameter
1352 if (!is_array($out)) {
1353 $out = trim($out);
1354 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1355 $out = '';
1356 }
1357 }
1358 break;
1359 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1360 if (!is_array($out)) {
1361 $out = trim($out);
1362 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1363 $out = '';
1364 }
1365 }
1366 break;
1367 case 'alpha': // No html and no ../ and "
1368 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1369 if (!is_array($out)) {
1370 $out = trim($out);
1371 do {
1372 $oldstringtoclean = $out;
1373 // Remove html tags
1374 $out = dol_string_nohtmltag($out, 0);
1375 // 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).
1376 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1377 // Remove also other dangerous string sequences
1378 // '../' or '..\' is dangerous because it allows dir transversals
1379 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1380 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1381 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1382 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1383 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1384 } while ($oldstringtoclean != $out);
1385 // keep lines feed
1386 }
1387 break;
1388 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'
1389 if (!is_array($out)) {
1390 $out = trim($out);
1391 do {
1392 $oldstringtoclean = $out;
1393 // Decode html entities
1394 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1395 // 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).
1396 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1397 // Remove also other dangerous string sequences
1398 // '../' or '..\' is dangerous because it allows dir transversals
1399 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1400 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1401 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1402 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1403 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1404 } while ($oldstringtoclean != $out);
1405 }
1406 break;
1407 case 'nohtml': // No html. Valid for JSON strings.
1408 $out = dol_string_nohtmltag($out, 0);
1409 break;
1410 case 'restricthtmlnolink':
1411 case 'restricthtml': // Recommended for most html textarea
1412 case 'restricthtmlallowclass':
1413 case 'restricthtmlallowiframe':
1414 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1415 case 'restricthtmlallowunvalid':
1416 $out = dol_htmlwithnojs($out, 1, $check);
1417 break;
1418 case 'custom':
1419 if (!empty($out)) {
1420 if (empty($filter)) {
1421 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1422 }
1423 if (is_null($options)) {
1424 $options = 0;
1425 }
1426 $out = filter_var($out, $filter, $options);
1427 }
1428 break;
1429 default:
1430 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1431 $out = GETPOST($out, 'alphanohtml');
1432 break;
1433 }
1434
1435 return $out;
1436}
1437
1446function dolSetCookie(string $cookiename, string $cookievalue, int $expire = -1)
1447{
1448 global $dolibarr_main_force_https;
1449
1450 if ($expire == -1) {
1451 $expire = (time() + (86400 * 354)); // keep cookie 1 year.
1452 }
1453
1454 if (PHP_VERSION_ID < 70300) {
1455 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : $expire, '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // add tag httponly
1456 } else {
1457 // Only available for php >= 7.3
1458 $cookieparams = array(
1459 'expires' => empty($cookievalue) ? 0 : $expire,
1460 'path' => '/',
1461 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
1462 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
1463 'httponly' => true,
1464 'samesite' => 'Lax' // None || Lax || Strict
1465 );
1466 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
1467 }
1468 if (empty($cookievalue)) {
1469 unset($_COOKIE[$cookiename]);
1470 }
1471}
1472
1473if (!function_exists('dol_getprefix')) {
1484 function dol_getprefix($mode = '')
1485 {
1486 // If prefix is for email (we need to have $conf already loaded for this case)
1487 if ($mode == 'email') {
1488 global $conf;
1489
1490 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1491 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1492 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1493 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1494 return $_SERVER["SERVER_NAME"];
1495 }
1496 }
1497
1498 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1499 if (!empty($conf->file->instance_unique_id)) {
1500 return sha1('dolibarr'.$conf->file->instance_unique_id);
1501 }
1502
1503 // For backward compatibility when instance_unique_id is not set
1504 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1505 }
1506
1507 // If prefix is for session (no need to have $conf loaded)
1508 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1509 $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
1510
1511 // The recommended value (may be not defined for old versions)
1512 if (!empty($tmp_instance_unique_id)) {
1513 return sha1('dolibarr'.$tmp_instance_unique_id);
1514 }
1515
1516 // For backward compatibility when instance_unique_id is not set
1517 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1518 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1519 } else {
1520 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1521 }
1522 }
1523}
1524
1535function dol_include_once($relpath, $classname = '')
1536{
1537 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']
1538
1539 $fullpath = dol_buildpath($relpath);
1540
1541 if (!file_exists($fullpath)) {
1542 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1543 return false;
1544 }
1545 if (!empty($classname) && !class_exists($classname)) {
1546 return include $fullpath;
1547 } else {
1548 return include_once $fullpath;
1549 }
1550}
1551
1552
1566function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1567{
1568 global $conf;
1569
1570 $path = preg_replace('/^\//', '', $path);
1571
1572 if (empty($type)) { // For a filesystem path
1573 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1574 if (is_array($conf->file->dol_document_root)) {
1575 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1576 if ($key == 'main') {
1577 continue;
1578 }
1579 // if (@file_exists($dirroot.'/'.$path)) {
1580 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1581 $res = $dirroot.'/'.$path;
1582 return $res;
1583 }
1584 }
1585 }
1586 if ($returnemptyifnotfound) {
1587 // Not found into alternate dir
1588 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1589 return '';
1590 }
1591 }
1592 } else {
1593 // For an url path
1594 // We try to get local path of file on filesystem from url
1595 // Note that trying to know if a file on disk exist by forging path on disk from url
1596 // works only for some web server and some setup. This is bugged when
1597 // using proxy, rewriting, virtual path, etc...
1598 $res = '';
1599 if ($type == 1) {
1600 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1601 }
1602 if ($type == 2) {
1603 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1604 }
1605 if ($type == 3) {
1606 $res = DOL_URL_ROOT.'/'.$path;
1607 }
1608
1609 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1610 if ($key == 'main') {
1611 if ($type == 3) {
1612 /*global $dolibarr_main_url_root;*/
1613
1614 // Define $urlwithroot
1615 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1616 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1617 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1618
1619 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1620 }
1621 continue;
1622 }
1623 $regs = array();
1624 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1625 if (!empty($regs[1])) {
1626 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1627 //if (file_exists($dirroot.'/'.$regs[1])) {
1628 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1629 if ($type == 1) {
1630 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1631 } elseif ($type == 2) {
1632 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1633 } elseif ($type == 3) {
1634 /*global $dolibarr_main_url_root;*/
1635
1636 // Define $urlwithroot
1637 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1638 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1639 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1640
1641 $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
1642 }
1643 break;
1644 }
1645 }
1646 }
1647 }
1648
1649 return $res;
1650}
1651
1662function dol_get_object_properties($obj, $properties = [])
1663{
1664 // Get real properties using get_object_vars() if $properties is empty
1665 if (empty($properties)) {
1666 return get_object_vars($obj);
1667 }
1668
1669 $existingProperties = [];
1670 $realProperties = get_object_vars($obj);
1671
1672 // Get the real or magic property values
1673 foreach ($properties as $property) {
1674 if (array_key_exists($property, $realProperties)) {
1675 // Real property, add the value
1676 $existingProperties[$property] = $obj->{$property};
1677 } elseif (property_exists($obj, $property)) {
1678 // Magic property
1679 $existingProperties[$property] = $obj->{$property};
1680 }
1681 }
1682
1683 return $existingProperties;
1684}
1685
1686
1702function dol_clone($object, $native = 2)
1703{
1704 if ($native == 0) {
1705 // deprecated method, use the method with native = 2 instead
1706 $tmpsavdb = null;
1707 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1708 $tmpsavdb = $object->db;
1709 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1710 }
1711
1712 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1713
1714 if (!empty($tmpsavdb)) {
1715 $object->db = $tmpsavdb;
1716 }
1717 } elseif ($native == 2) {
1718 // recommended method to have a full isolated cloned object
1719 $myclone = new stdClass();
1720 $tmparray = get_object_vars($object); // return only public properties
1721
1722 if (is_array($tmparray)) {
1723 foreach ($tmparray as $propertykey => $propertyval) {
1724 if (is_scalar($propertyval) || is_array($propertyval)) {
1725 $myclone->$propertykey = $propertyval;
1726 }
1727 }
1728 }
1729 } else {
1730 $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)
1731 }
1732
1733 return $myclone;
1734}
1735
1745function dol_size($size, $type = '')
1746{
1747 global $conf;
1748 if (empty($conf->dol_optimize_smallscreen)) {
1749 return $size;
1750 }
1751 if ($type == 'width' && $size > 250) {
1752 return 250;
1753 } else {
1754 return 10;
1755 }
1756}
1757
1758
1771function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0)
1772{
1773 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1774 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1775 // Char '/' and '\' are file delimiters.
1776 // 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
1777 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1778 if ($includequotes) {
1779 $filesystem_forbidden_chars[] = "'";
1780 }
1781 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1782 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1783 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1784 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1785 $tmp = str_replace('..', '', $tmp);
1786 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1787
1788 return $tmp;
1789}
1790
1791
1803function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1804{
1805 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1806 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1807 // 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
1808 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1809
1810 $tmp = $str;
1811 if ($unaccent) {
1812 $tmp = dol_string_unaccent($tmp);
1813 }
1814 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1815 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1816 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1817 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1818 $tmp = str_replace('..', '', $tmp);
1819 $tmp = preg_replace('/\s{2,}/', ' ', $tmp);
1820
1821 return $tmp;
1822}
1823
1831function dol_sanitizeUrl($stringtoclean, $type = 1)
1832{
1833 // 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)
1834 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1835 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1836 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1837 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1838
1839 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1840 if ($type == 1) {
1841 // removing : should disable links to external url like http:aaa)
1842 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1843 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1844 }
1845
1846 do {
1847 $oldstringtoclean = $stringtoclean;
1848 // removing '&colon' should disable links to external url like http:aaa)
1849 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1850 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1851 } while ($oldstringtoclean != $stringtoclean);
1852
1853 if ($type == 1) {
1854 // removing '//' should disable links to external url like //aaa or http//)
1855 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1856 }
1857
1858 return $stringtoclean;
1859}
1860
1867function dol_sanitizeEmail($stringtoclean)
1868{
1869 do {
1870 $oldstringtoclean = $stringtoclean;
1871 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1872 } while ($oldstringtoclean != $stringtoclean);
1873
1874 return $stringtoclean;
1875}
1876
1885function dol_sanitizeKeyCode($str)
1886{
1887 return preg_replace('/[^\w]+/', '', $str);
1888}
1889
1890
1899function dol_string_unaccent($str)
1900{
1901 if (is_null($str)) {
1902 return '';
1903 }
1904
1905 if (utf8_check($str)) {
1906 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1907 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1908 return $transliterator->transliterate($str);
1909 }
1910 // See http://www.utf8-chartable.de/
1911 $string = rawurlencode($str);
1912 $replacements = array(
1913 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1914 '%C3%87' => 'C',
1915 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1916 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1917 '%C3%91' => 'N',
1918 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1919 '%C5%A0' => 'S',
1920 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1921 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1922 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1923 '%C3%A7' => 'c',
1924 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1925 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1926 '%C3%B1' => 'n',
1927 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1928 '%C5%A1' => 's',
1929 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1930 '%C3%BD' => 'y', '%C3%BF' => 'y'
1931 );
1932 $string = strtr($string, $replacements);
1933 return rawurldecode($string);
1934 } else {
1935 // See http://www.ascii-code.com/
1936 $string = strtr(
1937 $str,
1938 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1939 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1940 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1941 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1942 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1943 \xF9\xFA\xFB\xFC\xFD\xFF",
1944 "AAAAAAC
1945 EEEEIIIIDN
1946 OOOOOUUUY
1947 aaaaaaceeee
1948 iiiidnooooo
1949 uuuuyy"
1950 );
1951 $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"));
1952 return $string;
1953 }
1954}
1955
1969function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1970{
1971 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1972 if (empty($keepspaces)) {
1973 $forbidden_chars_to_replace[] = " ";
1974 }
1975 $forbidden_chars_to_remove = array();
1976 //$forbidden_chars_to_remove=array("(",")");
1977
1978 if (is_array($badcharstoreplace)) {
1979 $forbidden_chars_to_replace = $badcharstoreplace;
1980 }
1981 if (is_array($badcharstoremove)) {
1982 $forbidden_chars_to_remove = $badcharstoremove;
1983 }
1984
1985 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1986 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1987}
1988
1989
2003function dol_string_nounprintableascii($str, $removetabcrlf = 1)
2004{
2005 if ($removetabcrlf) {
2006 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
2007 } else {
2008 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
2009 }
2010}
2011
2018function dolSlugify($stringtoslugify)
2019{
2020 $slug = dol_string_unaccent($stringtoslugify);
2021
2022 // Convert special characters to their ASCII equivalents
2023 if (function_exists('iconv')) {
2024 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
2025 }
2026
2027 // Convert to lowercase
2028 $slug = strtolower($slug);
2029
2030 // Replace non-alphanumeric characters with hyphens
2031 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
2032
2033 // Remove leading and trailing hyphens
2034 $slug = trim($slug, '-');
2035
2036 return $slug;
2037}
2038
2047function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
2048{
2049 if (is_null($stringtoescape)) {
2050 return '';
2051 }
2052
2053 // escape quotes and backslashes, newlines, etc.
2054 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
2055 //$substitjs['</']='<\/'; // We removed this. Should be useless.
2056 if (empty($noescapebackslashn)) {
2057 $substitjs["\n"] = '\\n';
2058 $substitjs['\\'] = '\\\\';
2059 }
2060 if (empty($mode)) {
2061 $substitjs["'"] = "\\'";
2062 $substitjs['"'] = "\\'";
2063 } elseif ($mode == 1) {
2064 $substitjs["'"] = "\\'";
2065 } elseif ($mode == 2) {
2066 $substitjs['"'] = '\\"';
2067 } elseif ($mode == 3) {
2068 $substitjs["'"] = "\\'";
2069 $substitjs['"'] = "\\\"";
2070 }
2071 return strtr((string) $stringtoescape, $substitjs);
2072}
2073
2083function dol_escape_uri($stringtoescape)
2084{
2085 return rawurlencode($stringtoescape);
2086}
2087
2094function dol_escape_json($stringtoescape)
2095{
2096 return str_replace('"', '\"', $stringtoescape);
2097}
2098
2106function dol_escape_php($stringtoescape, $stringforquotes = 2)
2107{
2108 if (is_null($stringtoescape)) {
2109 return '';
2110 }
2111
2112 if ($stringforquotes == 2) {
2113 return str_replace('"', "'", $stringtoescape);
2114 } elseif ($stringforquotes == 1) {
2115 // We remove the \ char.
2116 // If we allow the \ char, we can have $stringtoescape =
2117 // abc\';phpcodedanger; so the escapement will become
2118 // abc\\';phpcodedanger; and injecting this into
2119 // $a='...' will give $ac='abc\\';phpcodedanger;
2120 $stringtoescape = str_replace('\\', '', $stringtoescape);
2121 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2122 }
2123
2124 return 'Bad parameter for stringforquotes in dol_escape_php';
2125}
2126
2133function dol_escape_all($stringtoescape)
2134{
2135 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2136}
2137
2144function dol_escape_xml($stringtoescape)
2145{
2146 return $stringtoescape;
2147}
2148
2158function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2159{
2160 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2161}
2162
2171function dolPrintText($s)
2172{
2173 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2174}
2175
2186function dolPrintHTML($s, $allowiframe = 0)
2187{
2188 // If text is already HTML, we want to escape only dangerous chars else we want to escape all content.
2189 //$isAlreadyHTML = dol_textishtml($s);
2190
2191 // dol_htmlentitiesbr encode all chars except "'" if string is not already HTML, but
2192 // encode only special char like é but not &, <, >, ", ' if already HTML.
2193 $stringWithEntitesForSpecialChar = dol_htmlentitiesbr((string) $s);
2194
2195 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags($stringWithEntitesForSpecialChar, 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2196}
2197
2208function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2209{
2210 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2211 if (!empty($allowothertags) && is_array($allowothertags)) {
2212 $allowedtags = array_merge($allowedtags, $allowothertags);
2213 }
2214 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2215 // The dol_escape_htmltag will escape html tags.
2216 if ($escapeonlyhtmltags) {
2217 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2218 } else {
2219 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2220 }
2221}
2222
2231function dolPrintHTMLForAttributeUrl($s)
2232{
2233 // 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)
2234 // The dol_escape_htmltag will escape html chars.
2235 $escapeonlyhtmltags = 1;
2236 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2237}
2238
2248function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2249{
2250 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2251}
2252
2259function dolPrintPassword($s)
2260{
2261 return htmlspecialchars($s, ENT_HTML5, 'UTF-8');
2262}
2263
2264
2281function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2282{
2283 if ($noescapetags == 'common') {
2284 $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';
2285 // Add also html5 tags
2286 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2287 }
2288 if ($cleanalsojavascript) {
2289 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2290 }
2291
2292 // escape quotes and backslashes, newlines, etc.
2293 if ($escapeonlyhtmltags) {
2294 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2295 } else {
2296 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2297 // Because content can be or not already HTML.
2298 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2299 // In a future, we should not need this
2300
2301 $tmp = (string) $stringtoescape;
2302
2303 // We protect the 6 special entities that we don't want to decode.
2304 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2305 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2306 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2307 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2308 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2309 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2310
2311 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2312
2313 // We restore the 6 special entities that we don't want to have been decoded by previous command
2314 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2315 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2316 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2317 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2318 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2319 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2320
2321 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2322 }
2323 if (!$keepb) {
2324 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2325 }
2326 if (!$keepn) {
2327 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2328 } elseif ($keepn == -1) {
2329 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2330 }
2331
2332 if ($escapeonlyhtmltags) {
2333 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2334 return $tmp;
2335 } else {
2336 // Now we protect all the tags we want to keep
2337 $tmparrayoftags = array();
2338 if ($noescapetags) {
2339 $tmparrayoftags = explode(',', $noescapetags);
2340 }
2341
2342 if (count($tmparrayoftags)) {
2343 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2344
2345 $reg = array();
2346 // Remove reserved keywords. They are forbidden in a source string
2347 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2348
2349 foreach ($tmparrayoftags as $tagtoreplace) {
2350 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2351 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2352 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2353 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2354
2355 // For case of tag with attributes
2356 do {
2357 $tmpold = $tmp;
2358
2359 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2360 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2361 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2362 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2363 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2364 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2365 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2366 }
2367
2368 $diff = strcmp($tmpold, $tmp);
2369 } while ($diff);
2370 }
2371
2372 $tmp = str_ireplace('&amp', '__ANDNOSEMICOLON__', $tmp);
2373 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2374 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2375 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2376 }
2377
2378 // Warning: htmlentities encode all special chars that remains (except "'" with ENT_COMPAT).
2379 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2380
2381 //print $result;
2382
2383 if (count($tmparrayoftags)) {
2384 // Restore protected tags
2385 foreach ($tmparrayoftags as $tagtoreplace) {
2386 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2387 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2388 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2389 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2390 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2391 }
2392
2393 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2394
2395 $result = str_ireplace('__ANDNOSEMICOLON__', '&amp', $result);
2396 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2397 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2398 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2399 }
2400
2401 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2402
2403 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2404
2405 return $result;
2406 }
2407}
2408
2416function dol_strtolower($string, $encoding = "UTF-8")
2417{
2418 if (function_exists('mb_strtolower')) {
2419 return mb_strtolower($string, $encoding);
2420 } else {
2421 return strtolower($string);
2422 }
2423}
2424
2433function dol_strtoupper($string, $encoding = "UTF-8")
2434{
2435 if (function_exists('mb_strtoupper')) {
2436 return mb_strtoupper($string, $encoding);
2437 } else {
2438 return strtoupper($string);
2439 }
2440}
2441
2450function dol_ucfirst($string, $encoding = "UTF-8")
2451{
2452 if (function_exists('mb_substr')) {
2453 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2454 } else {
2455 return ucfirst($string);
2456 }
2457}
2458
2467function dol_ucwords($string, $encoding = "UTF-8")
2468{
2469 if (function_exists('mb_convert_case')) {
2470 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2471 } else {
2472 return ucwords($string);
2473 }
2474}
2475
2476
2482function getCallerInfoString()
2483{
2484 $backtrace = debug_backtrace();
2485 $msg = "";
2486 if (count($backtrace) >= 1) {
2487 $pos = 1;
2488 if (count($backtrace) == 1) {
2489 $pos = 0;
2490 }
2491 $trace = $backtrace[$pos];
2492 if (isset($trace['file'], $trace['line'])) {
2493 $msg = " From {$trace['file']}:{$trace['line']}.";
2494 }
2495 }
2496 return $msg;
2497}
2498
2521function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2522{
2523 global $conf, $user, $debugbar;
2524
2525 // If syslog module enabled
2526 if (!isModEnabled('syslog')) {
2527 return;
2528 }
2529
2530 // Check if we are into execution of code of a website
2531 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2532 global $website, $websitekey;
2533 if (is_object($website) && !empty($website->ref)) {
2534 $suffixinfilename .= '_website_'.$website->ref;
2535 } elseif (!empty($websitekey)) {
2536 $suffixinfilename .= '_website_'.$websitekey;
2537 }
2538 }
2539
2540 // Check if we have a forced suffix
2541 if (defined('USESUFFIXINLOG')) {
2542 $suffixinfilename .= constant('USESUFFIXINLOG');
2543 }
2544
2545 if ($ident < 0) {
2546 foreach ($conf->loghandlers as $loghandlerinstance) {
2547 $loghandlerinstance->setIdent($ident);
2548 }
2549 }
2550
2551 if (!empty($message)) {
2552 // Test log level
2553 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2554 $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');
2555
2556 if (!array_key_exists($level, $logLevels)) {
2557 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2558 $level = LOG_ERR;
2559 }
2560 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2561 return;
2562 }
2563
2564 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2565 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2566 }
2567
2568 // If adding log inside HTML page is required
2569 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2570 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2571 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2572 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2573
2574 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2575 }
2576
2577 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2578 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2579 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2580 print "\n\n<!-- Log start\n";
2581 print dol_escape_htmltag($message)."\n";
2582 print "Log end -->\n";
2583 }
2584
2585 $data = array(
2586 'message' => $message,
2587 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : ''),
2588 'level' => $level,
2589 'user' => ((is_object($user) && $user->id) ? $user->login : ''),
2590 'ip' => '',
2591 'osuser' => function_exists('posix_getuid') ? (string) posix_getuid() : '',
2592 'ospid' => (string) getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2593 );
2594
2595 // For log, we want the reliable IP first.
2596 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2597 if (!empty($remoteip)) {
2598 $data['ip'] = $remoteip;
2599 // This is when server run behind a reverse proxy
2600 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2601 // $data['ip'] is last
2602 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2603 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2604 $data['ip'] = '';
2605 $foundremoteip = 0;
2606 $j = 0;
2607 foreach ($tmpips as $tmpip) {
2608 $tmpip = trim($tmpip);
2609 if (strtolower($tmpip) == strtolower($remoteip)) {
2610 $foundremoteip = 1;
2611 }
2612 if (empty($data['ip'])) {
2613 $data['ip'] = $tmpip;
2614 } else {
2615 $j++;
2616 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2617 }
2618 }
2619 if (!$foundremoteip) {
2620 $j++;
2621 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2622 }
2623 $data['ip'] .= (($j > 0) ? ']' : '');
2624 } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
2625 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2626 $data['ip'] = '';
2627 $foundremoteip = 0;
2628 $j = 0;
2629 foreach ($tmpips as $tmpip) {
2630 $tmpip = trim($tmpip);
2631 if (strtolower($tmpip) == strtolower($remoteip)) {
2632 $foundremoteip = 1;
2633 }
2634 if (empty($data['ip'])) {
2635 $data['ip'] = $tmpip;
2636 } else {
2637 $j++;
2638 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2639 }
2640 }
2641 if (!$foundremoteip) {
2642 $j++;
2643 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2644 }
2645 $data['ip'] .= (($j > 0) ? ']' : '');
2646 }
2647 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2648 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2649 $data['ip'] = (string) $_SERVER['SERVER_ADDR'];
2650 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2651 // 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).
2652 $data['ip'] = (string) $_SERVER['COMPUTERNAME'];
2653 } else {
2654 $data['ip'] = '???';
2655 }
2656
2657 if (!empty($_SERVER['USERNAME'])) {
2658 // 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).
2659 $data['osuser'] = (string) $_SERVER['USERNAME'];
2660 } elseif (!empty($_SERVER['LOGNAME'])) {
2661 // 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).
2662 $data['osuser'] = (string) $_SERVER['LOGNAME'];
2663 }
2664
2665 // Loop on each log handler and send output
2666 foreach ($conf->loghandlers as $loghandlerinstance) {
2667 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2668 continue;
2669 }
2670 $loghandlerinstance->export($data, $suffixinfilename);
2671 }
2672 unset($data);
2673 }
2674
2675 if ($ident > 0) {
2676 foreach ($conf->loghandlers as $loghandlerinstance) {
2677 $loghandlerinstance->setIdent($ident);
2678 }
2679 }
2680}
2681
2693function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2694{
2695 global $langs, $db;
2696
2697 $form = new Form($db);
2698
2699 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2700 if (empty($templatenameforexport)) {
2701 $templatenameforexport = 'website_'.$website->ref;
2702 }
2703
2704 $out = '';
2705 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2706
2707 // for generate popup
2708 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2709 $out .= 'jQuery(document).ready(function () {';
2710 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2711 $out .= ' var dialogHtml = \'';
2712
2713 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2714 $dialogcontent .= ' <div style="margin-top: 20px;">';
2715 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2716 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2717 $dialogcontent .= ' </div>';
2718 $dialogcontent .= ' <br>';
2719 $dialogcontent .= ' <div style="margin-top: 20px;">';
2720 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2721 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2722 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2723 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2724 $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>';
2725 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2726 $dialogcontent .= ' </form>';
2727 $dialogcontent .= ' </div>';
2728 $dialogcontent .= ' </div>';
2729
2730 $out .= dol_escape_js($dialogcontent);
2731
2732 $out .= '\';';
2733
2734
2735 // Add the content of the dialog to the body of the page
2736 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2737 $out .= ' if ($dialog.length > 0) {
2738 $dialog.remove();
2739 }
2740 jQuery("body").append(dialogHtml);';
2741
2742 // Configuration of popup
2743 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2744 $out .= ' autoOpen: false,';
2745 $out .= ' modal: true,';
2746 $out .= ' height: 290,';
2747 $out .= ' width: "40%",';
2748 $out .= ' title: "' . dol_escape_js($label) . '",';
2749 $out .= ' });';
2750
2751 // Simulate a click on the original "submit" input to export the site.
2752 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2753 $out .= ' console.log("Clic on exportsite.");';
2754 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2755 $out .= ' console.log("element founded:", target.length > 0);';
2756 $out .= ' if (target.length > 0) { target.click(); }';
2757 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2758 $out .= ' });';
2759
2760 // open popup
2761 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2762 $out .= ' return false;';
2763 $out .= ' });';
2764 $out .= '});';
2765 $out .= '</script>';
2766
2767 return $out;
2768}
2769
2770
2787function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $jsonclose = '', $accesskey = '')
2788{
2789 global $conf;
2790
2791 if (strpos($url, '?') > 0) {
2792 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2793 } else {
2794 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2795 }
2796
2797 $out = '';
2798
2799 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2800 $out .= '<!-- a link for button to open url into a dialog popup -->';
2801 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2802 if (empty($conf->use_javascript_ajax)) {
2803 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2804 } elseif ($jsonopen) {
2805 $out .= ' href="#" onclick="'.$jsonopen.'"';
2806 } else {
2807 $out .= ' href="#"';
2808 }
2809 $out .= '>'.$buttonstring.'</a>';
2810
2811 if (!empty($conf->use_javascript_ajax)) {
2812 // Add code to open url using the popup.
2813 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2814 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2815
2816 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2817 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2818 jQuery(document).ready(function () {
2819 jQuery(".button_'.$name.'").click(function () {
2820 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2821 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2822 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2823 $tmpdialog.dialog({
2824 autoOpen: false,
2825 modal: true,
2826 height: (window.innerHeight - 150),
2827 width: \'80%\',
2828 title: \''.dol_escape_js($label).'\',
2829 open: function (event, ui) {
2830 console.log("open popup name='.$name.'");
2831 },
2832 close: function (event, ui) {
2833 console.log("Popup is closed, run jsonclose = '.$jsonclose.'");
2834 '.(empty($jsonclose) ? '' : $jsonclose.';').'
2835 }
2836 });
2837
2838 $tmpdialog.dialog(\'open\');
2839 return false;
2840 });
2841 });
2842 </script>';
2843 }
2844 return $out;
2845}
2846
2863function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2864{
2865 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2866}
2867
2885function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0, $morecssdiv = '')
2886{
2887 global $conf, $langs, $hookmanager;
2888
2889 // Show title
2890 $showtitle = 1;
2891 if (!empty($conf->dol_optimize_smallscreen)) {
2892 $showtitle = 0;
2893 }
2894
2895 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2896
2897 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2898 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2899 }
2900
2901 // Show right part
2902 if ($morehtmlright) {
2903 $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.
2904 }
2905
2906 // Show tabs
2907
2908 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2909 $maxkey = -1;
2910 if (is_array($links) && !empty($links)) {
2911 $keys = array_keys($links);
2912 if (count($keys)) {
2913 $maxkey = max($keys);
2914 }
2915 }
2916
2917 // Show tabs
2918 // if =0 we don't use the feature
2919 if (empty($limittoshow)) {
2920 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2921 }
2922 if (!empty($conf->dol_optimize_smallscreen)) {
2923 $limittoshow = 2;
2924 }
2925
2926 $displaytab = 0;
2927 $nbintab = 0;
2928 $popuptab = 0;
2929 $outmore = '';
2930 for ($i = 0; $i <= $maxkey; $i++) {
2931 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2932 // If active tab is already present
2933 if ($i >= $limittoshow) {
2934 $limittoshow--;
2935 }
2936 }
2937 }
2938
2939 for ($i = 0; $i <= $maxkey; $i++) {
2940 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2941 $isactive = true;
2942 } else {
2943 $isactive = false;
2944 }
2945
2946 if ($i < $limittoshow || $isactive) {
2947 // Output entry with a visible tab
2948 $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])).' -->';
2949
2950 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2951 if (!empty($links[$i][0])) {
2952 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2953 } else {
2954 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2955 }
2956 } elseif (!empty($links[$i][1])) {
2957 //print "x $i $active ".$links[$i][2]." z";
2958 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2959
2960 if (!empty($links[$i][0])) {
2961 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2962 $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).'">';
2963 }
2964
2965 if ($displaytab == 0 && $picto) {
2966 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2967 }
2968
2969 $out .= $links[$i][1];
2970 if (!empty($links[$i][0])) {
2971 $out .= '</a>'."\n";
2972 }
2973 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2974 $out .= '</div>';
2975 }
2976
2977 $out .= '</div>';
2978 } else {
2979 // Add entry into the combo popup with the other tabs
2980 if (!$popuptab) {
2981 $popuptab = 1;
2982 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2983 }
2984 $outmore_content = '';
2985
2986 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2987 if (!empty($links[$i][0])) {
2988 $outmore_content .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2989 } else {
2990 $outmore_content .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2991 }
2992 } elseif (!empty($links[$i][1])) {
2993 $outmore_content .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2994 $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.
2995 $outmore_content .= '</a>'."\n";
2996 }
2997 if ($outmore_content !== '') {
2998 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">' . $outmore_content . '</div>';
2999 }
3000
3001 $nbintab++;
3002 }
3003
3004 $displaytab = $i + 1;
3005 }
3006 if ($popuptab) {
3007 $outmore .= '</div>';
3008 }
3009
3010 if ($popuptab) { // If there is some tabs not shown
3011 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
3012 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
3013 $widthofpopup = 240;
3014
3015 $tabsname = $moretabssuffix;
3016 if (empty($tabsname)) {
3017 $tabsname = str_replace("@", "", $picto);
3018 }
3019 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
3020 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
3021 $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".
3022 }
3023 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
3024 $out .= $outmore;
3025 $out .= '</div>';
3026 $out .= '<div></div>';
3027 $out .= "</div>\n";
3028
3029 $out .= '<script nonce="'.getNonce().'">';
3030 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
3031 var x = this.offsetLeft, y = this.offsetTop;
3032 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
3033 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
3034 $('#moretabsList".$tabsname."').css('".$right."','8px');
3035 }
3036 $('#moretabsList".$tabsname."').css('".$left."','auto');
3037 });
3038 ";
3039 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
3040 $out .= "</script>";
3041 }
3042
3043 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
3044 $out .= "</div>\n";
3045 }
3046
3047 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
3048 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
3049 $out .= ($morecssdiv ? ' '.$morecssdiv : '');
3050 $out .= '">'."\n";
3051 }
3052 if (!empty($dragdropfile)) {
3053 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3054 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
3055 }
3056 $parameters = array('tabname' => $active, 'out' => $out);
3057 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
3058 if ($reshook > 0) {
3059 $out = $hookmanager->resPrint;
3060 }
3061
3062 return $out;
3063}
3064
3072function dol_fiche_end($notab = 0)
3073{
3074 print dol_get_fiche_end($notab);
3075}
3076
3083function dol_get_fiche_end($notab = 0)
3084{
3085 if (!$notab || $notab == -1) {
3086 return "\n</div>\n";
3087 } else {
3088 return '';
3089 }
3090}
3091
3111function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
3112{
3113 global $conf, $form, $user, $langs, $hookmanager, $action;
3114
3115 $error = 0;
3116
3117 $maxvisiblephotos = 1;
3118 $showimage = 1;
3119 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3120 // @phan-suppress-next-line PhanUndeclaredMethod
3121 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3122 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3123 $showbarcode = 0;
3124 }
3125 $modulepart = 'unknown';
3126
3127 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3128 $modulepart = $object->element;
3129 } elseif ($object->element == 'member') {
3130 $modulepart = 'memberphoto';
3131 } elseif ($object->element == 'user') {
3132 $modulepart = 'userphoto';
3133 }
3134
3135 if (class_exists("Imagick")) {
3136 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3137 $modulepart = $object->element;
3138 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3139 $modulepart = 'ficheinter';
3140 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3141 $modulepart = 'contract';
3142 } elseif ($object->element == 'order_supplier') {
3143 $modulepart = 'supplier_order';
3144 } elseif ($object->element == 'invoice_supplier') {
3145 $modulepart = 'supplier_invoice';
3146 }
3147 }
3148
3149 if ($object->element == 'product') {
3151 '@phan-var-force Product $object';
3152 $width = 80;
3153 $cssclass = 'photowithmargin photoref';
3154 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3155 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3156 if ($conf->browser->layout == 'phone') {
3157 $maxvisiblephotos = 1;
3158 }
3159 if ($showimage) {
3160 $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>';
3161 } else {
3162 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3163 $nophoto = '';
3164 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3165 } else { // Show no photo link
3166 $nophoto = '/public/theme/common/nophoto.png';
3167 $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>';
3168 }
3169 }
3170 } elseif ($object->element == 'category') {
3172 '@phan-var-force Categorie $object';
3173 $width = 80;
3174 $cssclass = 'photowithmargin photoref';
3175 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3176 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3177 if ($conf->browser->layout == 'phone') {
3178 $maxvisiblephotos = 1;
3179 }
3180 if ($showimage) {
3181 $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>';
3182 } else {
3183 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3184 $nophoto = '';
3185 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3186 } else { // Show no photo link
3187 $nophoto = '/public/theme/common/nophoto.png';
3188 $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>';
3189 }
3190 }
3191 } elseif ($object->element == 'bom') {
3193 '@phan-var-force Bom $object';
3194 $width = 80;
3195 $cssclass = 'photowithmargin photoref';
3196 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3197 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3198 if ($conf->browser->layout == 'phone') {
3199 $maxvisiblephotos = 1;
3200 }
3201 if ($showimage) {
3202 $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>';
3203 } else {
3204 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3205 $nophoto = '';
3206 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3207 } else { // Show no photo link
3208 $nophoto = '/public/theme/common/nophoto.png';
3209 $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>';
3210 }
3211 }
3212 } elseif ($object->element == 'ticket') {
3213 $width = 80;
3214 $cssclass = 'photoref';
3216 '@phan-var-force Ticket $object';
3217 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
3218 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3219 if ($conf->browser->layout == 'phone') {
3220 $maxvisiblephotos = 1;
3221 }
3222
3223 if ($showimage) {
3224 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3225 if ($object->nbphoto > 0) {
3226 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
3227 } else {
3228 $showimage = 0;
3229 }
3230 }
3231 if (!$showimage) {
3232 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3233 $nophoto = '';
3234 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3235 } else { // Show no photo link
3236 $nophoto = img_picto('No photo', 'object_ticket');
3237 $morehtmlleft .= '<!-- No photo to show -->';
3238 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3239 $morehtmlleft .= $nophoto;
3240 $morehtmlleft .= '</div></div>';
3241 }
3242 }
3243 } else {
3244 if ($showimage) {
3245 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3246 $phototoshow = '';
3247 // Check if a preview file is available
3248 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3249 $objectref = dol_sanitizeFileName($object->ref);
3250 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
3251 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3252 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3253 $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
3254 } else {
3255 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3256 }
3257 if (empty($subdir)) {
3258 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3259 }
3260
3261 $filepath = $dir_output.$subdir."/";
3262
3263 $filepdf = $filepath.$objectref.".pdf";
3264 $relativepath = $subdir.'/'.$objectref.'.pdf';
3265
3266 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3267 $fileimage = $filepdf.'_preview.png';
3268 $relativepathimage = $relativepath.'_preview.png';
3269
3270 $pdfexists = file_exists($filepdf);
3271
3272 // If PDF file exists
3273 if ($pdfexists) {
3274 // Conversion du PDF en image png si fichier png non existent
3275 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3276 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3277 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3278 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3279 if ($ret < 0) {
3280 $error++;
3281 }
3282 }
3283 }
3284 }
3285
3286 if ($pdfexists && !$error) {
3287 $heightforphotref = 80;
3288 if (!empty($conf->dol_optimize_smallscreen)) {
3289 $heightforphotref = 60;
3290 }
3291 // If the preview file is found
3292 if (file_exists($fileimage)) {
3293 $phototoshow = '<div class="photoref">';
3294 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3295 $phototoshow .= '</div>';
3296 }
3297 }
3298 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3299 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0);
3300 }
3301
3302 if ($phototoshow) {
3303 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3304 $morehtmlleft .= $phototoshow;
3305 $morehtmlleft .= '</div>';
3306 }
3307 }
3308
3309 if (empty($phototoshow)) { // Show No photo link (picto of object)
3310 if ($object->element == 'action') {
3311 $width = 80;
3312 $cssclass = 'photorefcenter';
3313 $nophoto = img_picto('No photo', 'title_agenda');
3314 } else {
3315 $width = 14;
3316 $cssclass = 'photorefcenter';
3317 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3318 $prefix = 'object_';
3319 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3320 $picto = 'project'; // instead of projectpub
3321 }
3322 if (strpos($picto, 'fontawesome_') !== false) {
3323 $prefix = '';
3324 }
3325 $nophoto = img_picto('No photo', $prefix.$picto);
3326 }
3327 $morehtmlleft .= '<!-- No photo to show -->';
3328 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3329 $morehtmlleft .= $nophoto;
3330 $morehtmlleft .= '</div></div>';
3331 }
3332 }
3333 }
3334
3335 // Show barcode
3336 if ($showbarcode) {
3337 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3338 }
3339
3340 if ($object->element == 'societe') {
3341 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3342 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3343 } else {
3344 $morehtmlstatus .= $object->getLibStatut(6);
3345 }
3346 } elseif ($object->element == 'product') {
3347 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3348 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3349 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3350 } else {
3351 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3352 }
3353 $morehtmlstatus .= ' &nbsp; ';
3354 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3355 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3356 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3357 } else {
3358 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3359 }
3360 } elseif (in_array($object->element, array('salary'))) {
3361 '@phan-var-force Salary $object';
3362 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3363 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3364 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3365 }
3366 $morehtmlstatus .= $tmptxt;
3367 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3368 '@phan-var-force Facture|FactureFournisseur|CommonInvoice $object';
3369 $totalallpayments = $object->getSommePaiement(0);
3370 $totalallpayments += $object->getSumCreditNotesUsed(0);
3371 $totalallpayments += $object->getSumDepositsUsed(0);
3372 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3373 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3374 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3375 }
3376 $morehtmlstatus .= $tmptxt;
3377 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3378 '@phan-var-force ChargeSociales|Loan|Tva $object';
3379 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3380 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3381 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3382 }
3383 $morehtmlstatus .= $tmptxt;
3384 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3385 if ($object->statut == 0) {
3386 $morehtmlstatus .= $object->getLibStatut(5);
3387 } else {
3388 $morehtmlstatus .= $object->getLibStatut(4);
3389 }
3390 } elseif ($object->element == 'facturerec') {
3391 '@phan-var-force FactureRec $object';
3392 if ($object->frequency == 0) {
3393 $morehtmlstatus .= $object->getLibStatut(2);
3394 } else {
3395 $morehtmlstatus .= $object->getLibStatut(5);
3396 }
3397 } elseif ($object->element == 'project_task') {
3398 $tmptxt = $object->getLibStatut(4);
3399 $morehtmlstatus .= $tmptxt;
3400 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3401 $tmptxt = $object->getLibStatut(6);
3402 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3403 $tmptxt = $object->getLibStatut(5);
3404 }
3405 $morehtmlstatus .= $tmptxt;
3406 }
3407
3408 // Say if object was dispatched/transferred "into accountancy"
3409 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3410 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3411 if (method_exists($object, 'getVentilExportCompta')) {
3412 $accounted = $object->getVentilExportCompta(1);
3413 $langs->load("accountancy");
3414 $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>';
3415 }
3416 }
3417
3418 // Add alias for thirdparty
3419 if (!empty($object->name_alias)) {
3420 '@phan-var-force Societe $object';
3421 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3422 }
3423
3424 // Add label
3425 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3426 if (!empty($object->label)) {
3427 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3428 }
3429 }
3430 // Show address and email
3431 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3432 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3433 if ($moreaddress) {
3434 $morehtmlref .= '<div class="refidno refaddress">';
3435 $morehtmlref .= $moreaddress;
3436 $morehtmlref .= '</div>';
3437 }
3438 }
3439 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)) {
3440 $morehtmlref .= '<div style="clear: both;"></div>';
3441 $morehtmlref .= '<div class="refidno opacitymedium">';
3442 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3443 $morehtmlref .= '</div>';
3444 }
3445
3446 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3447 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3448 if ($reshook < 0) {
3449 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3450 } elseif (empty($reshook)) {
3451 $morehtmlref .= $hookmanager->resPrint;
3452 } elseif ($reshook > 0) {
3453 $morehtmlref = $hookmanager->resPrint;
3454 }
3455
3456 // $morehtml is the right part (link "Back to list")
3457 // $morehtmlleft is the picto or photo of banner
3458 // $morehtmlstatus is part under the status
3459 // $morehtmlright is part of htmlright
3460
3461 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3462 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3463 print '</div>';
3464 print '<div class="underrefbanner clearboth"></div>';
3465}
3466
3476function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3477{
3478 global $langs;
3479 $ret = '';
3480 if ($fieldrequired) {
3481 $ret .= '<span class="fieldrequired">';
3482 }
3483 $ret .= '<label for="'.$fieldkey.'">';
3484 $ret .= $langs->trans($langkey);
3485 $ret .= '</label>';
3486 if ($fieldrequired) {
3487 $ret .= '</span>';
3488 }
3489 return $ret;
3490}
3491
3505function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3506{
3507 global $langs, $hookmanager;
3508
3509 $ret = '';
3510 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3511
3512 // See format of addresses on https://en.wikipedia.org/wiki/Address
3513 // Address
3514 if (empty($mode)) {
3515 $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)));
3516 }
3517 // Zip/Town/State
3518 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3519 // US: title firstname name \n address lines \n town, state, zip \n country
3520 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3521 $ret .= (($ret && $town) ? $sep : '').$town;
3522
3523 if (!empty($object->state)) {
3524 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3525 }
3526 if (!empty($object->zip)) {
3527 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3528 }
3529 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3530 // UK: title firstname name \n address lines \n town state \n zip \n country
3531 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3532 $ret .= ($ret ? $sep : '').$town;
3533 if (!empty($object->state)) {
3534 $ret .= ($ret ? ", " : '').$object->state;
3535 }
3536 if (!empty($object->zip)) {
3537 $ret .= ($ret ? $sep : '').$object->zip;
3538 }
3539 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3540 // ES: title firstname name \n address lines \n zip town \n state \n country
3541 $ret .= ($ret ? $sep : '').$object->zip;
3542 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3543 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3544 if (!empty($object->state)) {
3545 $ret .= $sep.$object->state;
3546 }
3547 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3548 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3549 // See https://www.sljfaq.org/afaq/addresses.html
3550 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3551 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3552 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3553 // IT: title firstname name\n address lines \n zip town state_code \n country
3554 $ret .= ($ret ? $sep : '').$object->zip;
3555 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3556 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3557 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3558 } else {
3559 // Other: title firstname name \n address lines \n zip town[, state] \n country
3560 $town = (($extralangcode && !empty($object->array_languages['address'][$extralangcode])) ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3561 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3562 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3563 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3564 $ret .= ($ret ? ", " : '').$object->state;
3565 }
3566 }
3567
3568 if (!is_object($outputlangs)) {
3569 $outputlangs = $langs;
3570 }
3571 if ($withcountry) {
3572 $langs->load("dict");
3573 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3574 }
3575 if ($hookmanager) {
3576 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3577 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3578 if ($reshook > 0) {
3579 $ret = '';
3580 }
3581 $ret .= $hookmanager->resPrint;
3582 }
3583
3584 return $ret;
3585}
3586
3587
3588
3598function dol_strftime($fmt, $ts = false, $is_gmt = false)
3599{
3600 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3601 return dol_print_date($ts, $fmt, $is_gmt);
3602 } else {
3603 return 'Error date outside supported range';
3604 }
3605}
3606
3628function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3629{
3630 global $conf, $langs;
3631
3632 // If date undefined or "", we return ""
3633 if (dol_strlen((string) $time) == 0) {
3634 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3635 }
3636
3637 if ($tzoutput === 'auto') {
3638 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3639 }
3640
3641 // Clean parameters
3642 $to_gmt = false;
3643 $offsettz = $offsetdst = 0;
3644 if ($tzoutput) {
3645 $to_gmt = true; // For backward compatibility
3646 if (is_string($tzoutput)) {
3647 if ($tzoutput == 'tzserver') {
3648 $to_gmt = false;
3649 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3650 // @phan-suppress-next-line PhanPluginRedundantAssignment
3651 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3652 // @phan-suppress-next-line PhanPluginRedundantAssignment
3653 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3654 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3655 $to_gmt = true;
3656 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3657
3658 if (class_exists('DateTimeZone')) {
3659 $user_date_tz = new DateTimeZone($offsettzstring);
3660 $user_dt = new DateTime();
3661 $user_dt->setTimezone($user_date_tz);
3662 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3663 $offsettz = $user_dt->getOffset(); // should include dst ?
3664 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3665 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3666 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3667 }
3668 }
3669 }
3670 }
3671 if (!is_object($outputlangs)) {
3672 $outputlangs = $langs;
3673 }
3674 if (!$format) {
3675 $format = 'daytextshort';
3676 }
3677
3678 // Do we have to reduce the length of date (year on 2 chars) to save space.
3679 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3680 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3681 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3682 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3683 if ($formatwithoutreduce != $format) {
3684 $format = $formatwithoutreduce;
3685 $reduceformat = 1;
3686 } // so format 'dayreduceformat' is processed like day
3687
3688 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3689 // TODO Add format daysmallyear and dayhoursmallyear
3690 if ($format == 'day') {
3691 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3692 } elseif ($format == 'hour') {
3693 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3694 } elseif ($format == 'hourduration') {
3695 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3696 } elseif ($format == 'daytext') {
3697 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3698 } elseif ($format == 'daytextshort') {
3699 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3700 } elseif ($format == 'dayhour') {
3701 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3702 } elseif ($format == 'dayhoursec') {
3703 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3704 } elseif ($format == 'dayhourtext') {
3705 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3706 } elseif ($format == 'dayhourtextshort') {
3707 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3708 } elseif ($format == 'dayhourlog') {
3709 // Format not sensitive to language
3710 $format = '%Y%m%d%H%M%S';
3711 } elseif ($format == 'dayhourlogsmall') {
3712 // Format not sensitive to language
3713 $format = '%y%m%d%H%M';
3714 } elseif ($format == 'dayhourldap') {
3715 $format = '%Y%m%d%H%M%SZ';
3716 } elseif ($format == 'dayhourxcard') {
3717 $format = '%Y%m%dT%H%M%SZ';
3718 } elseif ($format == 'dayxcard') {
3719 $format = '%Y%m%d';
3720 } elseif ($format == 'dayrfc') {
3721 $format = '%Y-%m-%d'; // DATE_RFC3339
3722 } elseif ($format == 'dayhourrfc') {
3723 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3724 } elseif ($format == 'standard') {
3725 $format = '%Y-%m-%d %H:%M:%S';
3726 }
3727
3728 if ($reduceformat) {
3729 $format = str_replace('%Y', '%y', $format);
3730 $format = str_replace('yyyy', 'yy', $format);
3731 }
3732
3733 // Clean format
3734 if (preg_match('/%b/i', $format)) { // There is some text to translate
3735 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3736 $format = str_replace('%b', '__b__', $format);
3737 $format = str_replace('%B', '__B__', $format);
3738 }
3739 if (preg_match('/%a/i', $format)) { // There is some text to translate
3740 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3741 $format = str_replace('%a', '__a__', $format);
3742 $format = str_replace('%A', '__A__', $format);
3743 }
3744
3745 // Analyze date
3746 $reg = array();
3747 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
3748 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"]));
3749 return '';
3750 } 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
3751 // This part of code should not be used anymore.
3752 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);
3753 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3754 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3755 $syear = (!empty($reg[1]) ? $reg[1] : '');
3756 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3757 $sday = (!empty($reg[3]) ? $reg[3] : '');
3758 $shour = (!empty($reg[4]) ? $reg[4] : '');
3759 $smin = (!empty($reg[5]) ? $reg[5] : '');
3760 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3761
3762 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3763
3764 if ($to_gmt) {
3765 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3766 } else {
3767 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3768 }
3769 $dtts = new DateTime();
3770 $dtts->setTimestamp($time);
3771 $dtts->setTimezone($tzo);
3772 $newformat = str_replace(
3773 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3774 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3775 $format
3776 );
3777 $ret = $dtts->format($newformat);
3778 $ret = str_replace(
3779 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3780 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3781 $ret
3782 );
3783 } else {
3784 // Date is a timestamps
3785 if ($time < 100000000000) { // Protection against bad date values
3786 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3787
3788 if ($to_gmt) {
3789 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3790 } else {
3791 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3792 }
3793 $dtts = new DateTime();
3794 $dtts->setTimestamp($timetouse);
3795 $dtts->setTimezone($tzo);
3796 $newformat = str_replace(
3797 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3798 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3799 $format
3800 );
3801 $ret = $dtts->format($newformat);
3802 $ret = str_replace(
3803 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3804 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3805 $ret
3806 );
3807 //var_dump($ret);exit;
3808 } else {
3809 $ret = 'Bad value '.$time.' for date';
3810 }
3811 }
3812
3813 if (preg_match('/__b__/i', $format)) {
3814 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3815
3816 if ($to_gmt) {
3817 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3818 } else {
3819 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3820 }
3821 $dtts = new DateTime();
3822 $dtts->setTimestamp($timetouse);
3823 $dtts->setTimezone($tzo);
3824 $month = (int) $dtts->format("m");
3825 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3826 if ($encodetooutput) {
3827 $monthtext = $outputlangs->transnoentities('Month'.$month);
3828 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3829 } else {
3830 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3831 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3832 }
3833 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3834 $ret = str_replace('__b__', $monthtextshort, $ret);
3835 $ret = str_replace('__B__', $monthtext, $ret);
3836 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3837 //return $ret;
3838 }
3839 if (preg_match('/__a__/i', $format)) {
3840 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3841 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3842
3843 if ($to_gmt) {
3844 $tzo = new DateTimeZone('UTC');
3845 } else {
3846 $tzo = new DateTimeZone(date_default_timezone_get());
3847 }
3848 $dtts = new DateTime();
3849 $dtts->setTimestamp($timetouse);
3850 $dtts->setTimezone($tzo);
3851 $w = $dtts->format("w");
3852 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3853
3854 $ret = str_replace('__A__', $dayweek, $ret);
3855 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3856 }
3857
3858 return $ret;
3859}
3860
3861
3882function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3883{
3884 if ($timestamp === '') {
3885 return array();
3886 }
3887
3888 $datetimeobj = new DateTime();
3889 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3890 if ($forcetimezone) {
3891 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3892 }
3893 $arrayinfo = array(
3894 'year' => ((int) date_format($datetimeobj, 'Y')),
3895 'mon' => ((int) date_format($datetimeobj, 'm')),
3896 'mday' => ((int) date_format($datetimeobj, 'd')),
3897 'wday' => ((int) date_format($datetimeobj, 'w')),
3898 'yday' => ((int) date_format($datetimeobj, 'z')),
3899 'hours' => ((int) date_format($datetimeobj, 'H')),
3900 'minutes' => ((int) date_format($datetimeobj, 'i')),
3901 'seconds' => ((int) date_format($datetimeobj, 's')),
3902 '0' => $timestamp
3903 );
3904
3905 return $arrayinfo;
3906}
3907
3929function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3930{
3931 global $conf;
3932 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3933
3934 if ($gm === 'auto') {
3935 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3936 }
3937 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3938
3939 // Clean parameters
3940 if ($hour == -1 || empty($hour)) {
3941 $hour = 0;
3942 }
3943 if ($minute == -1 || empty($minute)) {
3944 $minute = 0;
3945 }
3946 if ($second == -1 || empty($second)) {
3947 $second = 0;
3948 }
3949
3950 // Check parameters
3951 if ($check) {
3952 if (!$month || !$day) {
3953 return '';
3954 }
3955 if ($day > 31) {
3956 return '';
3957 }
3958 if ($month > 12) {
3959 return '';
3960 }
3961 if ($hour < 0 || $hour > 24) {
3962 return '';
3963 }
3964 if ($minute < 0 || $minute > 60) {
3965 return '';
3966 }
3967 if ($second < 0 || $second > 60) {
3968 return '';
3969 }
3970 }
3971
3972 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3973 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3974 $localtz = new DateTimeZone($default_timezone);
3975 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3976 // We use dol_tz_string first because it is more reliable.
3977 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3978 try {
3979 $localtz = new DateTimeZone($default_timezone);
3980 } catch (Exception $e) {
3981 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3982 $default_timezone = @date_default_timezone_get();
3983 }
3984 } elseif (strrpos($gm, "tz,") !== false) {
3985 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3986 try {
3987 $localtz = new DateTimeZone($timezone);
3988 } catch (Exception $e) {
3989 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3990 }
3991 }
3992
3993 if (empty($localtz)) {
3994 $localtz = new DateTimeZone('UTC');
3995 }
3996 //var_dump($localtz);
3997 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3998 $dt = new DateTime('now', $localtz);
3999 $dt->setDate((int) $year, (int) $month, (int) $day);
4000 $dt->setTime((int) $hour, (int) $minute, (int) $second);
4001 $date = $dt->getTimestamp(); // should include daylight saving time
4002 //var_dump($date);
4003 return $date;
4004}
4005
4006
4017function dol_now($mode = 'auto')
4018{
4019 $ret = 0;
4020
4021 if ($mode === 'auto') {
4022 $mode = 'gmt';
4023 }
4024
4025 if ($mode == 'gmt') {
4026 $ret = time(); // Time for now at greenwich.
4027 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
4028 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4029 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
4030 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
4031 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
4032 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
4033 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
4034 // $ret=dol_now('gmt')+($tzsecond*3600);
4035 //}
4036 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
4037 // Time for now with user timezone added
4038 //print 'time: '.time();
4039 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
4040 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
4041 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
4042 }
4043
4044 return $ret;
4045}
4046
4047
4056function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
4057{
4058 global $conf, $langs;
4059 $level = 1024;
4060
4061 if (!empty($conf->dol_optimize_smallscreen)) {
4062 $shortunit = 1;
4063 }
4064
4065 // Set value text
4066 if (empty($shortvalue) || $size < ($level * 10)) {
4067 $ret = $size;
4068 $textunitshort = $langs->trans("b");
4069 $textunitlong = $langs->trans("Bytes");
4070 } else {
4071 $ret = round($size / $level, 0);
4072 $textunitshort = $langs->trans("Kb");
4073 $textunitlong = $langs->trans("KiloBytes");
4074 }
4075 // Use long or short text unit
4076 if (empty($shortunit)) {
4077 $ret .= ' '.$textunitlong;
4078 } else {
4079 $ret .= ' '.$textunitshort;
4080 }
4081
4082 return $ret;
4083}
4084
4095function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
4096{
4097 global $langs;
4098
4099 if (empty($url)) {
4100 return '';
4101 }
4102
4103 $linkstart = '<a href="';
4104 if (!preg_match('/^http/i', $url)) {
4105 $linkstart .= 'http://';
4106 }
4107 $linkstart .= $url;
4108 $linkstart .= '"';
4109 if ($target) {
4110 $linkstart .= ' target="'.$target.'"';
4111 }
4112 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
4113 $linkstart .= '>';
4114
4115 $link = '';
4116 if (!preg_match('/^http/i', $url)) {
4117 $link .= 'http://';
4118 }
4119 $link .= dol_trunc($url, $max);
4120
4121 $linkend = '</a>';
4122
4123 if ($morecss == 'float') { // deprecated
4124 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
4125 } else {
4126 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
4127 }
4128}
4129
4143function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
4144{
4145 global $user, $langs, $hookmanager;
4146
4147 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4148 //$showinvalid = 1; $email = 'rrrrr';
4149
4150 $newemail = dol_escape_htmltag($email);
4151
4152 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4153 $withpicto = 0;
4154 }
4155
4156 if (empty($email)) {
4157 return '&nbsp;';
4158 }
4159
4160 if ($addlink == 1) {
4161 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
4162 if (!preg_match('/^mailto:/i', $email)) {
4163 $newemail .= 'mailto:';
4164 }
4165 $newemail .= $email;
4166 $newemail .= '" target="_blank">';
4167
4168 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4169
4170 if ($max > 0) {
4171 $newemail .= dol_trunc($email, $max);
4172 } else {
4173 $newemail .= $email;
4174 }
4175 $newemail .= '</a>';
4176 if ($showinvalid && !isValidEmail($email)) {
4177 $langs->load("errors");
4178 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
4179 }
4180
4181 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4182 $type = 'AC_EMAIL';
4183 $linktoaddaction = '';
4184 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4185 $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>';
4186 }
4187 if ($linktoaddaction) {
4188 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
4189 }
4190 }
4191 } elseif ($addlink === 'thirdparty') {
4192 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
4193 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4194 if ($withpicto == 1) {
4195 $tmpnewemail .= $newemail;
4196 }
4197 $tmpnewemail .= '</a>';
4198
4199 $newemail = $tmpnewemail;
4200 } else {
4201 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4202
4203 if ($showinvalid && !isValidEmail($email)) {
4204 $langs->load("errors");
4205 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
4206 }
4207 }
4208
4209 //$rep = '<div class="nospan" style="margin-right: 10px">';
4210 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4211 //$rep .= '</div>';
4212 $rep = $newemail;
4213
4214 if ($hookmanager) {
4215 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4216
4217 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4218 if ($reshook > 0) {
4219 $rep = '';
4220 }
4221 $rep .= $hookmanager->resPrint;
4222 }
4223
4224 return $rep;
4225}
4226
4232function getArrayOfSocialNetworks()
4233{
4234 global $conf, $db;
4235
4236 $socialnetworks = array();
4237 // Enable caching of array
4238 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
4239 $cachekey = 'socialnetworks_' . $conf->entity;
4240 $dataretrieved = dol_getcache($cachekey);
4241 if (!is_null($dataretrieved)) {
4242 $socialnetworks = $dataretrieved;
4243 } else {
4244 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
4245 $sql .= " WHERE entity=".$conf->entity;
4246 $resql = $db->query($sql);
4247 if ($resql) {
4248 while ($obj = $db->fetch_object($resql)) {
4249 $socialnetworks[$obj->code] = array(
4250 'rowid' => $obj->rowid,
4251 'label' => $obj->label,
4252 'url' => $obj->url,
4253 'icon' => $obj->icon,
4254 'active' => $obj->active,
4255 );
4256 }
4257 }
4258 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4259 }
4260 return $socialnetworks;
4261}
4262
4273function dol_print_socialnetworks($value, $contactid, $socid, $type, $dictsocialnetworks = array())
4274{
4275 global $hookmanager, $langs, $user;
4276
4277 $htmllink = $value;
4278
4279 if (empty($value)) {
4280 return '&nbsp;';
4281 }
4282
4283 if (!empty($type)) {
4284 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4285 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4286 $htmllink .= '<span class="fab pictofixedwidth ' . ($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link') . '"></span>';
4287 if ($type == 'skype') {
4288 $htmllink .= dol_escape_htmltag($value);
4289 $htmllink .= '&nbsp; <a href="skype:';
4290 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4291 $htmllink .= '?call" alt="' . $langs->trans("Call") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Call") . ' ' . $value) . '">';
4292 $htmllink .= '<img src="' . DOL_URL_ROOT . '/theme/common/skype_callbutton.png" border="0">';
4293 $htmllink .= '</a><a href="skype:';
4294 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4295 $htmllink .= '?chat" alt="' . $langs->trans("Chat") . '&nbsp;' . $value . '" title="' . dol_escape_htmltag($langs->trans("Chat") . ' ' . $value) . '">';
4296 $htmllink .= '<img class="paddingleft" src="' . DOL_URL_ROOT . '/theme/common/skype_chatbutton.png" border="0">';
4297 $htmllink .= '</a>';
4298 if (($contactid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4299 $addlink = 'AC_SKYPE';
4300 $link = '';
4301 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4302 $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>';
4303 }
4304 $htmllink .= ($link ? ' ' . $link : '');
4305 }
4306 } else {
4307 if (!empty($dictsocialnetworks[$type]['url'])) {
4308 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4309 if ($tmpvirginurl) {
4310 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4311 $value = preg_replace('/^' . preg_quote($tmpvirginurl, '/') . '\/?/', '', $value);
4312
4313 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4314 if ($tmpvirginurl3) {
4315 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4316 $value = preg_replace('/^' . preg_quote($tmpvirginurl3, '/') . '\/?/', '', $value);
4317 }
4318
4319 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4320 if ($tmpvirginurl2) {
4321 $value = preg_replace('/^www\.' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4322 $value = preg_replace('/^' . preg_quote($tmpvirginurl2, '/') . '\/?/', '', $value);
4323 }
4324 }
4325 if (preg_match('/^https?:\/\//i', $value)) {
4326 $link = $value;
4327 } else {
4328 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4329 }
4330 $valuetoshow = $value;
4331 $valuetoshow = preg_replace('/https:\/\/www\.(twitter|x|linkedin)\.com\/?/', '', $valuetoshow);
4332 if (preg_match('/^https?:\/\//i', $link)) {
4333 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 0) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4334 } else {
4335 $htmllink .= '<a href="' . dol_sanitizeUrl($link, 1) . '" target="_blank" rel="noopener noreferrer">' . dol_escape_htmltag($valuetoshow) . '</a>';
4336 }
4337 } else {
4338 $htmllink .= dol_escape_htmltag($value);
4339 }
4340 }
4341 $htmllink .= '</div>';
4342 } else {
4343 $langs->load("errors");
4344 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4345 }
4346
4347 if ($hookmanager) {
4348 $parameters = array(
4349 'value' => $value,
4350 'cid' => $contactid,
4351 'socid' => $socid,
4352 'type' => $type,
4353 'dictsocialnetworks' => $dictsocialnetworks,
4354 );
4355
4356 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4357 if ($reshook > 0) {
4358 $htmllink = '';
4359 }
4360 $htmllink .= $hookmanager->resPrint;
4361 }
4362
4363 return $htmllink;
4364}
4365
4375function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4376{
4377 global $mysoc;
4378
4379 if (empty($profID) || empty($profIDtype)) {
4380 return '';
4381 }
4382 if (empty($countrycode)) {
4383 $countrycode = $mysoc->country_code;
4384 }
4385 $newProfID = $profID;
4386 $id = substr($profIDtype, -1);
4387 $ret = '';
4388 if (strtoupper($countrycode) == 'FR') {
4389 // France
4390 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4391
4392 if ($id == 1 && dol_strlen($newProfID) == 9) {
4393 // SIREN (ex: 123 123 123)
4394 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4395 }
4396 if ($id == 2 && dol_strlen($newProfID) == 14) {
4397 // SIRET (ex: 123 123 123 12345)
4398 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4399 }
4400 if ($id == 3 && dol_strlen($newProfID) == 5) {
4401 // NAF/APE (ex: 69.20Z)
4402 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4403 }
4404 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4405 // TVA intracommunautaire (ex: FR12 123 123 123)
4406 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4407 }
4408 }
4409 if (!empty($addcpButton)) {
4410 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4411 } else {
4412 $ret = $newProfID;
4413 }
4414 return $ret;
4415}
4416
4432function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4433{
4434 global $conf, $user, $langs, $mysoc, $hookmanager;
4435
4436 // Clean phone parameter
4437 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4438 if (empty($phone)) {
4439 return '';
4440 }
4441 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4442 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4443 }
4444 if (empty($countrycode) && is_object($mysoc)) {
4445 $countrycode = $mysoc->country_code;
4446 }
4447
4448 // Short format for small screens
4449 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4450 $separ = '';
4451 }
4452
4453 $newphone = $phone;
4454 $newphonewa = $phone;
4455 if (strtoupper($countrycode) == "FR") {
4456 // France
4457 if (dol_strlen($phone) == 10) {
4458 $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);
4459 } elseif (dol_strlen($phone) == 7) {
4460 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4461 } elseif (dol_strlen($phone) == 9) {
4462 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4463 } elseif (dol_strlen($phone) == 11) {
4464 $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);
4465 } elseif (dol_strlen($phone) == 12) {
4466 $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);
4467 } elseif (dol_strlen($phone) == 13) {
4468 $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);
4469 }
4470 } elseif (strtoupper($countrycode) == "CA") {
4471 if (dol_strlen($phone) == 10) {
4472 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4473 }
4474 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4475 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4476 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4477 }
4478 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4479 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4480 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4481 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4482 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4483 }
4484 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4485 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4486 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4487 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4488 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4489 }
4490 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4491 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4492 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4493 }
4494 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4495 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4496 $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);
4497 }
4498 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4499 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4500 $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);
4501 }
4502 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4503 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4504 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4505 }
4506 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4507 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4508 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4509 }
4510 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4511 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4512 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4513 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4514 $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);
4515 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4516 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4517 }
4518 } elseif (strtoupper($countrycode) == "ML") {//Mali
4519 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4520 $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);
4521 }
4522 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4523 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4524 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4525 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4526 $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);
4527 }
4528 } elseif (strtoupper($countrycode) == "MU") {
4529 //Maurice
4530 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4531 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4532 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4533 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4534 }
4535 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4536 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4537 $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);
4538 }
4539 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4540 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4541 $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);
4542 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4543 $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);
4544 }
4545 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4546 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4547 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4548 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4549 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4550 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4551 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4552 }
4553 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4554 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4555 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4556 }
4557 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4558 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4559 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4560 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4561 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4562 }
4563 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4564 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4565 $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);
4566 }
4567 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4568 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4569 $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);
4570 }
4571 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4572 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4573 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 1).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4574 }
4575 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4576 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4577 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4578 }
4579 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4580 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4581 $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);
4582 }
4583 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4584 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4585 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4586 }
4587 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4588 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4589 $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);
4590 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4591 $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);
4592 }
4593 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4594 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4595 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4596 }
4597 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4598 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4599 $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);
4600 }
4601 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4602 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 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) == "MQ") {//Martinique
4606 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 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) == "IT") {//Italie
4610 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4611 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4612 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4613 $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);
4614 }
4615 } elseif (strtoupper($countrycode) == "AU") {
4616 //Australie
4617 if (dol_strlen($phone) == 12) {
4618 //ex: +61_A_BCDE_FGHI
4619 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4620 }
4621 } elseif (strtoupper($countrycode) == "LU") {
4622 // Luxembourg
4623 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4624 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4625 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4626 $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);
4627 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4628 $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);
4629 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4630 $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);
4631 }
4632 } elseif (strtoupper($countrycode) == "PE") {
4633 // Peru
4634 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4635 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4636 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4637 $newphonewa = '+51'.$newphone;
4638 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4639 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4640 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4641 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4642 $newphonewa = $newphone;
4643 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3).$separ.substr($newphone, 14, 3);
4644 }
4645 } elseif (strtoupper($countrycode) == "IN") {//India
4646 if (dol_strlen($phone) == 13) {
4647 if ($withpicto == 'phone') {//ex: +91_AB_CDEF_GHIJ
4648 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 4).$separ.substr($newphone, 9, 4);
4649 } else {//ex: +91_ABCDE_FGHIJ
4650 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 5).$separ.substr($newphone, 8, 5);
4651 }
4652 }
4653 }
4654
4655 $newphoneastart = $newphoneaend = '';
4656 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4657 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
4658 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4659 $newphoneaend .= '</a>';
4660 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4661 if (empty($user->clicktodial_loaded)) {
4662 $user->fetch_clicktodial();
4663 }
4664
4665 // Define urlmask
4666 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4667 if (!empty($user->clicktodial_url)) {
4668 $urlmask = $user->clicktodial_url;
4669 }
4670
4671 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4672 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4673 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4674 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4675 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4676 // Those lines are for substitution
4677 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4678 '__PHONETO__' => urlencode($phone),
4679 '__LOGIN__' => $clicktodial_login,
4680 '__PASS__' => $clicktodial_password);
4681 $url = make_substitutions($url, $substitarray);
4682 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4683 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4684 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4685 $newphoneaend = '</a>';
4686 } else {
4687 // Old method
4688 $newphoneastart = '<a href="'.$url.'"';
4689 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4690 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4691 }
4692 $newphoneastart .= '>';
4693 $newphoneaend .= '</a>';
4694 }
4695 }
4696
4697 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4698 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4699 $type = 'AC_TEL';
4700 $addlinktoagenda = '';
4701 if ($addlink == 'AC_FAX') {
4702 $type = 'AC_FAX';
4703 }
4704 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4705 $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>';
4706 }
4707 if ($addlinktoagenda) {
4708 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4709 }
4710 }
4711 }
4712
4713 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4714 // Link to Whatsapp
4715 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4716 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4717 }
4718
4719 if (empty($titlealt)) {
4720 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4721 }
4722 $rep = '';
4723
4724 if ($hookmanager) {
4725 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4726 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4727 $rep .= $hookmanager->resPrint;
4728 }
4729 if (empty($reshook)) {
4730 $picto = '';
4731 if ($withpicto) {
4732 if ($withpicto == 'fax') {
4733 $picto = 'phoning_fax';
4734 } elseif ($withpicto == 'phone') {
4735 $picto = 'phoning';
4736 } elseif ($withpicto == 'mobile') {
4737 $picto = 'phoning_mobile';
4738 } else {
4739 $picto = '';
4740 }
4741 }
4742 if ($adddivfloat == 1) {
4743 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4744 } elseif (empty($adddivfloat)) {
4745 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4746 }
4747
4748 $rep .= $newphoneastart;
4749 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4750 if ($separ != 'hidenum') {
4751 $rep .= ($withpicto ? ' ' : '').$newphone;
4752 }
4753 $rep .= $newphoneaend;
4754
4755 if ($adddivfloat == 1) {
4756 $rep .= '</div>';
4757 } elseif (empty($adddivfloat)) {
4758 $rep .= '</span>';
4759 }
4760 }
4761
4762 return $rep;
4763}
4764
4773function dol_print_ip($ip, $mode = 0, $showname = 0)
4774{
4775 global $conf;
4776
4777 $ret = '';
4778 if (!isset($conf->cache['resolveips'])) {
4779 $conf->cache['resolveips'] = array();
4780 }
4781
4782 if ($mode != 2) {
4783 $countrycode = dolGetCountryCodeFromIp($ip);
4784 if ($countrycode) { // If success, countrycode is us, fr, ...
4785 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4786 $ret .= picto_from_langcode($countrycode);
4787 // $ret .= img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4788 } else {
4789 $ret .= '('.$countrycode.')';
4790 }
4791 $ret .= '&nbsp;';
4792 } else {
4793 // Nothing
4794 }
4795 }
4796
4797 if (in_array($mode, [0, 2])) {
4798 $domain = '';
4799 if ($showname) {
4800 if (!array_key_exists($ip, $conf->cache['resolveips'])) {
4801 $domain = gethostbyaddr($ip);
4802 $conf->cache['resolveips'][$ip] = $domain; // false or domain
4803 } else {
4804 $domain = $conf->cache['resolveips'][$ip];
4805 }
4806 }
4807 if ($domain) {
4808 $ret .= $domain;
4809 } else {
4810 $ret .= $ip;
4811 }
4812 }
4813
4814 return $ret;
4815}
4816
4829function getUserRemoteIP($trusted = 0)
4830{
4831 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
4832 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
4833 // 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)
4834 // This can happen if the proxy were added in the list of trusted proxy.
4835 return $ip;
4836 }
4837
4838 // Try to guess the real IP of client (but this may not be reliable)
4839 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4840 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
4841 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4842 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
4843 } else {
4844 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4845 }
4846 } else {
4847 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
4848 }
4849 } else {
4850 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
4851 }
4852 return $ip;
4853}
4854
4863function isHTTPS()
4864{
4865 $isSecure = false;
4866 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4867 $isSecure = true;
4868 } 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') {
4869 $isSecure = true;
4870 }
4871 return $isSecure;
4872}
4873
4880function dolGetCountryCodeFromIp($ip)
4881{
4882 $countrycode = '';
4883
4884 if (isModEnabled('geoipmaxmind')) {
4885 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4886 //$ip='24.24.24.24';
4887 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4888 if ($datafile) {
4889 try {
4890 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4891 $geoip = new DolGeoIP('country', $datafile);
4892 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4893 $countrycode = $geoip->getCountryCodeFromIP($ip);
4894 } catch (Exception $e) {
4895 //print 'Error with GeoIP database: '.$e->getMessage();
4896 }
4897 }
4898 }
4899
4900 return $countrycode;
4901}
4902
4903
4910function dol_user_country()
4911{
4912 global $conf, $langs, $user;
4913
4914 //$ret=$user->xxx;
4915 $ret = '';
4916 if (isModEnabled('geoipmaxmind')) {
4917 $ip = getUserRemoteIP();
4918 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4919 //$ip='24.24.24.24';
4920 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4921 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4922 $geoip = new DolGeoIP('country', $datafile);
4923 $countrycode = $geoip->getCountryCodeFromIP($ip);
4924 $ret = $countrycode;
4925 }
4926 return $ret;
4927}
4928
4941function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4942{
4943 global $hookmanager;
4944
4945 $out = '';
4946
4947 if ($address) {
4948 if ($hookmanager) {
4949 $parameters = array('element' => $element, 'id' => $id);
4950 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4951 $out .= $hookmanager->resPrint;
4952 }
4953 if (empty($reshook)) {
4954 if (empty($charfornl)) {
4955 $out .= nl2br((string) $address);
4956 } else {
4957 $out .= preg_replace('/[\r\n]+/', $charfornl, (string) $address);
4958 }
4959
4960 // TODO Remove this block, we can add this using the hook now
4961 $showgmap = $showomap = 0;
4962 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4963 $showgmap = 1;
4964 }
4965 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4966 $showgmap = 1;
4967 }
4968 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4969 $showgmap = 1;
4970 }
4971 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4972 $showgmap = 1;
4973 }
4974 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4975 $showomap = 1;
4976 }
4977 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4978 $showomap = 1;
4979 }
4980 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4981 $showomap = 1;
4982 }
4983 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4984 $showomap = 1;
4985 }
4986 if ($showgmap) {
4987 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4988 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4989 }
4990 if ($showomap) {
4991 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4992 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4993 }
4994 }
4995 }
4996 if ($noprint) {
4997 return $out;
4998 } else {
4999 print $out;
5000 return null;
5001 }
5002}
5003
5004
5014function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
5015{
5016 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
5017 return true;
5018 }
5019 if ($acceptuserkey && $address == '__USER_EMAIL__') {
5020 return true;
5021 }
5022 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
5023 return true;
5024 }
5025
5026 return false;
5027}
5028
5038function isValidMXRecord($domain)
5039{
5040 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
5041 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
5042 return 0;
5043 }
5044 if (function_exists('getmxrr')) {
5045 $mxhosts = array();
5046 $weight = array();
5047 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
5048 if (count($mxhosts) > 1) {
5049 return 1;
5050 }
5051 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
5052 return 1;
5053 }
5054
5055 return 0;
5056 }
5057 }
5058
5059 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
5060 return -1;
5061}
5062
5070function isValidPhone($phone)
5071{
5072 return true;
5073}
5074
5075
5085function dolGetFirstLetters($s, $nbofchar = 1)
5086{
5087 $ret = '';
5088 $tmparray = explode(' ', $s);
5089 foreach ($tmparray as $tmps) {
5090 $ret .= dol_substr($tmps, 0, $nbofchar);
5091 }
5092
5093 return $ret;
5094}
5095
5096
5104function dol_strlen($string, $stringencoding = 'UTF-8')
5105{
5106 if (is_null($string)) {
5107 return 0;
5108 }
5109
5110 if (function_exists('mb_strlen')) {
5111 return mb_strlen($string, $stringencoding);
5112 } else {
5113 return strlen($string);
5114 }
5115}
5116
5127function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5128{
5129 global $langs;
5130
5131 if (empty($stringencoding)) {
5132 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5133 }
5134
5135 $ret = '';
5136 if (empty($trunconbytes)) {
5137 if (function_exists('mb_substr')) {
5138 $ret = mb_substr($string, $start, $length, $stringencoding);
5139 } else {
5140 $ret = substr($string, $start, $length);
5141 }
5142 } else {
5143 if (function_exists('mb_strcut')) {
5144 $ret = mb_strcut($string, $start, $length, $stringencoding);
5145 } else {
5146 $ret = substr($string, $start, $length);
5147 }
5148 }
5149 return $ret;
5150}
5151
5152
5166function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5167{
5168 global $conf;
5169
5170 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5171 return $string;
5172 }
5173
5174 if (empty($stringencoding)) {
5175 $stringencoding = 'UTF-8';
5176 }
5177 // reduce for small screen
5178 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5179 $size = round($size / 3);
5180 }
5181
5182 // We go always here
5183 if ($trunc == 'right') {
5184 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5185 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5186 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5187 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
5188 } else {
5189 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5190 return $string;
5191 }
5192 } elseif ($trunc == 'middle') {
5193 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5194 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5195 $size1 = (int) round($size / 2);
5196 $size2 = (int) round($size / 2);
5197 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5198 } else {
5199 return $string;
5200 }
5201 } elseif ($trunc == 'left') {
5202 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5203 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5204 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5205 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5206 } else {
5207 return $string;
5208 }
5209 } elseif ($trunc == 'wrap') {
5210 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5211 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5212 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5213 } else {
5214 return $string;
5215 }
5216 } else {
5217 return 'BadParam3CallingDolTrunc';
5218 }
5219}
5220
5228function getPictoForType($key, $morecss = '')
5229{
5230 // Set array with type -> picto
5231 $type2picto = array(
5232 'varchar' => 'font',
5233 'text' => 'font',
5234 'html' => 'code',
5235 'int' => 'sort-numeric-down',
5236 'double' => 'sort-numeric-down',
5237 'price' => 'currency',
5238 'pricecy' => 'multicurrency',
5239 'password' => 'key',
5240 'boolean' => 'check-square',
5241 'date' => 'calendar',
5242 'datetime' => 'calendar',
5243 'duration' => 'hourglass',
5244 'phone' => 'phone',
5245 'mail' => 'email',
5246 'url' => 'url',
5247 'ip' => 'country',
5248 'select' => 'list',
5249 'sellist' => 'list',
5250 'stars' => 'fontawesome_star_fas',
5251 'radio' => 'check-circle',
5252 'checkbox' => 'list',
5253 'chkbxlst' => 'list',
5254 'link' => 'link',
5255 'icon' => "question",
5256 'point' => "country",
5257 'multipts' => 'country',
5258 'linestrg' => "country",
5259 'polygon' => "country",
5260 'separate' => 'minus'
5261 );
5262
5263 if (!empty($type2picto[$key])) {
5264 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5265 }
5266
5267 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5268}
5269
5270
5293function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5294{
5295 global $conf;
5296
5297 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5298 $url = DOL_URL_ROOT;
5299 $theme = isset($conf->theme) ? $conf->theme : null;
5300 $path = 'theme/'.$theme;
5301 if (empty($picto)) {
5302 $picto = 'generic';
5303 }
5304
5305 // Define fullpathpicto to use into src
5306 if ($pictoisfullpath) {
5307 // Clean parameters
5308 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5309 $picto .= '.png';
5310 }
5311 $fullpathpicto = $picto;
5312 $reg = array();
5313 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5314 $morecss .= ($morecss ? ' ' : '').$reg[1];
5315 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5316 }
5317 } else {
5318 // $picto can not be null since replaced with 'generic' in that case
5319 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5320 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5321 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5322 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5323
5324 // Fix some values of $pictowithouttext
5325 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5326 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5327 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5328 }
5329
5330 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5331 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5332 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5333 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5334
5335 // Compatibility with old fontawesome versions
5336 if ($pictowithouttext == 'file-o') {
5337 $pictowithouttext = 'file';
5338 }
5339
5340 $pictowithouttextarray = explode('_', $pictowithouttext);
5341 $marginleftonlyshort = 0;
5342
5343 if (!empty($pictowithouttextarray[1])) {
5344 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5345 $fakey = 'fa-'.$pictowithouttextarray[0];
5346 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5347 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5348 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5349 } else {
5350 $fakey = 'fa-'.$pictowithouttext;
5351 $faprefix = 'fas';
5352 $facolor = '';
5353 $fasize = '';
5354 }
5355
5356 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5357 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5358 $morestyle = '';
5359 $reg = array();
5360 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5361 $morecss .= ($morecss ? ' ' : '').$reg[1];
5362 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5363 }
5364 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5365 $morestyle = $reg[1];
5366 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5367 }
5368 $moreatt = trim($moreatt);
5369
5370 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5371 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5372 $enabledisablehtml .= '</span>';
5373
5374 return $enabledisablehtml;
5375 }
5376
5377 if (empty($srconly) && in_array($pictowithouttext, getImgPictoNameList())) {
5378 $fakey = $pictowithouttext;
5379 $facolor = '';
5380 $fasize = '';
5381 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5382 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'))) {
5383 $fa = 'far';
5384 }
5385 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'))) {
5386 $fa = 'fab';
5387 }
5388
5389 $arrayconvpictotofa = getImgPictoConv('fa');
5390
5391 if ($pictowithouttext == 'off') {
5392 $fakey = 'fa-square';
5393 $fasize = '1.3em';
5394 } elseif ($pictowithouttext == 'on') {
5395 $fakey = 'fa-check-square';
5396 $fasize = '1.3em';
5397 } elseif ($pictowithouttext == 'listlight') {
5398 $fakey = 'fa-download';
5399 $marginleftonlyshort = 1;
5400 } elseif ($pictowithouttext == 'printer') {
5401 $fakey = 'fa-print';
5402 $fasize = '1.2em';
5403 } elseif ($pictowithouttext == 'note') {
5404 $fakey = 'fa-sticky-note';
5405 $marginleftonlyshort = 1;
5406 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5407 $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');
5408 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5409 if (preg_match('/selected/', $pictowithouttext)) {
5410 $facolor = '#888';
5411 }
5412 $marginleftonlyshort = 1;
5413 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5414 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5415 } else {
5416 $fakey = 'fa-'.$pictowithouttext;
5417 }
5418
5419 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5420 $morecss .= ' em092';
5421 }
5422 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'info_black', 'project', 'workstation'))) {
5423 $morecss .= ' em088';
5424 }
5425 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5426 $morecss .= ' em080';
5427 }
5428
5429 // Define $marginleftonlyshort
5430 $arrayconvpictotomarginleftonly = array(
5431 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5432 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5433 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5434 );
5435 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5436 $marginleftonlyshort = 0;
5437 }
5438
5439 // Add CSS
5440 $arrayconvpictotomorcess = array(
5441 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5442 'bank_account' => 'infobox-bank_account',
5443 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5444 'bookcal' => 'infobox-action',
5445 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5446 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5447 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5448 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5449 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5450 'incoterm' => 'infobox-supplier_proposal',
5451 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5452 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5453 'order' => 'infobox-commande',
5454 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5455 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5456 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'info_black' => 'font-status1', 'invoice' => 'infobox-commande',
5457 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5458 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5459 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5460 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5461 'resource' => 'infobox-action',
5462 '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',
5463 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5464 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5465 'vat' => 'infobox-bank_account',
5466 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5467 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode', 'calendarpertype' => 'imgforviewmode'
5468 );
5469 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5470 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5471 }
5472
5473 // Define $color
5474 $arrayconvpictotocolor = array(
5475 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5476 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5477 'dynamicprice' => '#a69944',
5478 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5479 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5480 'lock' => '#ddd', 'lot' => '#a69944',
5481 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5482 'other' => '#ddd', 'world' => '#986c6a',
5483 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5484 //'shipment'=>'#a69944',
5485 'search-plus' => '#808080', 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5486 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5487 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5488 'website' => '#304', 'workstation' => '#a69944'
5489 );
5490 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5491 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5492 }
5493
5494 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5495 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5496 $morestyle = '';
5497 $reg = array();
5498 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5499 $morecss .= ($morecss ? ' ' : '').$reg[1];
5500 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5501 }
5502 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5503 $morestyle = $reg[1];
5504 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5505 }
5506 $moreatt = trim($moreatt);
5507
5508 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5509 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5510 $enabledisablehtml .= '</span>';
5511
5512 return $enabledisablehtml;
5513 }
5514
5515 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5516 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5517 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5518 $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
5519 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5520 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5521 }
5522
5523 // If we ask an image into $url/$mymodule/img (instead of default path)
5524 $regs = array();
5525 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5526 $picto = $regs[1];
5527 $path = $regs[2]; // $path is $mymodule
5528 }
5529
5530 // Clean parameters
5531 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5532 $picto .= '.png';
5533 }
5534 // If alt path are defined, define url where img file is, according to physical path
5535 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5536 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5537 if ($type == 'main') {
5538 continue;
5539 }
5540 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5541 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5542 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5543 break;
5544 }
5545 }
5546
5547 // $url is '' or '/custom', $path is current theme or
5548 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5549 }
5550
5551 if ($srconly) {
5552 return $fullpathpicto;
5553 }
5554
5555 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5556 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
5557}
5558
5564function getImgPictoNameList()
5565{
5566 return array(
5567 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5568 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5569 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5570 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5571 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5572 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5573 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-up',
5574 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5575 'commercial', 'companies',
5576 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5577 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5578 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5579 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5580 'hands-helping', 'help', 'holiday',
5581 'id-card', 'images', 'incoterm', 'info', 'info_black', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5582 'key', 'knowledgemanagement',
5583 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5584 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5585 'off', 'on', 'order',
5586 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5587 'resize', 'search', 'service', 'stats', 'stock',
5588 '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',
5589 'discord', 'facebook', 'flickr', 'instagram','linkedin', 'github', 'google', 'jabber', 'meetup', 'microsoft', 'skype', 'slack', 'twitter', 'pinterest', 'reddit', 'snapchat', 'tumblr', 'youtube', 'viadeo', 'google-plus-g', 'whatsapp',
5590 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5591 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5592 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5593 'technic', 'ticket',
5594 'error', 'warning',
5595 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring', 'rss',
5596 'search-plus', 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'store', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5597 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5598 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'upload', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5599 'conferenceorbooth', 'eventorganization',
5600 'stamp', 'signature',
5601 'webportal'
5602 );
5603}
5604
5612function getImgPictoConv($mode = 'fa')
5613{
5614 global $conf;
5615
5616 // Array when the fa picto key is different than the Dolibarr picto key.
5617 $arrayconvpictotofa = array(
5618 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5619 'asset' => 'money-check-alt', 'autofill' => 'fill',
5620 'back' => 'arrow-left', 'bank_account' => 'university',
5621 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5622 'bookcal' => 'calendar-check',
5623 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5624 'bom' => 'shapes',
5625 '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',
5626 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5627 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5628 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5629 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5630 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5631 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5632 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5633 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5634 'generic' => 'file', 'holiday' => 'umbrella-beach',
5635 'info' => 'info-circle', 'info_black' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5636 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5637 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5638 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5639 'sign-out' => 'sign-out-alt',
5640 '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',
5641 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5642 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table', 'calendarpertype' => 'table',
5643 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5644 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5645 'other' => 'square',
5646 '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',
5647 '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',
5648 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5649 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5650 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5651 'service' => 'concierge-bell',
5652 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5653 'status' => 'stop-circle',
5654 'stripe' => 'stripe-s', 'supplier' => 'building',
5655 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5656 'title_agenda' => 'calendar-alt',
5657 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5658 'jabber' => 'comment',
5659 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5660 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5661 'webportal' => 'door-open'
5662 );
5663
5664 if ($conf->currency == 'EUR') {
5665 $arrayconvpictotofa['currency'] = 'euro-sign';
5666 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5667 } else {
5668 $arrayconvpictotofa['currency'] = 'dollar-sign';
5669 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5670 }
5671
5672 return $arrayconvpictotofa;
5673}
5674
5675
5690function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
5691{
5692 if (strpos($picto, '^') === 0) {
5693 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5694 } else {
5695 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5696 }
5697}
5698
5710function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5711{
5712 global $conf;
5713
5714 if (is_numeric($picto)) {
5715 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5716 //$picto = $leveltopicto[$picto];
5717 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5718 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5719 $picto .= '.png';
5720 }
5721
5722 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5723
5724 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5725}
5726
5738function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5739{
5740 global $conf;
5741
5742 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5743 $picto .= '.png';
5744 }
5745
5746 if ($pictoisfullpath) {
5747 $path = $picto;
5748 } else {
5749 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5750
5751 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5752 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5753
5754 if (file_exists($themepath)) {
5755 $path = $themepath;
5756 }
5757 }
5758 }
5759
5760 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5761}
5762
5776function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5777{
5778 global $langs;
5779
5780 if (empty($titlealt) || $titlealt == 'default') {
5781 if ($numaction == '-1' || $numaction == 'ST_NO') {
5782 $numaction = -1;
5783 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5784 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5785 $numaction = 0;
5786 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5787 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5788 $numaction = 1;
5789 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5790 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5791 $numaction = 2;
5792 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5793 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5794 $numaction = 3;
5795 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5796 } else {
5797 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5798 $numaction = 0;
5799 }
5800 }
5801 if (!is_numeric($numaction)) {
5802 $numaction = 0;
5803 }
5804
5805 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5806}
5807
5815function img_pdf($titlealt = 'default', $size = 3)
5816{
5817 global $langs;
5818
5819 if ($titlealt == 'default') {
5820 $titlealt = $langs->trans('Show');
5821 }
5822
5823 return img_picto($titlealt, 'pdf'.$size.'.png');
5824}
5825
5833function img_edit_add($titlealt = 'default', $other = '')
5834{
5835 global $langs;
5836
5837 if ($titlealt == 'default') {
5838 $titlealt = $langs->trans('Add');
5839 }
5840
5841 return img_picto($titlealt, 'edit_add.png', $other);
5842}
5850function img_edit_remove($titlealt = 'default', $other = '')
5851{
5852 global $langs;
5853
5854 if ($titlealt == 'default') {
5855 $titlealt = $langs->trans('Remove');
5856 }
5857
5858 return img_picto($titlealt, 'edit_remove.png', $other);
5859}
5860
5869function img_edit($titlealt = 'default', $float = 0, $other = '')
5870{
5871 global $langs;
5872
5873 if ($titlealt == 'default') {
5874 $titlealt = $langs->trans('Modify');
5875 }
5876
5877 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5878}
5879
5888function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5889{
5890 global $langs;
5891
5892 if ($titlealt == 'default') {
5893 $titlealt = $langs->trans('View');
5894 }
5895
5896 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5897
5898 return img_picto($titlealt, 'eye', $moreatt);
5899}
5900
5909function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5910{
5911 global $langs;
5912
5913 if ($titlealt == 'default') {
5914 $titlealt = $langs->trans('Delete');
5915 }
5916
5917 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5918}
5919
5927function img_printer($titlealt = "default", $other = '')
5928{
5929 global $langs;
5930 if ($titlealt == "default") {
5931 $titlealt = $langs->trans("Print");
5932 }
5933 return img_picto($titlealt, 'printer.png', $other);
5934}
5935
5943function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5944{
5945 global $langs;
5946
5947 if ($titlealt == 'default') {
5948 $titlealt = $langs->trans('Split');
5949 }
5950
5951 return img_picto($titlealt, 'split.png', $other);
5952}
5953
5961function img_help($usehelpcursor = 1, $usealttitle = 1)
5962{
5963 global $langs;
5964
5965 if ($usealttitle) {
5966 if (is_string($usealttitle)) {
5967 $usealttitle = dol_escape_htmltag($usealttitle);
5968 } else {
5969 $usealttitle = $langs->trans('Info');
5970 }
5971 }
5972
5973 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5974}
5975
5982function img_info($titlealt = 'default')
5983{
5984 global $langs;
5985
5986 if ($titlealt == 'default') {
5987 $titlealt = $langs->trans('Informations');
5988 }
5989
5990 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5991}
5992
6001function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
6002{
6003 global $langs;
6004
6005 if ($titlealt == 'default') {
6006 $titlealt = $langs->trans('Warning');
6007 }
6008
6009 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
6010 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
6011}
6012
6019function img_error($titlealt = 'default')
6020{
6021 global $langs;
6022
6023 if ($titlealt == 'default') {
6024 $titlealt = $langs->trans('Error');
6025 }
6026
6027 return img_picto($titlealt, 'error.png');
6028}
6029
6037function img_next($titlealt = 'default', $moreatt = '')
6038{
6039 global $langs;
6040
6041 if ($titlealt == 'default') {
6042 $titlealt = $langs->trans('Next');
6043 }
6044
6045 //return img_picto($titlealt, 'next.png', $moreatt);
6046 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
6047}
6048
6056function img_previous($titlealt = 'default', $moreatt = '')
6057{
6058 global $langs;
6059
6060 if ($titlealt == 'default') {
6061 $titlealt = $langs->trans('Previous');
6062 }
6063
6064 //return img_picto($titlealt, 'previous.png', $moreatt);
6065 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
6066}
6067
6076function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
6077{
6078 global $langs;
6079
6080 if ($titlealt == 'default') {
6081 $titlealt = $langs->trans('Down');
6082 }
6083
6084 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
6085}
6086
6095function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
6096{
6097 global $langs;
6098
6099 if ($titlealt == 'default') {
6100 $titlealt = $langs->trans('Up');
6101 }
6102
6103 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
6104}
6105
6114function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
6115{
6116 global $langs;
6117
6118 if ($titlealt == 'default') {
6119 $titlealt = $langs->trans('Left');
6120 }
6121
6122 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
6123}
6124
6133function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
6134{
6135 global $langs;
6136
6137 if ($titlealt == 'default') {
6138 $titlealt = $langs->trans('Right');
6139 }
6140
6141 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
6142}
6143
6151function img_allow($allow, $titlealt = 'default')
6152{
6153 global $langs;
6154
6155 if ($titlealt == 'default') {
6156 $titlealt = $langs->trans('Active');
6157 }
6158
6159 if ($allow == 1) {
6160 return img_picto($titlealt, 'tick.png');
6161 }
6162
6163 return '-';
6164}
6165
6173function img_credit_card($brand, $morecss = null)
6174{
6175 if (is_null($morecss)) {
6176 $morecss = 'fa-2x';
6177 }
6178
6179 if ($brand == 'visa' || $brand == 'Visa') {
6180 $brand = 'cc-visa';
6181 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6182 $brand = 'cc-mastercard';
6183 } elseif ($brand == 'amex' || $brand == 'American Express') {
6184 $brand = 'cc-amex';
6185 } elseif ($brand == 'discover' || $brand == 'Discover') {
6186 $brand = 'cc-discover';
6187 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6188 $brand = 'cc-jcb';
6189 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6190 $brand = 'cc-diners-club';
6191 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6192 $brand = 'credit-card';
6193 }
6194
6195 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
6196}
6197
6206function img_mime($file, $titlealt = '', $morecss = '')
6207{
6208 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
6209
6210 $mimetype = dol_mimetype($file, '', 1);
6211 //$mimeimg = dol_mimetype($file, '', 2);
6212 $mimefa = dol_mimetype($file, '', 4);
6213
6214 if (empty($titlealt)) {
6215 $titlealt = 'Mime type: '.$mimetype;
6216 }
6217
6218 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6219 return '<i class="fa fa-'.$mimefa.' '.(preg_match('/pictofixedwidth/', $morecss) ? '' : 'paddingright ').($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.dolPrintHTMLForAttribute($titlealt).'"' : '').'></i>';
6220}
6221
6222
6230function img_search($titlealt = 'default', $other = '')
6231{
6232 global $langs;
6233
6234 if ($titlealt == 'default') {
6235 $titlealt = $langs->trans('Search');
6236 }
6237
6238 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
6239
6240 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
6241 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6242
6243 return $input;
6244}
6245
6253function img_searchclear($titlealt = 'default', $other = '')
6254{
6255 global $langs;
6256
6257 if ($titlealt == 'default') {
6258 $titlealt = $langs->trans('Search');
6259 }
6260
6261 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6262
6263 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
6264 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6265
6266 return $input;
6267}
6268
6281function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6282{
6283 global $conf, $langs;
6284
6285 if ($infoonimgalt) {
6286 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
6287 } else {
6288 if (empty($conf->use_javascript_ajax)) {
6289 $textfordropdown = '';
6290 }
6291
6292 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
6293 $fa = 'info-circle';
6294 if ($picto == 'warning') {
6295 $fa = 'exclamation-triangle';
6296 }
6297 $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> ';
6298 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6299 $result .= ($nodiv ? '' : '</div>');
6300
6301 if ($textfordropdown) {
6302 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
6303 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
6304 jQuery(document).ready(function() {
6305 jQuery(".'.$class.'text").click(function() {
6306 console.log("toggle text");
6307 jQuery(".'.$class.'").toggle();
6308 });
6309 });
6310 </script>';
6311
6312 $result = $tmpresult.$result;
6313 }
6314 }
6315
6316 return $result;
6317}
6318
6319
6331function dol_print_error($db = null, $error = '', $errors = null)
6332{
6333 global $conf, $langs, $user, $argv;
6334 global $dolibarr_main_prod;
6335
6336 $out = '';
6337 $syslog = '';
6338
6339 // If error occurs before the $lang object was loaded
6340 if (!$langs) {
6341 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6342 $langs = new Translate('', $conf);
6343 $langs->load("main");
6344 }
6345
6346 // Load translation files required by the error messages
6347 $langs->loadLangs(array('main', 'errors'));
6348
6349 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6350 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6351 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6352 $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";
6353 }
6354 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6355
6356 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6357 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6358 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6359 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6360 }
6361 if ($user instanceof User) {
6362 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6363 }
6364 if (function_exists("phpversion")) {
6365 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6366 }
6367 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6368 if (function_exists("php_uname")) {
6369 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6370 }
6371 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6372 $out .= "<br>\n";
6373 $out .= "<b>" . $langs->trans("RequestedUrl") . ":</b> " . (isset($_SERVER["REQUEST_URI"]) ? dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT) : '') . "<br>\n";
6374 $out .= "<b>" . $langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '') . "<br>\n";
6375 $out .= "<b>" . $langs->trans("MenuManager") . ":</b> " . (isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '') . "<br>\n";
6376 $out .= "<br>\n";
6377 $syslog .= "url=" . (isset($_SERVER["REQUEST_URI"]) ? dol_escape_htmltag($_SERVER["REQUEST_URI"]) : '');
6378 $syslog .= ", query_string=" . (isset($_SERVER["QUERY_STRING"]) ? dol_escape_htmltag($_SERVER["QUERY_STRING"]) : '');
6379 } else { // Mode CLI
6380 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6381 $syslog .= "pid=".dol_getmypid();
6382 }
6383
6384 if (!empty($conf->modules)) {
6385 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6386 }
6387
6388 if (is_object($db)) {
6389 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6390 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6391 $lastqueryerror = $db->lastqueryerror();
6392 if (!utf8_check($lastqueryerror)) {
6393 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6394 }
6395 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6396 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6397 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6398 $out .= "<br>\n";
6399 } else { // Mode CLI
6400 // No dol_escape_htmltag for output, we are in CLI mode
6401 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6402 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6403 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6404 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6405 }
6406 $syslog .= ", sql=".$db->lastquery();
6407 $syslog .= ", db_error=".$db->lasterror();
6408 }
6409
6410 if ($error || $errors) {
6411 // Merge all into $errors array
6412 if (is_array($error) && is_array($errors)) {
6413 $errors = array_merge($error, $errors);
6414 } elseif (is_array($error)) { // deprecated, use second parameters
6415 $errors = $error;
6416 } elseif (is_array($errors) && !empty($error)) {
6417 $errors = array_merge(array($error), $errors);
6418 } elseif (!empty($error)) {
6419 $errors = array_merge(array($error), array($errors));
6420 }
6421
6422 $langs->load("errors");
6423
6424 foreach ($errors as $msg) {
6425 if (empty($msg)) {
6426 continue;
6427 }
6428 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6429 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6430 } else { // Mode CLI
6431 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6432 }
6433 $syslog .= ", msg=".$msg;
6434 }
6435 }
6436 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6437 xdebug_print_function_stack();
6438 $out .= '<b>XDebug information:</b>'."<br>\n";
6439 $out .= 'File: '.xdebug_call_file()."<br>\n";
6440 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6441 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6442 $out .= "<br>\n";
6443 }
6444
6445 // Return a http header with error code if possible
6446 if (!headers_sent()) {
6447 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6448 top_httphead();
6449 }
6450 //http_response_code(500); // If we use 500, message is not output with some command line tools
6451 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
6452 }
6453
6454 if (empty($dolibarr_main_prod)) {
6455 print $out;
6456 } else {
6457 if (empty($langs->defaultlang)) {
6458 $langs->setDefaultLang();
6459 }
6460 $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.
6461 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6462 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";
6463 print $langs->trans("DolibarrHasDetectedError").'. ';
6464 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6465 if (!defined("MAIN_CORE_ERROR")) {
6466 define("MAIN_CORE_ERROR", 1);
6467 }
6468 }
6469
6470 dol_syslog("Error ".$syslog, LOG_ERR);
6471}
6472
6483function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6484{
6485 global $langs;
6486
6487 if (empty($email)) {
6488 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6489 }
6490
6491 $langs->load("errors");
6492 $now = dol_now();
6493
6494 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6495 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6496 if ($errormessage) {
6497 print '<br><br>'.$errormessage;
6498 }
6499 if (is_array($errormessages) && count($errormessages)) {
6500 foreach ($errormessages as $mesgtoshow) {
6501 print '<br><br>'.$mesgtoshow;
6502 }
6503 }
6504 print '</div></div>';
6505}
6506
6523function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $param = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6524{
6525 print getTitleFieldOfList($name, 0, $file, $field, $begin, $param, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6526}
6527
6546function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6547{
6548 global $langs, $form;
6549 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6550
6551 if ($moreattrib == 'class="right"') {
6552 $prefix .= 'right '; // For backward compatibility
6553 }
6554
6555 $sortorder = strtoupper((string) $sortorder);
6556 $out = '';
6557 $sortimg = '';
6558
6559 $tag = 'th';
6560 if ($thead == 2) {
6561 $tag = 'div';
6562 }
6563
6564 $tmpsortfield = explode(',', (string) $sortfield);
6565 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6566 $tmpfield = explode(',', $field);
6567 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6568
6569 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6570 $prefix = 'wrapcolumntitle '.$prefix;
6571 }
6572
6573 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6574 // If field is used as sort criteria we use a specific css class liste_titre_sel
6575 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6576 $liste_titre = 'liste_titre';
6577 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6578 $liste_titre = 'liste_titre_sel';
6579 }
6580
6581 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6582 //$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)).'"' : '');
6583 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dolPrintHTMLForAttribute($langs->trans($name)).'"' : '';
6584 $tagstart .= '>';
6585
6586 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6587 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6588 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6589 $options = preg_replace('/&+/i', '&', $options);
6590 if (!preg_match('/^&/', $options)) {
6591 $options = '&'.$options;
6592 }
6593
6594 $sortordertouseinlink = '';
6595 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6596 if (preg_match('/^DESC/i', $sortorder)) {
6597 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6598 } else { // We reverse the var $sortordertouseinlink
6599 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6600 }
6601 } else { // We are on field that is the first current sorting criteria
6602 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6603 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6604 } else {
6605 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6606 }
6607 }
6608 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6609 $out .= '<a class="reposition" href="'.$file.'?sortfield='.urlencode($field).'&sortorder='.urlencode($sortordertouseinlink).'&begin='.urlencode($begin).$options.'"';
6610 //$out .= (getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') ? '' : ' title="'.dol_escape_htmltag($langs->trans($name)).'"');
6611 $out .= '>';
6612 }
6613 if ($tooltip) {
6614 // You can also use 'TranslationString:keyfortooltiponclick:tooltipdirection' for a tooltip on click or to change tooltip position.
6615 if (strpos($tooltip, ':') !== false) {
6616 $tmptooltip = explode(':', $tooltip);
6617 } else {
6618 $tmptooltip = array($tooltip);
6619 }
6620 $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]));
6621 } else {
6622 $out .= $langs->trans((string) $name);
6623 }
6624
6625 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6626 $out .= '</a>';
6627 }
6628
6629 if (empty($thead) && $field) { // If this is a sort field
6630 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6631 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6632 $options = preg_replace('/&+/i', '&', $options);
6633 if (!preg_match('/^&/', $options)) {
6634 $options = '&'.$options;
6635 }
6636
6637 if (!$sortorder || ($field1 != $sortfield1)) {
6638 // Nothing
6639 } else {
6640 if (preg_match('/^DESC/', $sortorder)) {
6641 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6642 }
6643 if (preg_match('/^ASC/', $sortorder)) {
6644 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6645 }
6646 }
6647 }
6648
6649 $tagend = '</'.$tag.'>';
6650
6651 $out = $tagstart.$sortimg.$out.$tagend;
6652
6653 return $out;
6654}
6655
6664function print_titre($title)
6665{
6666 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6667
6668 print '<div class="titre">'.$title.'</div>';
6669}
6670
6682function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6683{
6684 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6685}
6686
6700function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6701{
6702 $return = '';
6703
6704 if ($picto == 'setup') {
6705 $picto = 'generic';
6706 }
6707
6708 $return .= "\n";
6709 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6710 $return .= '<tr class="toptitle">';
6711 if ($picto) {
6712 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6713 }
6714 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6715 $return .= '<div class="titre inline-block">';
6716 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6717 $return .= '</div>';
6718 $return .= '</td>';
6719 if (dol_strlen($morehtmlcenter)) {
6720 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6721 }
6722 if (dol_strlen($morehtmlright)) {
6723 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6724 }
6725 $return .= '</tr></table>'."\n";
6726
6727 return $return;
6728}
6729
6753function 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 = '')
6754{
6755 global $conf, $langs;
6756
6757 $savlimit = $limit;
6758 $savtotalnboflines = $totalnboflines;
6759 if (is_numeric($totalnboflines)) {
6760 $totalnboflines = abs($totalnboflines);
6761 }
6762
6763 // Detect if there is a subtitle
6764 $subtitle = '';
6765 $tmparray = preg_split('/<br>/i', $title, 2);
6766 if (!empty($tmparray[1])) {
6767 $title = $tmparray[0];
6768 $subtitle = $tmparray[1];
6769 }
6770
6771 $page = (int) $page;
6772
6773 if ($picto == 'setup') {
6774 $picto = 'title_setup.png';
6775 }
6776 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6777 $picto = 'title.gif';
6778 }
6779 if ($limit < 0) {
6780 $limit = $conf->liste_limit;
6781 }
6782
6783 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6784 $nextpage = 1;
6785 } else {
6786 $nextpage = 0;
6787 }
6788 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6789
6790 print "\n";
6791 print "<!-- Begin print_barre_liste -->\n";
6792 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6793 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6794
6795 // Left
6796
6797 if ($picto && $title) {
6798 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6799 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6800 print '</td>';
6801 }
6802
6803 print '<td class="nobordernopadding valignmiddle col-title">';
6804 print '<div class="titre inline-block">';
6805 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()
6806 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6807 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6808 }
6809 print '</div>';
6810 if (!empty($subtitle)) {
6811 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6812 }
6813 print '</td>';
6814
6815 // Center
6816 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6817 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6818 }
6819
6820 // Right
6821 print '<td class="nobordernopadding valignmiddle right col-right">';
6822 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6823 if ($sortfield) {
6824 $options .= "&sortfield=".urlencode($sortfield);
6825 }
6826 if ($sortorder) {
6827 $options .= "&sortorder=".urlencode($sortorder);
6828 }
6829 // Show navigation bar
6830 $pagelist = '';
6831 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6832 if ($totalnboflines) { // If we know total nb of lines
6833 // Define nb of extra page links before and after selected page + ... + first or last
6834 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6835
6836 if ($limit > 0) {
6837 $nbpages = ceil($totalnboflines / $limit);
6838 } else {
6839 $nbpages = 1;
6840 }
6841 $cpt = ($page - $maxnbofpage);
6842 if ($cpt < 0) {
6843 $cpt = 0;
6844 }
6845
6846 if ($cpt >= 1) {
6847 if (empty($pagenavastextinput)) {
6848 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6849 if ($cpt > 2) {
6850 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6851 } elseif ($cpt == 2) {
6852 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6853 }
6854 }
6855 }
6856
6857 do {
6858 if ($pagenavastextinput) {
6859 if ($cpt == $page) {
6860 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone heightofcombo" name="pageplusone" value="'.($page + 1).'"></li>';
6861 $pagelist .= '/';
6862 }
6863 } else {
6864 if ($cpt == $page) {
6865 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6866 } else {
6867 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6868 }
6869 }
6870 $cpt++;
6871 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6872
6873 if (empty($pagenavastextinput)) {
6874 if ($cpt < $nbpages) {
6875 if ($cpt < $nbpages - 2) {
6876 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6877 } elseif ($cpt == $nbpages - 2) {
6878 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6879 }
6880 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6881 }
6882 } else {
6883 //var_dump($page.' '.$cpt.' '.$nbpages);
6884 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6885 }
6886 } else {
6887 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6888 }
6889 }
6890
6891 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6892 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
6893 }
6894
6895 // js to autoselect page field on focus
6896 if ($pagenavastextinput) {
6897 print ajax_autoselect('.pageplusone');
6898 }
6899
6900 print '</td>';
6901 print '</tr>';
6902
6903 print '</table>'."\n";
6904
6905 // Center
6906 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6907 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6908 }
6909
6910 print "<!-- End title -->\n\n";
6911}
6912
6929function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6930{
6931 global $conf, $langs;
6932
6933 print '<div class="pagination"><ul>';
6934 if ($beforearrows) {
6935 print '<li class="paginationbeforearrows">';
6936 print $beforearrows;
6937 print '</li>';
6938 }
6939
6940 if (empty($hidenavigation)) {
6941 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6942 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6943 $pagesizechoices .= ',5000:5000';
6944 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6945 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6946 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6947 //$pagesizechoices .= ',2:2';
6948 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6949 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6950 }
6951
6952 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6953 print '<li class="pagination">';
6954 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.'">';
6955 print '<datalist id="limitlist">';
6956 } else {
6957 print '<li class="paginationcombolimit valignmiddle">';
6958 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")).'">';
6959 }
6960 $tmpchoice = explode(',', $pagesizechoices);
6961 $tmpkey = $limit.':'.$limit;
6962 if (!in_array($tmpkey, $tmpchoice)) {
6963 $tmpchoice[$tmpkey] = $tmpkey;
6964 }
6965 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6966 if (!in_array($tmpkey, $tmpchoice)) {
6967 $tmpchoice[$tmpkey] = $tmpkey;
6968 }
6969 asort($tmpchoice, SORT_NUMERIC);
6970 foreach ($tmpchoice as $val) {
6971 $selected = '';
6972 $tmp = explode(':', $val);
6973 $key = $tmp[0];
6974 $val = $tmp[1];
6975 if ($key != '' && $val != '') {
6976 if ((int) $key == (int) $limit) {
6977 $selected = ' selected="selected"';
6978 }
6979 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6980 }
6981 }
6982 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6983 print '</datalist>';
6984 } else {
6985 print '</select>';
6986 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
6987 //print ajax_combobox("limit");
6988 }
6989
6990 if ($conf->use_javascript_ajax) {
6991 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6992 <script>
6993 jQuery(document).ready(function () {
6994 jQuery(".selectlimit").change(function() {
6995 console.log("We change limit so we submit the form");
6996 $(this).parents(\'form:first\').submit();
6997 });
6998 });
6999 </script>
7000 ';
7001 }
7002 print '</li>';
7003 }
7004 if ($page > 0) {
7005 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>';
7006 }
7007 if ($betweenarrows) {
7008 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
7009 print $betweenarrows;
7010 print '<!--</div>-->';
7011 }
7012 if ($nextpage > 0) {
7013 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>';
7014 }
7015 if ($afterarrows) {
7016 print '<li class="paginationafterarrows">';
7017 print $afterarrows;
7018 print '</li>';
7019 }
7020 }
7021 print '</ul></div>'."\n";
7022}
7023
7024
7036function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
7037{
7038 $morelabel = '';
7039
7040 if (preg_match('/%/', $rate)) {
7041 $rate = str_replace('%', '', $rate);
7042 $addpercent = true;
7043 }
7044 $reg = array();
7045 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
7046 $morelabel = ' ('.$reg[1].')';
7047 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
7048 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
7049 }
7050 if (preg_match('/\*/', $rate)) {
7051 $rate = str_replace('*', '', $rate);
7052 $info_bits |= 1;
7053 }
7054
7055 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
7056 if (!preg_match('/\//', $rate)) {
7057 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
7058 } else {
7059 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
7060 $ret = $rate.($addpercent ? '%' : '');
7061 }
7062 if (($info_bits & 1) && $usestarfornpr >= 0) {
7063 $ret .= ' *';
7064 }
7065 $ret .= $morelabel;
7066 return $ret;
7067}
7068
7069
7085function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
7086{
7087 global $langs, $conf;
7088
7089 // Clean parameters
7090 if (empty($amount)) {
7091 $amount = 0; // To have a numeric value if amount not defined or = ''
7092 }
7093 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
7094 if ($rounding == -1) {
7095 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
7096 }
7097 $nbdecimal = $rounding;
7098
7099 if ($outlangs === 'none') {
7100 // Use international separators
7101 $dec = '.';
7102 $thousand = '';
7103 } else {
7104 // Output separators by default (french)
7105 $dec = ',';
7106 $thousand = ' ';
7107
7108 // If $outlangs not forced, we use use language
7109 if (!($outlangs instanceof Translate)) {
7110 $outlangs = $langs;
7111 }
7112
7113 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7114 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
7115 }
7116 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7117 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
7118 }
7119 if ($thousand == 'None') {
7120 $thousand = '';
7121 } elseif ($thousand == 'Space') {
7122 $thousand = ' ';
7123 }
7124 }
7125 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7126
7127 //print "amount=".$amount."-";
7128 $amount = str_replace(',', '.', $amount); // should be useless
7129 //print $amount."-";
7130 $data = explode('.', $amount);
7131 $decpart = isset($data[1]) ? $data[1] : '';
7132 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
7133 //print "decpart=".$decpart."<br>";
7134 $end = '';
7135
7136 // We increase nbdecimal if there is more decimal than asked (to not loose information)
7137 if (dol_strlen($decpart) > $nbdecimal) {
7138 $nbdecimal = dol_strlen($decpart);
7139 }
7140
7141 // If nbdecimal is higher than max to show
7142 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
7143 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
7144 $nbdecimal = $nbdecimalmaxshown;
7145 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
7146 // If output is truncated, we show ...
7147 $end = '...';
7148 }
7149 }
7150
7151 // If force rounding
7152 if ((string) $forcerounding != '-1' && (string) $forcerounding != '') {
7153 if ($forcerounding === 'MU') {
7154 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7155 } elseif ($forcerounding === 'MT') {
7156 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7157 } elseif ($forcerounding >= 0) {
7158 $nbdecimal = (int) $forcerounding;
7159 }
7160 }
7161
7162 // Format number
7163 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7164 // Add symbol of currency if requested
7165 $cursymbolbefore = $cursymbolafter = '';
7166 if ($currency_code && is_object($outlangs)) {
7167 if ($currency_code == 'auto') {
7168 $currency_code = $conf->currency;
7169 }
7170
7171 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7172 $listoflanguagesbefore = array('nl_NL');
7173 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7174 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7175 } else {
7176 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7177 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
7178 }
7179 }
7180 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
7181 if ($form) {
7182 $output = preg_replace('/\s/', '&nbsp;', $output);
7183 $output = preg_replace('/\'/', '&#039;', $output);
7184 }
7185
7186 return $output;
7187}
7188
7213function price2num($amount, $rounding = '', $option = 0)
7214{
7215 global $langs, $conf;
7216
7217 // Clean parameters
7218 if (is_null($amount)) {
7219 $amount = '';
7220 }
7221
7222 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7223 // Numbers must be '1234.56'
7224 // Decimal delimiter for PHP and database SQL requests must be '.'
7225 $dec = ',';
7226 $thousand = ' ';
7227 if (is_null($langs)) { // $langs is not defined, we use english values.
7228 $dec = '.';
7229 $thousand = ',';
7230 } else {
7231 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7232 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7233 }
7234 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7235 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7236 }
7237 }
7238 if ($thousand == 'None') {
7239 $thousand = '';
7240 } elseif ($thousand == 'Space') {
7241 $thousand = ' ';
7242 }
7243 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7244
7245 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7246 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7247 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7248 if (!is_numeric($amount)) {
7249 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7250 }
7251
7252 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
7253 $amount = str_replace($thousand, '', $amount);
7254 }
7255
7256 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7257 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7258 // So if number was already a good number, it is converted into local Dolibarr setup.
7259 if (is_numeric($amount)) {
7260 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7261 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7262 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7263 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7264 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7265 }
7266 //print "QQ".$amount."<br>\n";
7267
7268 // Now make replaceents (the main goal of function)
7269
7270 if ($thousand != ',' && $thousand != '.') {
7271 // Accept the two types of decimal points french users (i.e., using ' ' for thousands)
7272
7273 // REGEX: Find the integral and decimal parts.
7274 //
7275 // We require that the decimal point only appears once in $amount.
7276 // The regex `/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u` can be broken down as follows:
7277 // - `(?<int>[^,]*,|[^.]*\.)` is any accepted sequence up to the last potential decimal point '.' or ',' and named `int`.
7278 // It covers two cases:
7279 // - `[^,]*,`: Any sequence of characters that is not ',' with ',' accepted as the decimal point (from start of string because of earlier `^`);
7280 // - `[^.]*\.`: Any sequence of characters that is not a '.' with '.' accepted as the decimal point (from start of string.
7281 // - `(?<dec>[^.,]*)`: The sequence after the character accepted as the decimal point, not including it.
7282 $matches = array();
7283 if (preg_match('/^(?<int>[^,]*,|[^.]*\.)(?<dec>[^.,]*)$/u', $amount, $matches)) {
7284 $intPart = $matches['int'];
7285 $decPart = $matches['dec'];
7286
7287 // Remove all commas and dots from intPart
7288 $intPart = str_replace(['.', ','], '', $intPart);
7289
7290 // Combine intPart and decPart with a dot
7291 $amount = $intPart . $dec . $decPart;
7292 }
7293 }
7294
7295 $amount = str_replace(' ', '', $amount); // To avoid spaces
7296 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7297 $amount = str_replace($dec, '.', $amount);
7298
7299 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7300 }
7301 //print ' XX'.$amount.' '.$rounding;
7302
7303 // Now, $amount is a real PHP float number. We make a rounding if required.
7304 if ($rounding) {
7305 $nbofdectoround = '';
7306 if ($rounding == 'MU') {
7307 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT'); // usually 5
7308 } elseif ($rounding == 'MT') {
7309 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT'); // usually 2 or 3
7310 } elseif ($rounding == 'MS') {
7311 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? getDolGlobalInt('MAIN_MAX_DECIMALS_STOCK') : 5;
7312 } elseif ($rounding == 'CU') {
7313 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_UNIT', getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT')); // TODO Use param of currency
7314 } elseif ($rounding == 'CT') {
7315 $nbofdectoround = getDolGlobalInt('MAIN_MAX_DECIMALS_CURRENCY_TOT', getDolGlobalInt('MAIN_MAX_DECIMALS_TOT')); // TODO Use param of currency
7316 } elseif (is_numeric($rounding)) {
7317 $nbofdectoround = (int) $rounding;
7318 }
7319
7320 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7321 if (dol_strlen($nbofdectoround)) {
7322 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7323 } else {
7324 return 'ErrorBadParameterProvidedToFunction';
7325 }
7326 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7327
7328 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7329 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7330 if (is_numeric($amount)) {
7331 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7332 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7333 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7334 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7335 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7336 }
7337 //print "TT".$amount.'<br>';
7338
7339 // Always make replace because each math function (like round) replace
7340 // with local values and we want a number that has a SQL string format x.y
7341 if ($thousand != ',' && $thousand != '.') {
7342 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7343 }
7344
7345 $amount = str_replace(' ', '', $amount); // To avoid spaces
7346 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7347 $amount = str_replace($dec, '.', $amount);
7348
7349 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7350 }
7351
7352 return $amount;
7353}
7354
7367function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7368{
7369 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7370
7371 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7372 $dimension *= 1000000;
7373 $unit -= 6;
7374 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7375 $dimension *= 1000;
7376 $unit -= 3;
7377 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7378 $dimension /= 1000000;
7379 $unit += 6;
7380 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7381 $dimension /= 1000;
7382 $unit += 3;
7383 }
7384 // Special case when we want output unit into pound or ounce
7385 /* TODO
7386 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7387 {
7388 $dimension = // convert dimension from standard unit into ounce or pound
7389 $unit = $forceunitoutput;
7390 }
7391 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7392 {
7393 $dimension = // convert dimension from standard unit into ounce or pound
7394 $unit = $forceunitoutput;
7395 }*/
7396
7397 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7398 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7399 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
7400
7401 return $ret;
7402}
7403
7404
7417function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7418{
7419 global $db, $conf, $mysoc;
7420
7421 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7422 $thirdparty_seller = $mysoc;
7423 }
7424
7425 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);
7426
7427 $vatratecleaned = $vatrate;
7428 $reg = array();
7429 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7430 $vatratecleaned = trim($reg[1]);
7431 $vatratecode = $reg[2];
7432 }
7433
7434 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7435 {
7436 return 0;
7437 }*/
7438
7439 // Some test to guess with no need to make database access
7440 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7441 if ($local == 1) {
7442 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7443 return 0;
7444 }
7445 if ($thirdparty_seller->id == $mysoc->id) {
7446 if (!$thirdparty_buyer->localtax1_assuj) {
7447 return 0;
7448 }
7449 } else {
7450 if (!$thirdparty_seller->localtax1_assuj) {
7451 return 0;
7452 }
7453 }
7454 }
7455
7456 if ($local == 2) {
7457 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7458 if (!$mysoc->localtax2_assuj) {
7459 return 0; // If main vat is 0, IRPF may be different than 0.
7460 }
7461 if ($thirdparty_seller->id == $mysoc->id) {
7462 if (!$thirdparty_buyer->localtax2_assuj) {
7463 return 0;
7464 }
7465 } else {
7466 if (!$thirdparty_seller->localtax2_assuj) {
7467 return 0;
7468 }
7469 }
7470 }
7471 } else {
7472 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7473 return 0;
7474 }
7475 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7476 return 0;
7477 }
7478 }
7479
7480 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7481 if (in_array($mysoc->country_code, array('ES'))) {
7482 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7483 }
7484
7485 // Search local taxes
7486 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7487 if ($local == 1) {
7488 if ($thirdparty_seller != $mysoc) {
7489 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7490 return $thirdparty_seller->localtax1_value;
7491 }
7492 } else { // i am the seller
7493 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7494 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7495 }
7496 }
7497 }
7498 if ($local == 2) {
7499 if ($thirdparty_seller != $mysoc) {
7500 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7501 // TODO We should also return value defined on thirdparty only if defined
7502 return $thirdparty_seller->localtax2_value;
7503 }
7504 } else { // i am the seller
7505 if (in_array($mysoc->country_code, array('ES'))) {
7506 return $thirdparty_buyer->localtax2_value;
7507 } else {
7508 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7509 }
7510 }
7511 }
7512 }
7513
7514 // By default, search value of local tax on line of common tax
7515 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7516 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7517 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7518 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7519 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7520 if (!empty($vatratecode)) {
7521 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7522 } else {
7523 $sql .= " AND t.recuperableonly = '".$db->escape((string) $vatnpr)."'";
7524 }
7525
7526 $resql = $db->query($sql);
7527
7528 if ($resql) {
7529 $obj = $db->fetch_object($resql);
7530 if ($obj) {
7531 if ($local == 1) {
7532 return $obj->localtax1;
7533 } elseif ($local == 2) {
7534 return $obj->localtax2;
7535 }
7536 }
7537 }
7538
7539 return 0;
7540}
7541
7542
7551function isOnlyOneLocalTax($local)
7552{
7553 $tax = get_localtax_by_third($local);
7554
7555 $valors = explode(":", $tax);
7556
7557 if (count($valors) > 1) {
7558 return false;
7559 } else {
7560 return true;
7561 }
7562}
7563
7570function get_localtax_by_third($local)
7571{
7572 global $db, $mysoc;
7573
7574 $sql = " SELECT t.localtax".$local." as localtax";
7575 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7576 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7577 $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";
7578 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7579 $sql .= " AND t.localtax".$local."_type <> '0'";
7580 $sql .= " ORDER BY t.rowid DESC";
7581
7582 $resql = $db->query($sql);
7583 if ($resql) {
7584 $obj = $db->fetch_object($resql);
7585 if ($obj) {
7586 return $obj->localtax;
7587 } else {
7588 return '0';
7589 }
7590 }
7591
7592 return 'Error';
7593}
7594
7595
7607function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7608{
7609 global $db;
7610
7611 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7612
7613 // Search local taxes
7614 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7615 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7616 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7617 if ($firstparamisid) {
7618 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7619 } else {
7620 $vatratecleaned = $vatrate;
7621 $vatratecode = '';
7622 $reg = array();
7623 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7624 $vatratecleaned = $reg[1];
7625 $vatratecode = $reg[2];
7626 }
7627
7628 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7629 /*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 ??
7630 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7631 $sql .= " WHERE t.fk_pays = c.rowid";
7632 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7633 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7634 } else {
7635 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7636 }
7637 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7638 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7639 if ($vatratecode) {
7640 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7641 }
7642 }
7643
7644 $resql = $db->query($sql);
7645 if ($resql) {
7646 $obj = $db->fetch_object($resql);
7647 if ($obj) {
7648 return array(
7649 'rowid' => $obj->rowid,
7650 'code' => $obj->code,
7651 'rate' => $obj->rate,
7652 'localtax1' => $obj->localtax1,
7653 'localtax1_type' => $obj->localtax1_type,
7654 'localtax2' => $obj->localtax2,
7655 'localtax2_type' => $obj->localtax2_type,
7656 'npr' => $obj->npr,
7657 'accountancy_code_sell' => $obj->accountancy_code_sell,
7658 'accountancy_code_buy' => $obj->accountancy_code_buy
7659 );
7660 } else {
7661 return array();
7662 }
7663 } else {
7664 dol_print_error($db);
7665 }
7666
7667 return array();
7668}
7669
7686function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7687{
7688 global $db, $mysoc;
7689
7690 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7691
7692 // Search local taxes
7693 $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";
7694 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7695 if ($firstparamisid) {
7696 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7697 } else {
7698 $vatratecleaned = $vatrate;
7699 $vatratecode = '';
7700 $reg = array();
7701 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7702 $vatratecleaned = $reg[1];
7703 $vatratecode = $reg[2];
7704 }
7705
7706 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7707 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7708 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7709 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7710 } else {
7711 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7712 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7713 }
7714 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7715 if ($vatratecode) {
7716 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7717 }
7718 }
7719
7720 $resql = $db->query($sql);
7721 if ($resql) {
7722 $obj = $db->fetch_object($resql);
7723
7724 if ($obj) {
7725 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7726
7727 if ($local == 1) {
7728 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7729 } elseif ($local == 2) {
7730 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7731 } else {
7732 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);
7733 }
7734 }
7735 }
7736
7737 return array();
7738}
7739
7750function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7751{
7752 global $db, $mysoc;
7753
7754 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7755
7756 $ret = 0;
7757 $found = 0;
7758
7759 if ($idprod > 0) {
7760 // Load product
7761 $product = new Product($db);
7762 $product->fetch($idprod);
7763
7764 if (($mysoc->country_code == $thirdpartytouse->country_code)
7765 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7766 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7767 ) {
7768 // If country of thirdparty to consider is ours
7769 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7770 $result = $product->get_buyprice($idprodfournprice, 0, 0, '');
7771 if ($result > 0) {
7772 $ret = $product->vatrate_supplier;
7773 if ($product->default_vat_code_supplier) {
7774 $ret .= ' ('.$product->default_vat_code_supplier.')';
7775 }
7776 $found = 1;
7777 }
7778 }
7779 if (!$found) {
7780 $ret = $product->tva_tx; // Default sales vat of product
7781 if ($product->default_vat_code) {
7782 $ret .= ' ('.$product->default_vat_code.')';
7783 }
7784 $found = 1;
7785 }
7786 } else {
7787 // TODO Read default product vat according to product and an other countrycode.
7788 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7789 }
7790 }
7791
7792 if (!$found) {
7793 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7794 // 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).
7795 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7796 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7797 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7798 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7799 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7800 $sql .= $db->plimit(1);
7801
7802 $resql = $db->query($sql);
7803 if ($resql) {
7804 $obj = $db->fetch_object($resql);
7805 if ($obj) {
7806 $ret = $obj->vat_rate;
7807 if ($obj->default_vat_code) {
7808 $ret .= ' ('.$obj->default_vat_code.')';
7809 }
7810 }
7811 $db->free($resql);
7812 } else {
7813 dol_print_error($db);
7814 }
7815 } else {
7816 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7817 // '1.23'
7818 // or '1.23 (CODE)'
7819 $defaulttx = '';
7820 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7821 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7822 }
7823 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7824 $defaultcode = $reg[1];
7825 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7826 }*/
7827
7828 $ret = $defaulttx;
7829 }
7830 }
7831
7832 dol_syslog("get_product_vat_for_country: ret=".$ret);
7833 return $ret;
7834}
7835
7845function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7846{
7847 global $db, $mysoc;
7848
7849 if (!class_exists('Product')) {
7850 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7851 }
7852
7853 $ret = 0;
7854 $found = 0;
7855
7856 if ($idprod > 0) {
7857 // Load product
7858 $product = new Product($db);
7859 $result = $product->fetch($idprod);
7860
7861 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7862 /* Not defined yet, so we don't use this
7863 if ($local==1) $ret=$product->localtax1_tx;
7864 elseif ($local==2) $ret=$product->localtax2_tx;
7865 $found=1;
7866 */
7867 } else {
7868 // TODO Read default product vat according to product and another countrycode.
7869 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7870 }
7871 }
7872
7873 if (!$found) {
7874 // If vat of product for the country not found or not defined, we return higher vat of country.
7875 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7876 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7877 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7878 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7879 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7880 $sql .= $db->plimit(1);
7881
7882 $resql = $db->query($sql);
7883 if ($resql) {
7884 $obj = $db->fetch_object($resql);
7885 if ($obj) {
7886 if ($local == 1) {
7887 $ret = $obj->localtax1;
7888 } elseif ($local == 2) {
7889 $ret = $obj->localtax2;
7890 }
7891 }
7892 } else {
7893 dol_print_error($db);
7894 }
7895 }
7896
7897 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7898 return $ret;
7899}
7900
7918function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7919{
7920 global $mysoc, $db;
7921
7922 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7923
7924 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7925 $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;
7926
7927 if (empty($thirdparty_seller->country_code)) {
7928 $thirdparty_seller->country_code = $mysoc->country_code;
7929 }
7930 $seller_country_code = $thirdparty_seller->country_code;
7931 $seller_in_cee = isInEEC($thirdparty_seller);
7932
7933 if (empty($thirdparty_buyer->country_code)) {
7934 $thirdparty_buyer->country_code = $mysoc->country_code;
7935 }
7936 $buyer_country_code = $thirdparty_buyer->country_code;
7937 $buyer_in_cee = isInEEC($thirdparty_buyer);
7938
7939 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'));
7940
7941 // 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)
7942 // we use the buyer VAT.
7943 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7944 if ($seller_in_cee && $buyer_in_cee) {
7945 $isacompany = $thirdparty_buyer->isACompany();
7946 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7947 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7948 if (!isValidVATID($thirdparty_buyer)) {
7949 $isacompany = 0;
7950 }
7951 }
7952
7953 if (!$isacompany) {
7954 //print 'VATRULE 0';
7955 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7956 }
7957 }
7958 }
7959
7960 // If seller does not use VAT, default VAT is 0. End of rule.
7961 if (!$seller_use_vat) {
7962 //print 'VATRULE 1';
7963 return 0;
7964 }
7965
7966 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
7967 if (!empty($thirdparty_buyer->state_id)) {
7968 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
7969 $sql .= " FROM ".$db->prefix()."c_tva as t";
7970 $sql .= " INNER JOIN ".$db->prefix()."c_departements as d ON t.fk_department_buyer = d.rowid";
7971 $sql .= " WHERE d.rowid = ".((int) $thirdparty_buyer->state_id);
7972 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7973
7974 $res = $db->query($sql);
7975 if ($res) {
7976 if ($db->num_rows($res)) {
7977 $obj = $db->fetch_object($res);
7978 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7979 }
7980 $db->free($res);
7981 }
7982 }
7983
7984 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7985 if (($seller_country_code == $buyer_country_code)
7986 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7987 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7988 ) { // Warning ->country_code not always defined
7989 //print 'VATRULE 3';
7990 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7991
7992 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7993 // Special case for india.
7994 //print 'VATRULE 3b';
7995 $reg = array();
7996 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7997 // we must revert the C+S into I
7998 $tmpvat = str_replace("C+S", "I", $tmpvat);
7999 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8000 // we must revert the I into C+S
8001 $tmpvat = str_replace("I", "C+S", $tmpvat);
8002 }
8003 }
8004
8005 return $tmpvat;
8006 }
8007
8008 // 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.
8009 // 'VATRULE 4' - Not supported
8010
8011 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8012 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8013 if (($seller_in_cee && $buyer_in_cee)) {
8014 $isacompany = $thirdparty_buyer->isACompany();
8015 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
8016 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
8017 if (!isValidVATID($thirdparty_buyer)) {
8018 $isacompany = 0;
8019 }
8020 }
8021
8022 if (!$isacompany) {
8023 //print 'VATRULE 5';
8024 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8025 } else {
8026 //print 'VATRULE 6';
8027 return 0;
8028 }
8029 }
8030
8031 // 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
8032 // I don't see any use case that need this rule.
8033 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8034 $isacompany = $thirdparty_buyer->isACompany();
8035 if (!$isacompany) {
8036 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8037 //print 'VATRULE extra';
8038 }
8039 }
8040
8041 // Otherwise the VAT proposed by default=0. End of rule.
8042 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8043 //print 'VATRULE 7';
8044 return 0;
8045}
8046
8047
8058function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8059{
8060 global $db;
8061
8062 if ($idprodfournprice > 0) {
8063 if (!class_exists('ProductFournisseur')) {
8064 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8065 }
8066 $prodprice = new ProductFournisseur($db);
8067 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8068 return $prodprice->fourn_tva_npr;
8069 } elseif ($idprod > 0) {
8070 if (!class_exists('Product')) {
8071 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8072 }
8073 $prod = new Product($db);
8074 $prod->fetch($idprod);
8075 return $prod->tva_npr;
8076 }
8077
8078 return 0;
8079}
8080
8094function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8095{
8096 global $mysoc;
8097
8098 if (!is_object($thirdparty_seller)) {
8099 return -1;
8100 }
8101 if (!is_object($thirdparty_buyer)) {
8102 return -1;
8103 }
8104
8105 if (empty($thirdparty_seller->country_code)) {
8106 $thirdparty_seller->country_code = $mysoc->country_code;
8107 }
8108 $seller_country_code = $thirdparty_seller->country_code;
8109 //$seller_in_cee = isInEEC($thirdparty_seller);
8110
8111 if (empty($thirdparty_buyer->country_code)) {
8112 $thirdparty_buyer->country_code = $mysoc->country_code;
8113 }
8114 $buyer_country_code = $thirdparty_buyer->country_code;
8115 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8116
8117 if ($local == 1) { // Localtax 1
8118 if ($mysoc->country_code == 'ES') {
8119 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8120 return 0;
8121 }
8122 } else {
8123 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8124 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8125 return 0;
8126 }
8127 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8128 return 0;
8129 }
8130 }
8131 } elseif ($local == 2) { //I Localtax 2
8132 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8133 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8134 return 0;
8135 }
8136 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8137 return 0;
8138 }
8139 }
8140
8141 if ($seller_country_code == $buyer_country_code) {
8142 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8143 }
8144
8145 return 0;
8146}
8147
8156function yn($yesno, $format = 1, $color = 0)
8157{
8158 global $langs;
8159
8160 $result = 'unknown';
8161 $classname = '';
8162 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
8163 $result = $langs->trans('yes');
8164 if ($format == 1 || $format == 3) {
8165 $result = $langs->trans("Yes");
8166 }
8167 if ($format == 2) {
8168 $result = '<input type="checkbox" value="1" checked disabled>';
8169 }
8170 if ($format == 3) {
8171 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
8172 }
8173 if ($format == 4 || !is_numeric($format)) {
8174 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8175 }
8176
8177 $classname = 'ok';
8178 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
8179 $result = $langs->trans("no");
8180 if ($format == 1 || $format == 3) {
8181 $result = $langs->trans("No");
8182 }
8183 if ($format == 2) {
8184 $result = '<input type="checkbox" value="0" disabled>';
8185 }
8186 if ($format == 3) {
8187 $result = '<input type="checkbox" value="0" disabled> '.$result;
8188 }
8189 if ($format == 4 || !is_numeric($format)) {
8190 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8191 }
8192
8193 if ($color == 2) {
8194 $classname = 'ok';
8195 } else {
8196 $classname = 'error';
8197 }
8198 }
8199 if ($color) {
8200 return '<span class="'.$classname.'">'.$result.'</span>';
8201 }
8202 return $result;
8203}
8204
8223function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8224{
8225 if (empty($modulepart) && is_object($object)) {
8226 if (!empty($object->module)) {
8227 $modulepart = $object->module;
8228 } elseif (!empty($object->element)) {
8229 $modulepart = $object->element;
8230 }
8231 }
8232
8233 $path = '';
8234
8235 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8236 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8237 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8238 $arrayforoldpath['product'] = 2;
8239 }
8240
8241 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8242 $level = $arrayforoldpath[$modulepart];
8243 }
8244 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8245 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8246 if (empty($num) && is_object($object)) {
8247 $num = $object->id;
8248 }
8249 if (empty($alpha)) {
8250 $num = preg_replace('/([^0-9])/i', '', $num);
8251 } else {
8252 $num = preg_replace('/^.*\-/i', '', $num);
8253 }
8254 $num = substr("000".$num, -$level);
8255 if ($level == 1) {
8256 $path = substr($num, 0, 1);
8257 }
8258 if ($level == 2) {
8259 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
8260 }
8261 if ($level == 3) {
8262 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
8263 }
8264 } else {
8265 // We will enhance here a common way of forging path for document storage.
8266 // In a future, we may distribute directories on several levels depending on setup and object.
8267 // Here, $object->id, $object->ref and $modulepart are required.
8268 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
8269 // 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
8270 $path = dol_sanitizeFileName((string) $object->id);
8271 } else {
8272 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8273 }
8274 }
8275
8276 if (empty($withoutslash) && !empty($path)) {
8277 $path .= '/';
8278 }
8279
8280 return $path;
8281}
8282
8291function dol_mkdir($dir, $dataroot = '', $newmask = '')
8292{
8293 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
8294
8295 $dir = dol_sanitizePathName($dir, '_', 0);
8296
8297 $dir_osencoded = dol_osencode($dir);
8298 if (@is_dir($dir_osencoded)) {
8299 return 0;
8300 }
8301
8302 $nberr = 0;
8303 $nbcreated = 0;
8304
8305 $ccdir = '';
8306 if (!empty($dataroot)) {
8307 // Remove data root from loop
8308 $dir = str_replace($dataroot.'/', '', $dir);
8309 $ccdir = $dataroot.'/';
8310 }
8311
8312 $cdir = explode("/", $dir);
8313 $num = count($cdir);
8314 for ($i = 0; $i < $num; $i++) {
8315 if ($i > 0) {
8316 $ccdir .= '/'.$cdir[$i];
8317 } else {
8318 $ccdir .= $cdir[$i];
8319 }
8320 $regs = array();
8321 if (preg_match("/^.:$/", $ccdir, $regs)) {
8322 continue; // If the Windows path is incomplete, continue with next directory
8323 }
8324
8325 // Attention, is_dir() can fail event if the directory exists
8326 // (i.e. according the open_basedir configuration)
8327 if ($ccdir) {
8328 $ccdir_osencoded = dol_osencode($ccdir);
8329 if (!@is_dir($ccdir_osencoded)) {
8330 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8331
8332 umask(0);
8333 $dirmaskdec = octdec((string) $newmask);
8334 if (empty($newmask)) {
8335 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8336 }
8337 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8338 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8339 // If the is_dir has returned a false information, we arrive here
8340 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' (no permission to write into parent or directory already exists).", LOG_WARNING);
8341 $nberr++;
8342 } else {
8343 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
8344 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8345 $nbcreated++;
8346 }
8347 } else {
8348 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8349 }
8350 }
8351 }
8352 return ($nberr ? -$nberr : $nbcreated);
8353}
8354
8355
8363function dolChmod($filepath, $newmask = '')
8364{
8365 if (!empty($newmask)) {
8366 @chmod($filepath, octdec($newmask));
8367 } elseif (getDolGlobalString('MAIN_UMASK')) {
8368 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8369 }
8370}
8371
8372
8378function picto_required()
8379{
8380 return '<span class="fieldrequired">*</span>';
8381}
8382
8383
8400function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8401{
8402 if (is_null($stringtoclean)) {
8403 return '';
8404 }
8405
8406 if ($removelinefeed == 2) {
8407 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8408 }
8409 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8410
8411 // 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)
8412 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8413
8414 $temp = str_replace('< ', '__ltspace__', $temp);
8415 $temp = str_replace('<:', '__lttwopoints__', $temp);
8416
8417 if ($strip_tags) {
8418 $temp = strip_tags($temp);
8419 } else {
8420 // 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).
8421 $pattern = "/<[^<>]+>/";
8422 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8423 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8424 // pass 2 - $temp after pass 2: 0000-021
8425 $tempbis = $temp;
8426 do {
8427 $temp = $tempbis;
8428 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8429 $tempbis = preg_replace($pattern, '', $tempbis);
8430 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8431 } while ($tempbis != $temp);
8432
8433 $temp = $tempbis;
8434
8435 // 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).
8436 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8437 }
8438
8439 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8440
8441 // Remove also carriage returns
8442 if ($removelinefeed == 1) {
8443 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8444 }
8445
8446 // And double quotes
8447 if ($removedoublespaces) {
8448 while (strpos($temp, " ")) {
8449 $temp = str_replace(" ", " ", $temp);
8450 }
8451 }
8452
8453 $temp = str_replace('__ltspace__', '< ', $temp);
8454 $temp = str_replace('__lttwopoints__', '<:', $temp);
8455
8456 return trim($temp);
8457}
8458
8478function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
8479{
8480 if (empty($allowed_tags)) {
8481 $allowed_tags = array(
8482 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8483 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8484 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8485 );
8486 }
8487 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8488 if ($allowiframe) {
8489 if (!in_array('iframe', $allowed_tags)) {
8490 $allowed_tags[] = "iframe";
8491 }
8492 }
8493 if ($allowlink) {
8494 if (!in_array('link', $allowed_tags)) {
8495 $allowed_tags[] = "link";
8496 }
8497 }
8498 if ($allowscript) {
8499 if (!in_array('script', $allowed_tags)) {
8500 $allowed_tags[] = "script";
8501 }
8502 }
8503 if ($allowstyle) {
8504 if (!in_array('style', $allowed_tags)) {
8505 $allowed_tags[] = "style";
8506 }
8507 }
8508
8509 $allowed_tags_string = implode("><", $allowed_tags);
8510 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8511
8512 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8513
8514 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8515
8516 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8517 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8518
8519 if ($allowphp) {
8520 $allowed_tags[] = "commentphp";
8521 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
8522 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
8523 }
8524
8525 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8526 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8527
8528 // Remove all HTML tags
8529 $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
8530
8531 if ($cleanalsosomestyles) { // Clean for remaining html tags
8532 $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
8533 }
8534 if ($removeclassattribute) { // Clean for remaining html tags
8535 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8536 }
8537
8538 // Remove 'javascript:' that we should not find into a text
8539 // 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)).
8540 if ($cleanalsojavascript) {
8541 $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);
8542 }
8543
8544 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8545
8546 if ($allowphp) {
8547 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
8548 }
8549
8550 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8551
8552
8553 return $temp;
8554}
8555
8556
8569function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8570{
8571 if (is_null($allowed_attributes)) {
8572 $allowed_attributes = array(
8573 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8574 // HTML5
8575 "header", "footer", "nav", "section", "menu", "menuitem"
8576 );
8577 }
8578 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8579 if (!in_array("content", $allowed_attributes)) {
8580 $allowed_attributes[] = "content";
8581 }
8582 if (!in_array("http-equiv", $allowed_attributes)) {
8583 $allowed_attributes[] = "http-equiv";
8584 }
8585
8586 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8587 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8588 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8589
8590 // Warning: loadHTML does not support HTML5 on old libxml versions.
8591 $dom = new DOMDocument('', 'UTF-8');
8592 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8593 $savwarning = error_reporting();
8594 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8595 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8596 error_reporting($savwarning);
8597
8598 if ($dom instanceof DOMDocument) {
8599 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8600 $el = $els->item($i);
8601 if (!$el instanceof DOMElement) {
8602 continue;
8603 }
8604 $attrs = $el->attributes;
8605 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8606 //var_dump($attrs->item($ii));
8607 if (!empty($attrs->item($ii)->name)) {
8608 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8609 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8610 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8611 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8612 // If attribute is 'style'
8613 $valuetoclean = $attrs->item($ii)->value;
8614
8615 if (isset($valuetoclean)) {
8616 do {
8617 $oldvaluetoclean = $valuetoclean;
8618 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8619 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8620 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8621 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8622 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8623 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8624 }
8625
8626 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8627 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8628 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8629 } while ($oldvaluetoclean != $valuetoclean);
8630 }
8631
8632 $attrs->item($ii)->value = $valuetoclean;
8633 }
8634 }
8635 }
8636 }
8637 }
8638
8639 $dom->encoding = 'UTF-8';
8640
8641 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8642 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8643
8644 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8645 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8646 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8647
8648 return trim($return);
8649 } else {
8650 return $stringtoclean;
8651 }
8652}
8653
8665function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8666{
8667 $temp = $stringtoclean;
8668 foreach ($disallowed_tags as $tagtoremove) {
8669 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8670 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8671 }
8672
8673 if ($cleanalsosomestyles) {
8674 $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
8675 }
8676
8677 return $temp;
8678}
8679
8680
8690function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8691{
8692 if ($nboflines == 1) {
8693 if (dol_textishtml($text)) {
8694 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8695 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8696 } else {
8697 if (isset($text)) {
8698 $firstline = preg_replace('/[\n\r].*/', '', $text);
8699 } else {
8700 $firstline = '';
8701 }
8702 }
8703 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8704 } else {
8705 $ishtml = 0;
8706 if (dol_textishtml($text)) {
8707 $text = preg_replace('/\n/', '', $text);
8708 $ishtml = 1;
8709 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8710 } else {
8711 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8712 }
8713
8714 $text = strtr($text, $repTable);
8715 if ($charset == 'UTF-8') {
8716 $pattern = '/(<br[^>]*>)/Uu';
8717 } else {
8718 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8719 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8720 }
8721 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8722
8723 $firstline = '';
8724 $i = 0;
8725 $countline = 0;
8726 $lastaddediscontent = 1;
8727 while ($countline < $nboflines && isset($a[$i])) {
8728 if (preg_match('/<br[^>]*>/', $a[$i])) {
8729 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8730 $firstline .= ($ishtml ? "<br>\n" : "\n");
8731 // Is it a br for a new line of after a printed line ?
8732 if (!$lastaddediscontent) {
8733 $countline++;
8734 }
8735 $lastaddediscontent = 0;
8736 }
8737 } else {
8738 $firstline .= $a[$i];
8739 $lastaddediscontent = 1;
8740 $countline++;
8741 }
8742 $i++;
8743 }
8744
8745 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8746 //unset($a);
8747 $ret = $firstline.($adddots ? '...' : '');
8748 //exit;
8749 return $ret;
8750 }
8751}
8752
8753
8765function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8766{
8767 if (is_null($stringtoencode)) {
8768 return '';
8769 }
8770
8771 if (!$nl2brmode) {
8772 return nl2br($stringtoencode, $forxml);
8773 } else {
8774 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8775 return $ret;
8776 }
8777}
8778
8788function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8789{
8790 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8791 // TODO using sandbox on inline html content is not possible yet with current browsers
8792 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8793 //$s .= $stringtoencode;
8794 //$s .= '</body></html></iframe>';
8795 return $stringtoencode;
8796 } else {
8797 $out = $stringtoencode;
8798
8799 // First clean HTML content
8800 do {
8801 $oldstringtoclean = $out;
8802
8803 $outishtml = 0;
8804 if (dol_textishtml($out)) {
8805 $outishtml = 1;
8806 }
8807
8808 // HTML sanitizer by DOMDocument
8809 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8810 try {
8811 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8812 if (LIBXML_VERSION < 20900) {
8813 // Avoid load of external entities (security problem).
8814 // Required only if LIBXML_VERSION < 20900
8815 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8816 libxml_disable_entity_loader(true);
8817 }
8818
8819 $dom = new DOMDocument();
8820 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
8821 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8822 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8823 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
8824 // I don't know what the xml encoding is the trick for
8825 if ($outishtml) {
8826 //$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>';
8827 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8828 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8829 } else {
8830 //$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>';
8831 $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>';
8832 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
8833 }
8834
8835 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8836
8837 $dom->encoding = 'UTF-8';
8838
8839 $out = trim($dom->saveHTML());
8840
8841 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8842 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8843 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8844 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8845 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8846 // $out = preg_replace('/<\/div>$/', '', $out);
8847 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8848
8849 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
8850 $out = str_replace('<br>', '', $out);
8851 }
8852 } catch (Exception $e) {
8853 // If error, invalid HTML string with no way to clean it
8854 //print $e->getMessage();
8855 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8856 }
8857 }
8858
8859 // HTML sanitizer by Tidy
8860 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8861 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
8862 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
8863 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8864 try {
8865 //var_dump($out);
8866
8867 // Try cleaning using tidy
8868 if (extension_loaded('tidy') && class_exists("tidy")) {
8869 //print "aaa".$out."\n";
8870
8871 // See options at https://tidy.sourceforge.net/docs/quickref.html
8872 $config = array(
8873 'clean' => false,
8874 // 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;
8875 'quote-marks' => false,
8876 'doctype' => 'strict',
8877 'show-body-only' => true,
8878 "indent-attributes" => false,
8879 "vertical-space" => false,
8880 //'ident' => false, // Not always supported
8881 "wrap" => 0,
8882 'preserve-entities' => true
8883 // HTML5 tags
8884 //'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',
8885 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8886 //'new-empty-tags' => 'command embed keygen source track wbr',
8887 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8888 );
8889
8890 // Tidy
8891 $tidy = new tidy();
8892 $out = $tidy->repairString($out, $config, 'utf8');
8893
8894 //print "xxx".$out;exit;
8895 }
8896
8897 //var_dump($out);
8898 } catch (Exception $e) {
8899 // If error, invalid HTML string with no way to clean it
8900 //print $e->getMessage();
8901 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8902 }
8903 }
8904
8905 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8906 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8907
8908 // Clean some html entities that are useless so text is cleaner
8909 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8910
8911 // Ckeditor uses the numeric entity for apostrophe, so we force it to
8912 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
8913 $out = preg_replace('/&#39;/i', '&apos;', $out);
8914
8915 // 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).
8916 // 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
8917 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8918 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
8919 $out = preg_replace_callback(
8920 '/&#(x?[0-9][0-9a-f]+;?)/i',
8925 static function ($m) {
8926 return realCharForNumericEntities($m);
8927 },
8928 $out
8929 );
8930 }
8931
8932 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8933 $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'.
8934
8935 // Keep only some html tags and remove also some 'javascript:' strings
8936 if ($check == 'restricthtmlallowlinkscript') {
8937 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
8938 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8939 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8940 } elseif ($check == 'restricthtmlallowiframe') {
8941 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
8942 } else {
8943 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8944 }
8945
8946 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8947 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8949 }
8950
8951 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
8952 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
8953 $out = preg_replace('/&apos;/i', "&#39;", $out);
8954
8955 // Now remove js
8956 // 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
8957 $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)>
8958 $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);
8959 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8960 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8961 $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);
8962 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8963 // More not into the previous list
8964 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8965 } while ($oldstringtoclean != $out);
8966
8967 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8968 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8969 // 'url(' to avoid inline style like background: url(http...
8970 // '<link' to avoid <link href="http...">
8971 $reg = array();
8972 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8973 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8974 $nblinks = count($reg[0]);
8975 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8976 $out = 'ErrorTooManyLinksIntoHTMLString';
8977 }
8978
8979 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8980 if ($nblinks > 0) {
8981 $out = 'ErrorHTMLLinksNotAllowed';
8982 }
8983 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8984 $nblinks = 0;
8985 // Loop on each url in src= and url(
8986 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8987
8988 $matches = array();
8989 if (preg_match_all($pattern, $out, $matches)) {
8990 // URLs are into $matches[1]
8991 $urls = $matches[1];
8992
8993 // Affiche les URLs
8994 foreach ($urls as $url) {
8995 $nblinks++;
8996 echo "Found url = ".$url . "\n";
8997 }
8998 if ($nblinks > 0) {
8999 $out = 'ErrorHTMLExternalLinksNotAllowed';
9000 }
9001 }
9002 }
9003
9004 return $out;
9005 }
9006}
9007
9028function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9029{
9030 if (is_null($stringtoencode)) {
9031 return '';
9032 }
9033
9034 $newstring = $stringtoencode;
9035 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9036 $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.
9037 if ($removelasteolbr) {
9038 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9039 }
9040 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9041 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9042 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9043 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9044 } else {
9045 if ($removelasteolbr) {
9046 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9047 }
9048 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9049 }
9050 // Other substitutions that htmlentities does not do
9051 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9052 return $newstring;
9053}
9054
9062function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9063{
9064 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9065 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9066 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
9067 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
9068 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9069 return $ret;
9070}
9071
9078function dol_htmlcleanlastbr($stringtodecode)
9079{
9080 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9081 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
9082 return $ret;
9083}
9084
9094function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9095{
9096 $newstring = $a;
9097 if ($keepsomeentities) {
9098 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9099 }
9100 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9101 if ($keepsomeentities) {
9102 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9103 }
9104 return $newstring;
9105}
9106
9118function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9119{
9120 return htmlentities($string, $flags, $encoding, $double_encode);
9121}
9122
9134function dol_string_is_good_iso($s, $clean = 0)
9135{
9136 $len = dol_strlen($s);
9137 $out = '';
9138 $ok = 1;
9139 for ($scursor = 0; $scursor < $len; $scursor++) {
9140 $ordchar = ord($s[$scursor]);
9141 //print $scursor.'-'.$ordchar.'<br>';
9142 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9143 $ok = 0;
9144 break;
9145 } elseif ($ordchar > 126 && $ordchar < 160) {
9146 $ok = 0;
9147 break;
9148 } elseif ($clean) {
9149 $out .= $s[$scursor];
9150 }
9151 }
9152 if ($clean) {
9153 return $out;
9154 }
9155 return $ok;
9156}
9157
9166function dol_nboflines($s, $maxchar = 0)
9167{
9168 if ($s == '') {
9169 return 0;
9170 }
9171 $arraystring = explode("\n", $s);
9172 $nb = count($arraystring);
9173
9174 return $nb;
9175}
9176
9177
9187function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9188{
9189 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9190 if (dol_textishtml($text)) {
9191 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9192 }
9193
9194 $text = strtr($text, $repTable);
9195 if ($charset == 'UTF-8') {
9196 $pattern = '/(<br[^>]*>)/Uu';
9197 } else {
9198 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9199 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9200 }
9201 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9202
9203 $nblines = (int) floor((count($a) + 1) / 2);
9204 // count possible auto line breaks
9205 if ($maxlinesize) {
9206 foreach ($a as $line) {
9207 if (dol_strlen($line) > $maxlinesize) {
9208 //$line_dec = html_entity_decode(strip_tags($line));
9209 $line_dec = html_entity_decode($line);
9210 if (dol_strlen($line_dec) > $maxlinesize) {
9211 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9212 $nblines += substr_count($line_dec, '\n');
9213 }
9214 }
9215 }
9216 }
9217
9218 unset($a);
9219 return $nblines;
9220}
9221
9230function dol_textishtml($msg, $option = 0)
9231{
9232 if (is_null($msg)) {
9233 return false;
9234 }
9235
9236 if ($option == 1) {
9237 if (preg_match('/<(html|link|script)/i', $msg)) {
9238 return true;
9239 } elseif (preg_match('/<body/i', $msg)) {
9240 return true;
9241 } elseif (preg_match('/<\/textarea/i', $msg)) {
9242 return true;
9243 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9244 return true;
9245 } elseif (preg_match('/<br/i', $msg)) {
9246 return true;
9247 }
9248 return false;
9249 } else {
9250 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9251 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9252 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9253 return true;
9254 } elseif (preg_match('/<\/textarea/i', $msg)) {
9255 return true;
9256 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9257 return true;
9258 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9259 return true;
9260 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9261 return true;
9262 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9263 return true;
9264 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9265 return true; // must accept <img src="http://example.com/aaa.png" />
9266 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9267 return true; // must accept <a href="http://example.com/aaa.png" />
9268 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9269 return true;
9270 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9271 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9272 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9273 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9274 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9275 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
9276 return true; // Html entities numbers in hexa
9277 }
9278
9279 return false;
9280 }
9281}
9282
9297function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
9298{
9299 if (!empty($invert)) {
9300 $tmp = $text1;
9301 $text1 = $text2;
9302 $text2 = $tmp;
9303 }
9304
9305 $ret = '';
9306 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
9307 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
9308 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
9309 return $ret;
9310}
9311
9312
9313
9327function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
9328{
9329 global $db, $conf, $mysoc, $user, $extrafields;
9330
9331 $substitutionarray = array();
9332
9333 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
9334 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
9335 // this will include signature content first and then replace var found into content of signature
9336 //var_dump($onlykey);
9337 $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()
9338 $usersignature = $user->signature;
9339 $substitutionarray = array_merge($substitutionarray, array(
9340 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
9341 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
9342 ));
9343
9344 if (is_object($user) && ($user instanceof User)) {
9345 $substitutionarray = array_merge($substitutionarray, array(
9346 '__USER_ID__' => (string) $user->id,
9347 '__USER_LOGIN__' => (string) $user->login,
9348 '__USER_EMAIL__' => (string) $user->email,
9349 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
9350 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
9351 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
9352 '__USER_FAX__' => (string) $user->office_fax,
9353 '__USER_LASTNAME__' => (string) $user->lastname,
9354 '__USER_FIRSTNAME__' => (string) $user->firstname,
9355 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
9356 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
9357 '__USER_JOB__' => (string) $user->job,
9358 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
9359 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
9360 ));
9361 }
9362 }
9363 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
9364 $substitutionarray = array_merge($substitutionarray, array(
9365 '__MYCOMPANY_NAME__' => $mysoc->name,
9366 '__MYCOMPANY_EMAIL__' => $mysoc->email,
9367 '__MYCOMPANY_URL__' => $mysoc->url,
9368 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
9369 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
9370 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
9371 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
9372 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
9373 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
9374 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
9375 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
9376 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
9377 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
9378 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
9379 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
9380 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
9381 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
9382 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
9383 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
9384 '__MYCOMPANY_ZIP__' => $mysoc->zip,
9385 '__MYCOMPANY_TOWN__' => $mysoc->town,
9386 '__MYCOMPANY_STATE__' => $mysoc->state,
9387 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
9388 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
9389 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
9390 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
9391 ));
9392 }
9393
9394 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
9395 if ($onlykey) {
9396 $substitutionarray['__ID__'] = '__ID__';
9397 $substitutionarray['__REF__'] = '__REF__';
9398 $substitutionarray['__NEWREF__'] = '__NEWREF__';
9399 $substitutionarray['__LABEL__'] = '__LABEL__';
9400 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
9401 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
9402 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
9403 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
9404 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
9405
9406 if (isModEnabled("societe")) { // Most objects are concerned
9407 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
9408 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
9409 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
9410 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
9411 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
9412 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
9413 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
9414 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
9415 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
9416 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
9417 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
9418 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
9419 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
9420 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
9421 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
9422 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
9423 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
9424 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
9425 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
9426 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
9427 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
9428 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
9429 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
9430 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
9431 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
9432 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
9433 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
9434 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
9435 }
9436 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9437 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9438 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9439 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9440 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9441 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9442 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9443 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9444 }
9445 // add substitution variables for ticket
9446 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9447 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9448 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9449 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9450 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9451 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9452 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9453 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9454 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9455 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9456 }
9457
9458 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9459 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9460 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9461 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9462 }
9463 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9464 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9465 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9466 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9467 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9468 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9469 }
9470 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9471 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9472 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9473 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9474 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9475 }
9476 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9477 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9478 }
9479 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9480 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9481 }
9482 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9483 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9484 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9485 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9486 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9487 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9488 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9489
9490 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9491 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9492 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9493 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9494 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9495
9496 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9497 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9498 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9499 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9500 }
9501 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9502 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9503 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9504 }
9505 } else {
9506 '@phan-var-force Adherent|Delivery $object';
9508 $substitutionarray['__ID__'] = $object->id;
9509 $substitutionarray['__REF__'] = $object->ref;
9510 $substitutionarray['__NEWREF__'] = $object->newref;
9511 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9512 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9513 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9514 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9515 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9516
9517 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9518 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9519 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9520
9521 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
9522 $date_delivery = null;
9523 if (property_exists($object, 'date_delivery')) {
9524 $date_delivery = $object->date_delivery;
9525 } elseif (property_exists($object, 'delivery_date')) {
9526 $date_delivery = $object->delivery_date;
9527 }
9528 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9529 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
9530 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
9531 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
9532 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
9533 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
9534 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
9535 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
9536 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
9537
9538 // For backward compatibility (deprecated)
9539 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9540 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9541
9542 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9543 $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 : '')) : '');
9544 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9545
9546 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9547 '@phan-var-force Adherent $object';
9549 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9550
9551 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9552 if (method_exists($object, 'getCivilityLabel')) {
9553 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9554 }
9555 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9556 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9557 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9558 if (method_exists($object, 'getFullName')) {
9559 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9560 }
9561 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9562 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9563 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9564 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9565 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9566 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9567 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9568 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9569 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9570 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9571 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9572 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9573 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9574 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9575 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9576 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9577
9578 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9579 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9580 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9581 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9582 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9583 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9584 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9585 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9586 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9587 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9588 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9589 }
9590
9591 if (is_object($object) && $object->element == 'societe') {
9593 '@phan-var-force Societe $object';
9594 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
9595 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
9596 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
9597 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
9598 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
9599 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
9600 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
9601 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
9602 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
9603 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
9604 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
9605 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
9606 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
9607 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
9608 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
9609 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ?: '');
9610 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
9611 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
9612 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
9613 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
9614 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
9615 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
9616 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
9617 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
9618 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
9619 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
9620 } elseif (is_object($object) && is_object($object->thirdparty)) {
9621 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
9622 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
9623 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
9624 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
9625 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
9626 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
9627 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
9628 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
9629 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
9630 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
9631 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
9632 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
9633 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
9634 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ?: '');
9635 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
9636 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
9637 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
9638 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
9639 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
9640 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
9641 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
9642 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
9643 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
9644 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
9645 }
9646
9647 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9648 '@phan-var-force RecruitmentCandidature $object';
9650 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9651 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9652 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9653 }
9654 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9655 '@phan-var-force ConferenceOrBoothAttendee $object';
9657 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9658 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9659 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9660 }
9661
9662 if (is_object($object) && $object->element == 'project') {
9663 '@phan-var-force Project $object';
9665 $substitutionarray['__PROJECT_ID__'] = $object->id;
9666 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9667 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9668 } elseif (is_object($object)) {
9669 $project = null;
9670 if (!empty($object->project)) {
9671 $project = $object->project;
9672 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9673 $project = $object->projet;
9674 }
9675 if (!is_null($project) && is_object($project)) {
9676 $substitutionarray['__PROJECT_ID__'] = $project->id;
9677 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9678 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9679 } else {
9680 // can substitute variables for project : uses lazy load in "make_substitutions" method
9681 $project_id = 0;
9682 if (!empty($object->fk_project) && $object->fk_project > 0) {
9683 $project_id = $object->fk_project;
9684 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9685 $project_id = $object->fk_project;
9686 }
9687 if ($project_id > 0) {
9688 // path:class:method:id
9689 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9690 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9691 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9692 }
9693 }
9694 }
9695
9696 if (is_object($object) && $object->element == 'facture') {
9697 '@phan-var-force Facture $object';
9699 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9700 }
9701 if (is_object($object) && $object->element == 'shipping') {
9702 '@phan-var-force Expedition $object';
9704 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9705 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9706 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9707 }
9708 if (is_object($object) && $object->element == 'reception') {
9709 '@phan-var-force Reception $object';
9711 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9712 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9713 }
9714
9715 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9716 '@phan-var-force Contrat $object';
9718 $dateplannedstart = '';
9719 $datenextexpiration = '';
9720 foreach ($object->lines as $line) {
9721 if ($line->date_start > $dateplannedstart) {
9722 $dateplannedstart = $line->date_start;
9723 }
9724 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9725 $datenextexpiration = $line->date_end;
9726 }
9727 }
9728 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9729 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9730 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9731
9732 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9733 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9734 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9735 }
9736 // add substitution variables for ticket
9737 if (is_object($object) && $object->element == 'ticket') {
9738 '@phan-var-force Ticket $object';
9740 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9741 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9742 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9743 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9744 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9745 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9746 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9747 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9748 $userstat = new User($db);
9749 if ($object->fk_user_assign > 0) {
9750 $userstat->fetch($object->fk_user_assign);
9751 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9752 }
9753
9754 if ($object->fk_user_create > 0) {
9755 $userstat->fetch($object->fk_user_create);
9756 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9757 }
9758 }
9759
9760 // Create dynamic tags for __EXTRAFIELD_FIELD__
9761 if ($object->table_element && $object->id > 0) {
9762 if (!is_object($extrafields)) {
9763 $extrafields = new ExtraFields($db);
9764 }
9765 $extrafields->fetch_name_optionals_label($object->table_element, true);
9766
9767 if ($object->fetch_optionals() > 0) {
9768 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9769 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9770 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9771 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9772 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9773 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9774 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9775 $datetime = $object->array_options['options_'.$key];
9776 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9777 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9778 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9779 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9780 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9781 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9782 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9783 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9784 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9785 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9786 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9787 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9788 }
9789 }
9790 }
9791 }
9792 }
9793
9794 // Complete substitution array with the url to make online payment
9795 if (empty($substitutionarray['__REF__'])) {
9796 $paymenturl = '';
9797 } else {
9798 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9799 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9800 $outputlangs->loadLangs(array('paypal', 'other'));
9801
9802 $amounttouse = 0;
9803 $typeforonlinepayment = 'free';
9804 if (is_object($object) && $object->element == 'commande') {
9805 $typeforonlinepayment = 'order';
9806 }
9807 if (is_object($object) && $object->element == 'facture') {
9808 $typeforonlinepayment = 'invoice';
9809 }
9810 if (is_object($object) && $object->element == 'member') {
9811 $typeforonlinepayment = 'member';
9812 if (!empty($object->last_subscription_amount)) {
9813 $amounttouse = $object->last_subscription_amount;
9814 }
9815 }
9816 if (is_object($object) && $object->element == 'contrat') {
9817 $typeforonlinepayment = 'contract';
9818 }
9819 if (is_object($object) && $object->element == 'fichinter') {
9820 $typeforonlinepayment = 'ficheinter';
9821 }
9822
9823 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9824 $paymenturl = $url;
9825 }
9826
9827 if ($object->id > 0) {
9828 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9829 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9830
9831 // Show structured communication
9832 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9833 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9834 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9835 }
9836
9837 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9838 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9839 } else {
9840 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9841 }
9842 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9843 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9844 } else {
9845 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9846 }
9847 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9848 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9849 } else {
9850 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9851 }
9852 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9853 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9854 } else {
9855 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9856 }
9857 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9858 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9859 } else {
9860 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9861 }
9862 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9863 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9864 } else {
9865 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9866 }
9867
9868 if (is_object($object) && $object->element == 'propal') {
9869 '@phan-var-force Propal $object';
9871 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9872 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9873 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9874 }
9875 if (is_object($object) && $object->element == 'commande') {
9876 '@phan-var-force Commande $object';
9878 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9879 }
9880 if (is_object($object) && $object->element == 'facture') {
9881 '@phan-var-force Facture $object';
9883 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9884 }
9885 if (is_object($object) && $object->element == 'contrat') {
9886 '@phan-var-force Contrat $object';
9888 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9889 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9890 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9891 }
9892 if (is_object($object) && $object->element == 'fichinter') {
9893 '@phan-var-force Fichinter $object';
9895 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9896 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9897 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9898 }
9899 if (is_object($object) && $object->element == 'supplier_proposal') {
9900 '@phan-var-force SupplierProposal $object';
9902 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9903 }
9904 if (is_object($object) && $object->element == 'invoice_supplier') {
9905 '@phan-var-force FactureFournisseur $object';
9907 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9908 }
9909 if (is_object($object) && $object->element == 'shipping') {
9910 '@phan-var-force Expedition $object';
9912 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9913 }
9914 }
9915
9916 if (is_object($object) && $object->element == 'action') {
9917 '@phan-var-force ActionComm $object';
9919 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9920 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9921 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9922 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9923 }
9924 }
9925 }
9926 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9927 '@phan-var-force Facture|FactureRec $object';
9929 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9930
9931 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9932 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9933 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9934 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9935
9936 $already_payed_all = 0;
9937 if (is_object($object) && ($object instanceof Facture)) {
9938 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9939 }
9940
9941 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9942 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9943 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9944
9945 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9946 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9947 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9948
9949 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9950
9951 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9952 $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)) : '';
9953 $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)) : '';
9954
9955 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9956 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9957 }
9958 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9959 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9960 }
9961
9962 // Amount keys formatted in a currency
9963 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9964 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9965 $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) : '';
9966 $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)) : '';
9967 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9968 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9969 }
9970 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9971 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9972 }
9973 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9974 if ($onlykey != 2) {
9975 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9976 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9977 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9978 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9979 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9980 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9981 }
9982 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9983 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9984 }
9985 }
9986
9987 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9988 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9989 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9990 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9991 // TODO Add other keys for foreign multicurrency
9992
9993 // For backward compatibility
9994 if ($onlykey != 2) {
9995 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9996 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9997 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9998 }
9999 }
10000
10001
10002 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10003 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
10004
10005 $now = dol_now();
10006
10007 $tmp = dol_getdate($now, true);
10008 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10009 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10010 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10011 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10012
10013 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
10014
10015 $substitutionarray = array_merge($substitutionarray, array(
10016 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10017 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10018 '__DAY__' => (string) $tmp['mday'],
10019 '__DAY_TEXT__' => $daytext, // Monday
10020 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10021 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10022 '__MONTH__' => (string) $tmp['mon'],
10023 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
10024 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
10025 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
10026 '__YEAR__' => (string) $tmp['year'],
10027 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10028 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10029 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10030 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10031 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp3['month'])),
10032 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp3['month'])),
10033 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp3['month'])),
10034 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10035 '__NEXT_DAY__' => (string) $tmp4['day'],
10036 '__NEXT_MONTH__' => (string) $tmp5['month'],
10037 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
10038 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
10039 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
10040 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10041 ));
10042 }
10043
10044 if (isModEnabled('multicompany')) {
10045 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10046 }
10047 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10048 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10049 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10050 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
10051 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10052 }
10053
10054 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10055
10056 return $substitutionarray;
10057}
10058
10075function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10076{
10077 global $conf, $db, $langs;
10078
10079 if (!is_array($substitutionarray)) {
10080 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10081 }
10082
10083 if (empty($outputlangs)) {
10084 $outputlangs = $langs;
10085 }
10086
10087 // Is initial text HTML or simple text ?
10088 $msgishtml = 0;
10089 if (dol_textishtml($text, 1)) {
10090 $msgishtml = 1;
10091 }
10092
10093 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10094 if (is_object($outputlangs)) {
10095 $reg = array();
10096 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10097 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10098 $tmp = explode('|', $reg[1]);
10099 if (!empty($tmp[1])) {
10100 $outputlangs->load($tmp[1]);
10101 }
10102
10103 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10104
10105 if (empty($converttextinhtmlifnecessary)) {
10106 // convert $newval into HTML is necessary
10107 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10108 } else {
10109 if (! $msgishtml) {
10110 $valueishtml = dol_textishtml($value, 1);
10111 //var_dump("valueishtml=".$valueishtml);
10112
10113 if ($valueishtml) {
10114 $text = dol_htmlentitiesbr($text);
10115 $msgishtml = 1;
10116 }
10117 } else {
10118 $value = dol_nl2br((string) $value);
10119 }
10120
10121 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
10122 }
10123 }
10124 }
10125
10126 // Make substitution for constant keys.
10127 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10128 $reg = array();
10129 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10130 $keyfound = $reg[1];
10131 if (isASecretKey($keyfound)) {
10132 $value = '*****forbidden*****';
10133 } else {
10134 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
10135 }
10136
10137 if (empty($converttextinhtmlifnecessary)) {
10138 // convert $newval into HTML is necessary
10139 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10140 } else {
10141 if (! $msgishtml) {
10142 $valueishtml = dol_textishtml($value, 1);
10143
10144 if ($valueishtml) {
10145 $text = dol_htmlentitiesbr($text);
10146 $msgishtml = 1;
10147 }
10148 } else {
10149 $value = dol_nl2br((string) $value);
10150 }
10151
10152 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
10153 }
10154 }
10155
10156 // Make substitution for array $substitutionarray
10157 foreach ($substitutionarray as $key => $value) {
10158 if (!isset($value)) {
10159 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10160 }
10161
10162 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
10163 $value = ''; // Protection
10164 }
10165
10166 if (empty($converttextinhtmlifnecessary)) {
10167 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10168 } else {
10169 if (! $msgishtml) {
10170 $valueishtml = dol_textishtml($value, 1);
10171
10172 if ($valueishtml) {
10173 $text = dol_htmlentitiesbr($text);
10174 $msgishtml = 1;
10175 }
10176 } else {
10177 $value = dol_nl2br((string) $value);
10178 }
10179 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10180 }
10181 }
10182
10183 // TODO Implement the lazyload substitution
10184 /*
10185 add a loop to scan $substitutionarray:
10186 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.
10187 If no, we don't need to make replacement, so we do nothing.
10188 If yes, we can make the substitution:
10189
10190 include_once $path;
10191 $tmpobj = new $class($db);
10192 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10193 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10194 */
10195 $memory_object_list = array();
10196 foreach ($substitutionarray as $key => $value) {
10197 $lazy_load_arr = array();
10198 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10199 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10200 $key_to_substitute = $lazy_load_arr[1];
10201 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10202 $param_arr = explode(':', (string) $value);
10203 // path:class:method:id
10204 if (count($param_arr) == 4) {
10205 $path = $param_arr[0];
10206 $class = $param_arr[1];
10207 $method = $param_arr[2];
10208 $id = (int) $param_arr[3];
10209
10210 // load class file and init object list in memory
10211 if (!isset($memory_object_list[$class])) {
10212 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10213 require_once DOL_DOCUMENT_ROOT . $path;
10214 if (class_exists($class)) {
10215 $memory_object_list[$class] = array(
10216 'list' => array(),
10217 );
10218 }
10219 }
10220 }
10221
10222 // fetch object and set substitution
10223 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
10224 if (method_exists($class, $method)) {
10225 if (!isset($memory_object_list[$class]['list'][$id])) {
10226 $tmpobj = new $class($db);
10227 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10228 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
10229 $memory_object_list[$class]['list'][$id] = $tmpobj;
10230 } else {
10231 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10232 $tmpobj = $memory_object_list[$class]['list'][$id];
10233 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10234 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
10235 }
10236
10237 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
10238 }
10239 }
10240 }
10241 }
10242 }
10243 }
10244 }
10245
10246 return $text;
10247}
10248
10261function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
10262{
10263 global $conf, $user;
10264
10265 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
10266
10267 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
10268
10269 // Check if there is external substitution to do, requested by plugins
10270 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
10271
10272 foreach ($dirsubstitutions as $reldir) {
10273 $dir = dol_buildpath($reldir, 0);
10274
10275 // Check if directory exists
10276 if (!dol_is_dir($dir)) {
10277 continue;
10278 }
10279
10280 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
10281 foreach ($substitfiles as $substitfile) {
10282 $reg = array();
10283 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
10284 $module = $reg[1];
10285
10286 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
10287 // Include the user's functions file
10288 require_once $dir.$substitfile['name'];
10289 // Call the user's function, and only if it is defined
10290 $function_name = $module."_".$callfunc;
10291 if (function_exists($function_name)) {
10292 $function_name($substitutionarray, $outputlangs, $object, $parameters);
10293 }
10294 }
10295 }
10296 }
10297 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
10298 // to list all tags in odt template
10299 $tags = '';
10300 foreach ($substitutionarray as $key => $value) {
10301 $tags .= '{'.$key.'} => '.$value."\n";
10302 }
10303 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
10304 }
10305}
10306
10316function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
10317{
10318 print get_date_range($date_start, $date_end, $format, $outputlangs);
10319}
10320
10331function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
10332{
10333 global $langs;
10334
10335 $out = '';
10336
10337 if (!is_object($outputlangs)) {
10338 $outputlangs = $langs;
10339 }
10340
10341 if ($date_start && $date_end) {
10342 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10343 }
10344 if ($date_start && !$date_end) {
10345 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10346 }
10347 if (!$date_start && $date_end) {
10348 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10349 }
10350
10351 return $out;
10352}
10353
10362function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
10363{
10364 global $conf;
10365
10366 $ret = '';
10367 // If order not defined, we use the setup
10368 if ($nameorder < 0) {
10369 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
10370 }
10371 if ($nameorder == 1) {
10372 $ret .= $firstname;
10373 if ($firstname && $lastname) {
10374 $ret .= ' ';
10375 }
10376 $ret .= $lastname;
10377 } elseif ($nameorder == 2 || $nameorder == 3) {
10378 $ret .= $firstname;
10379 if (empty($ret) && $nameorder == 3) {
10380 $ret .= $lastname;
10381 }
10382 } else { // 0, 4 or 5
10383 $ret .= $lastname;
10384 if (empty($ret) && $nameorder == 5) {
10385 $ret .= $firstname;
10386 }
10387 if ($nameorder == 0) {
10388 if ($firstname && $lastname) {
10389 $ret .= ' ';
10390 }
10391 $ret .= $firstname;
10392 }
10393 }
10394 return $ret;
10395}
10396
10397
10410function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
10411{
10412 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
10413 if (!is_array($mesgs)) {
10414 $mesgs = trim((string) $mesgs);
10415 // If mesgs is a not an empty string
10416 if ($mesgs) {
10417 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
10418 return;
10419 }
10420 if ($attop) {
10421 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10422 } else {
10423 $_SESSION['dol_events'][$style][] = $mesgs;
10424 }
10425 }
10426 } else {
10427 // If mesgs is an array
10428 foreach ($mesgs as $mesg) {
10429 $mesg = trim((string) $mesg);
10430 if ($mesg) {
10431 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
10432 return;
10433 }
10434 if ($attop) {
10435 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10436 } else {
10437 $_SESSION['dol_events'][$style][] = $mesg;
10438 }
10439 }
10440 }
10441 }
10442}
10443
10457function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
10458{
10459 if (empty($mesg) && empty($mesgs)) {
10460 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
10461 } else {
10462 if ($messagekey) {
10463 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
10464 // TODO
10465 $mesg .= '';
10466 }
10467 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
10468 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
10469 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
10470 }
10471 if (empty($mesgs)) {
10472 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
10473 } else {
10474 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10475 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10476 }
10477 setEventMessage($mesgs, $style, $noduplicate, $attop);
10478 }
10479 }
10480 }
10481}
10482
10492function dol_htmloutput_events($disabledoutputofmessages = 0)
10493{
10494 // Show mesgs
10495 if (isset($_SESSION['dol_events']['mesgs'])) {
10496 if (empty($disabledoutputofmessages)) {
10497 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10498 }
10499 unset($_SESSION['dol_events']['mesgs']);
10500 }
10501 // Show errors
10502 if (isset($_SESSION['dol_events']['errors'])) {
10503 if (empty($disabledoutputofmessages)) {
10504 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10505 }
10506 unset($_SESSION['dol_events']['errors']);
10507 }
10508
10509 // Show warnings
10510 if (isset($_SESSION['dol_events']['warnings'])) {
10511 if (empty($disabledoutputofmessages)) {
10512 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10513 }
10514 unset($_SESSION['dol_events']['warnings']);
10515 }
10516}
10517
10532function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10533{
10534 global $conf, $langs;
10535
10536 $ret = 0;
10537 $return = '';
10538 $out = '';
10539 $divstart = $divend = '';
10540
10541 // If inline message with no format, we add it.
10542 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10543 $divstart = '<div class="'.$style.' clearboth">';
10544 $divend = '</div>';
10545 }
10546
10547 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10548 $langs->load("errors");
10549 $out .= $divstart;
10550 if (is_array($mesgarray) && count($mesgarray)) {
10551 foreach ($mesgarray as $message) {
10552 $ret++;
10553 $out .= $langs->trans($message);
10554 if ($ret < count($mesgarray)) {
10555 $out .= "<br>\n";
10556 }
10557 }
10558 }
10559 if ($mesgstring) {
10560 $ret++;
10561 $out .= $langs->trans($mesgstring);
10562 }
10563 $out .= $divend;
10564 }
10565
10566 if ($out) {
10567 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10568 $return = '<script nonce="'.getNonce().'">
10569 $(document).ready(function() {
10570 /* jnotify(message, preset of message type, keepmessage) */
10571 $.jnotify("'.dol_escape_js($out).'", "'.($style == "ok" ? 3000 : $style).'", '.($style == "ok" ? "false" : "true").',{ remove: function (){} } );
10572 });
10573 </script>';
10574 } else {
10575 $return = $out;
10576 }
10577 }
10578
10579 return $return;
10580}
10581
10593function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10594{
10595 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10596}
10597
10611function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10612{
10613 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10614 return;
10615 }
10616
10617 $iserror = 0;
10618 $iswarning = 0;
10619 if (is_array($mesgarray)) {
10620 foreach ($mesgarray as $val) {
10621 if ($val && preg_match('/class="error"/i', $val)) {
10622 $iserror++;
10623 break;
10624 }
10625 if ($val && preg_match('/class="warning"/i', $val)) {
10626 $iswarning++;
10627 break;
10628 }
10629 }
10630 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10631 $iserror++;
10632 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10633 $iswarning++;
10634 }
10635 if ($style == 'error') {
10636 $iserror++;
10637 }
10638 if ($style == 'warning') {
10639 $iswarning++;
10640 }
10641
10642 if ($iserror || $iswarning) {
10643 // Remove div from texts
10644 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10645 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10646 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10647 // Remove div from texts array
10648 if (is_array($mesgarray)) {
10649 $newmesgarray = array();
10650 foreach ($mesgarray as $val) {
10651 if (is_string($val)) {
10652 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10653 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10654 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10655 $newmesgarray[] = $tmpmesgstring;
10656 } else {
10657 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10658 }
10659 }
10660 $mesgarray = $newmesgarray;
10661 }
10662 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10663 } else {
10664 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10665 }
10666}
10667
10679function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10680{
10681 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10682}
10683
10704function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10705{
10706 // Clean parameters
10707 $order = strtolower($order);
10708
10709 if (is_array($array)) {
10710 $sizearray = count($array);
10711 if ($sizearray > 0) {
10712 $temp = array();
10713 foreach (array_keys($array) as $key) {
10714 if (is_object($array[$key])) {
10715 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10716 } else {
10717 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10718 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10719 }
10720 if ($natsort == -1) {
10721 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10722 }
10723 }
10724
10725 if (empty($natsort) || $natsort == -1) {
10726 if ($order == 'asc') {
10727 asort($temp);
10728 } else {
10729 arsort($temp);
10730 }
10731 } else {
10732 if ($case_sensitive) {
10733 natsort($temp);
10734 } else {
10735 natcasesort($temp); // natecasesort is not sensible to case
10736 }
10737 if ($order != 'asc') {
10738 $temp = array_reverse($temp, true);
10739 }
10740 }
10741
10742 $sorted = array();
10743
10744 foreach (array_keys($temp) as $key) {
10745 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10746 }
10747
10748 return $sorted;
10749 }
10750 }
10751 return $array;
10752}
10753
10754
10762function utf8_check($str)
10763{
10764 $str = (string) $str; // Sometimes string is an int.
10765
10766 // We must use here a binary strlen function (so not dol_strlen)
10767 $strLength = strlen($str);
10768 for ($i = 0; $i < $strLength; $i++) {
10769 if (ord($str[$i]) < 0x80) {
10770 continue; // 0bbbbbbb
10771 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10772 $n = 1; // 110bbbbb
10773 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10774 $n = 2; // 1110bbbb
10775 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10776 $n = 3; // 11110bbb
10777 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10778 $n = 4; // 111110bb
10779 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10780 $n = 5; // 1111110b
10781 } else {
10782 return false; // Does not match any model
10783 }
10784 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10785 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10786 return false;
10787 }
10788 }
10789 }
10790 return true;
10791}
10792
10800function utf8_valid($str)
10801{
10802 /* 2 other methods to test if string is utf8
10803 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10804 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10805 */
10806 return preg_match('//u', $str) ? true : false;
10807}
10808
10809
10816function ascii_check($str)
10817{
10818 if (function_exists('mb_check_encoding')) {
10819 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10820 if (!mb_check_encoding($str, 'ASCII')) {
10821 return false;
10822 }
10823 } else {
10824 if (preg_match('/[^\x00-\x7f]/', $str)) {
10825 return false; // Contains a byte > 7f
10826 }
10827 }
10828
10829 return true;
10830}
10831
10832
10840function dol_osencode($str)
10841{
10842 $tmp = ini_get("unicode.filesystem_encoding");
10843 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10844 $tmp = 'iso-8859-1'; // By default for windows
10845 }
10846 if (empty($tmp)) {
10847 $tmp = 'utf-8'; // By default for other
10848 }
10849 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10850 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10851 }
10852
10853 if ($tmp == 'iso-8859-1') {
10854 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10855 }
10856 return $str;
10857}
10858
10859
10875function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10876{
10877 global $conf;
10878
10879 // If key empty
10880 if ($key == '') {
10881 return 0;
10882 }
10883
10884 // Check in cache
10885 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10886 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10887 }
10888
10889 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10890
10891 $sql = "SELECT ".$fieldid." as valuetoget";
10892 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10893 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10894 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10895 } else {
10896 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10897 }
10898 if (!empty($entityfilter)) {
10899 $sql .= " AND entity IN (".getEntity($tablename).")";
10900 }
10901 if ($filters) {
10902 $sql .= $filters;
10903 }
10904
10905 $resql = $db->query($sql);
10906 if ($resql) {
10907 $obj = $db->fetch_object($resql);
10908 $valuetoget = '';
10909 if ($obj) {
10910 $valuetoget = $obj->valuetoget;
10911 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10912 } else {
10913 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10914 }
10915 $db->free($resql);
10916
10917 return $valuetoget;
10918 } else {
10919 return -1;
10920 }
10921}
10922
10932function isStringVarMatching($var, $regextext, $matchrule = 1)
10933{
10934 if ($matchrule == 1) {
10935 if ($var == 'mainmenu') {
10936 global $mainmenu;
10937 return (preg_match('/^'.$regextext.'/', $mainmenu));
10938 } elseif ($var == 'leftmenu') {
10939 global $leftmenu;
10940 return (preg_match('/^'.$regextext.'/', $leftmenu));
10941 } else {
10942 return 'This variable is not accessible with dol_eval';
10943 }
10944 } else {
10945 return 'This value for matchrule is not implemented';
10946 }
10947}
10948
10949
10959function verifCond($strToEvaluate, $onlysimplestring = '1')
10960{
10961 //print $strToEvaluate."<br>\n";
10962 $rights = true;
10963 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10964 //var_dump($strToEvaluate);
10965 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10966 $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
10967 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10968 //var_dump($rights);
10969 }
10970 return $rights;
10971}
10972
10987function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10988{
10989 if ($returnvalue != 1) {
10990 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
10991 }
10992
10993 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
10994 return dol_eval_new($s);
10995 } else {
10996 return dol_eval_standard($s, $returnvalue, $hideerrors, $onlysimplestring);
10997 }
10998}
10999
11009function dol_eval_new($s)
11010{
11011 // Only this global variables can be read by eval function and returned to caller
11012 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11013 $db, $langs, $user, $website, $websitepage,
11014 $action, $mainmenu, $leftmenu,
11015 $mysoc,
11016 $objectoffield, // To allow the use of $objectoffield in computed fields
11017
11018 // Old variables used
11019 $object,
11020 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11021
11022 // PHP < 7.4.0
11023 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11024 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11025
11026 // PHP < 8.0.0
11027 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11028 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11029 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11030 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11031 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11032
11033 // PHP < 8.1.0
11034 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11035 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11036 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11037 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11038
11039 // PHP < 8.4.0
11040 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11041 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11042 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11043
11044 $prohibited_token_ids = [
11045 /*
11046 * Prohibited int tokens
11047 */
11048
11049 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11050 'T_ABSTRACT', 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', 'T_ATTRIBUTE',
11051 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11052 'T_BAD_CHARACTER',
11053 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11054 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11055 'T_CALLABLE', 'T_CATCH', 'T_CLASS', 'T_CLOSE_TAG', 'T_CONST',
11056 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11057 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11058 'T_DECLARE', 'T_DIR',
11059 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11060 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11061 'T_ECHO', 'T_ENDDECLARE', 'T_ENUM', 'T_EVAL', 'T_EXIT', 'T_EXTENDS',
11062 // 'T_FOR', 'T_FOREACH',
11063 'T_FILE', 'T_FINAL', 'T_FINALLY', 'T_FN', 'T_FUNCTION', 'T_FUNC_C',
11064 'T_GLOBAL', 'T_GOTO',
11065 'T_HALT_COMPILER',
11066 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11067 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11068 'T_IMPLEMENTS', 'T_INCLUDE', 'T_INCLUDE_ONCE', 'T_INSTEADOF', 'T_INTERFACE',
11069 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11070 'T_LINE',
11071 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11072 'T_METHOD_C',
11073 // 'T_NEW',
11074 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11075 'T_NAMESPACE',
11076 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11077 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11078 'T_OPEN_TAG', 'T_OPEN_TAG_WITH_ECHO',
11079 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11080 'T_PRINT', 'T_PRIVATE', 'T_PROTECTED', 'T_PUBLIC',
11081 // 'T_PROPERTY_C',
11082 'T_READONLY', 'T_REQUIRE', 'T_REQUIRE_ONCE', 'T_RETURN',
11083 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11084 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11085 'T_STATIC',
11086 'T_THROW', 'T_TRAIT', 'T_TRAIT_C', 'T_TRY',
11087 'T_UNSET', 'T_UNSET_CAST', 'T_USE',
11088 // 'T_VARIABLE',
11089 'T_VAR',
11090 // 'T_WHILE', 'T_WHITESPACE',
11091 // 'T_XOR_EQUAL',
11092 // 'T_YIELD', 'T_YIELD_FROM',
11093
11094 /*
11095 * Prohibited string tokens
11096 */
11097 ';', '`',
11098 ];
11099
11100 $prohibited_variables = [
11101 '$_COOKIE', '$_ENV', '$_FILES', '$GLOBALS', '$_GET', '$_POST', '$_REQUEST', '$_SERVER', '$_SESSION',
11102 ];
11103
11104 $prohibited_functions = [
11105 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11106 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11107 'override_function', 'session_id', 'session_create_id', 'session_regenerate_id',
11108 'call_user_func', 'call_user_func_array', // PREVENT calling forbidden functions
11109 'exec', 'passthru', 'shell_exec', 'system', 'proc_open', 'popen',
11110 'dol_eval', 'dol_eval_new', 'dol_eval_standard', 'dol_contctdesc', 'executeCLI', 'verifCond', 'GETPOST', // Native Dolibarr functions
11111 'create_function', 'assert', 'mb_ereg_replace', 'mb_eregi_replace', // function with eval capabilities
11112 'dol_compress_dir', 'dol_decode', 'dol_delete_file', 'dol_delete_dir', 'dol_delete_dir_recursive', 'dol_copy', 'archiveOrBackupFile', // more dolibarr functions
11113 'fopen', 'file_put_contents', 'fputs', 'fputscsv', 'fwrite', 'fpassthru', 'mkdir', 'rmdir', 'symlink', 'touch', 'unlink', 'umask', // PHP functions related to file operations
11114 'invoke', 'invokeArgs', // Method of ReflectionFunction to execute a function
11115 'filter_input', 'filter_input_array', 'GETPOST', // PREVENT CODE INJECTION
11116 ];
11117
11118 $prohibited_token_arrangements = [
11119 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11120 ' T_VARIABLE ( ', ' " ( ', ' \' ( ', ' T_CONSTANT_ENCAPSED_STRING ( ', ' ) ( ',
11121 ];
11122
11123 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
11124
11125 $tokens_arrangement = ' ';
11126
11127 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
11128 if (is_array($tokens[$i])) {
11129 $token_id = $tokens[$i][0];
11130 $token_value = $tokens[$i][1];
11131 $token_name = token_name($tokens[$i][0]);
11132 } else {
11133 $token_id = $tokens[$i];
11134 $token_value = $tokens[$i];
11135 $token_name = $tokens[$i];
11136 }
11137
11138 // Ignore whitespaces
11139 if (T_WHITESPACE === $token_id) {
11140 continue;
11141 }
11142
11143 // Keep history to check arrangements
11144 $tokens_arrangement .= "{$token_name} ";
11145
11146 // Prohibited Variables
11147 if (T_VARIABLE === $token_id
11148 && in_array($token_value, $prohibited_variables, true)
11149 ) {
11150 return "« {$token_value} » is prohibited in « {$s} »";
11151 }
11152
11153 // Prohibited Functions
11154 if (T_STRING === $token_id
11155 && in_array($token_value, $prohibited_functions, true)
11156 ) {
11157 return "« {$token_value} » is prohibited in « {$s} »";
11158 }
11159 }
11160
11161 // Prohibited Token IDs
11162 $maxi = count($prohibited_token_ids);
11163 for ($i = 0; $i < $maxi; ++$i) {
11164 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
11165 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
11166 }
11167 }
11168
11169 // Prohibited token arrangements
11170 $maxi = count($prohibited_token_arrangements);
11171 for ($i = 0; $i < $maxi; ++$i) {
11172 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
11173 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
11174 }
11175 }
11176
11177 // Return result
11178 try {
11179 return @eval("return {$s};") ?? '';
11180 } catch (Throwable $ex) {
11181 return "Exception during evaluation: ".$s." - ".$ex->getMessage();
11182 }
11183}
11184
11199function dol_eval_standard($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11200{
11201 // Only this global variables can be read by eval function and returned to caller
11202 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11203 global $db, $langs, $user, $website, $websitepage;
11204 global $action, $mainmenu, $leftmenu;
11205 global $mysoc;
11206 global $objectoffield; // To allow the use of $objectoffield in computed fields
11207
11208 // Old variables used (deprecated)
11209 global $object;
11210 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11211
11212 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
11213 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
11214 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
11215 }
11216 if (!is_scalar($s)) {
11217 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
11218 }
11219
11220 try {
11221 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
11222 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
11223 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
11224 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
11225 // 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"
11226
11227 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
11228 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
11229 if ($onlysimplestring == '2') {
11230 $specialcharsallowed .= '<[]';
11231 }
11232 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
11233 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
11234 }
11235 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
11236 if ($returnvalue) {
11237 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s;
11238 } else {
11239 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING);
11240 return '';
11241 }
11242 }
11243
11244 // Check if we found a ? without a space before and after
11245 $tmps = str_replace(' ? ', '__XXX__', $s);
11246 if (strpos($tmps, '?') !== false) {
11247 if ($returnvalue) {
11248 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s;
11249 } else {
11250 dol_syslog('Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s, LOG_WARNING);
11251 return '';
11252 }
11253 }
11254
11255 // Check if there is a < or <= without spaces before/after
11256 if (preg_match('/<=?[^\s]/', $s)) {
11257 if ($returnvalue) {
11258 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s;
11259 } else {
11260 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING);
11261 return '';
11262 }
11263 }
11264
11265 // Check if there is dynamic call (first we use black list patterns)
11266 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
11267 if ($returnvalue) {
11268 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;
11269 } else {
11270 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);
11271 return '';
11272 }
11273 }
11274
11275 // Now we check if we try dynamic call
11276 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
11277 $savescheck = '';
11278 $scheck = $s;
11279 while ($scheck && $savescheck != $scheck) {
11280 $savescheck = $scheck;
11281 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
11282 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
11283 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
11284 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11285 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11286 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
11287 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
11288 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
11289 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
11290 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
11291 }
11292 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
11293
11294 // Now test if it remains 1 open parenthesis.
11295 if (strpos($scheck, '(') !== false) {
11296 if ($returnvalue) {
11297 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
11298 } else {
11299 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);
11300 return '';
11301 }
11302 }
11303
11304 // TODO
11305 // We can exclude $ char that are not in dol_eval global, so that are not:
11306 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object, $obj, ...,
11307 }
11308 if ($s === 'Array') {
11309 if ($returnvalue) {
11310 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
11311 } else {
11312 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
11313 return '';
11314 }
11315 }
11316
11317 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
11318 if ($returnvalue) {
11319 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
11320 } else {
11321 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
11322 return '';
11323 }
11324 }
11325
11326 if (strpos($s, '`') !== false) {
11327 if ($returnvalue) {
11328 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
11329 } else {
11330 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
11331 return '';
11332 }
11333 }
11334
11335 // Disallow also concat
11336 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
11337 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
11338 if ($returnvalue) {
11339 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
11340 } else {
11341 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
11342 return '';
11343 }
11344 }
11345 }
11346
11347 // We block use of php exec or php file functions
11348 $forbiddenphpstrings = array('$$', '$_', '}[', ')(');
11349 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction'));
11350
11351 // 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)
11352 // 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
11353 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
11354 $forbiddenphpfunctions = array();
11355 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
11356 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
11357 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
11358
11359 $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"));
11360 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
11361 $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"));
11362 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
11363 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
11364
11365 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
11366
11367 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
11368 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
11369 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "dol_concatdesc", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
11370 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
11371 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
11372 $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
11373 $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"));
11374 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
11375 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) { // We disabllow all function that allow to obfuscate the real name of a function
11376 // @phpcs:ignore
11377 $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
11378 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concatdesc")); // native dolibarr functions
11379 }
11380
11381 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
11382
11383 $forbiddenphpregex = 'global\s*\$';
11384 $forbiddenphpregex .= '|';
11385 $forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
11386
11387 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
11388
11389 do {
11390 $oldstringtoclean = $s;
11391 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
11392 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
11393 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
11394 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
11395 } while ($oldstringtoclean != $s);
11396
11397
11398 if (strpos($s, '__forbiddenstring__') !== false) {
11399 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
11400 if ($returnvalue) {
11401 return 'Bad string syntax to evaluate: '.$s;
11402 } else {
11403 dol_syslog('Bad string syntax to evaluate: '.$s);
11404 return '';
11405 }
11406 }
11407
11408 //print $s."<br>\n";
11409 if ($returnvalue) {
11410 ob_start(); // An evaluation has no reason to output data
11411 $isObBufferActive = true;
11412 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
11413 $tmpo = ob_get_clean();
11414 $isObBufferActive = false;
11415 if ($tmpo) {
11416 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
11417 }
11418 return $tmps;
11419 } else {
11420 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
11421 if ($hideerrors) {
11422 @eval($s);
11423 } else {
11424 eval($s);
11425 }
11426 return '';
11427 }
11428 } catch (Error $e) {
11429 if ($isObBufferActive) {
11430 // Clean up buffer which was left behind due to exception.
11431 $tmpo = ob_get_clean();
11432 $isObBufferActive = false;
11433 }
11434 $error = 'dol_eval try/catch error : ';
11435 $error .= $e->getMessage();
11436 dol_syslog($error, LOG_WARNING);
11437 if ($returnvalue) {
11438 return 'Exception during evaluation: '.$s;
11439 } else {
11440 return '';
11441 }
11442 }
11443}
11444
11452function dol_validElement($element)
11453{
11454 return (trim($element) != '');
11455}
11456
11465function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
11466{
11467 if (empty($codelang)) {
11468 return '';
11469 }
11470
11471 if ($codelang == 'auto') {
11472 return '<span class="fa fa-language"></span>';
11473 }
11474
11475 $langtocountryflag = array(
11476 'ar_AR' => '',
11477 'ca_ES' => 'catalonia',
11478 'da_DA' => 'dk',
11479 'fr_CA' => 'mq',
11480 'sv_SV' => 'se',
11481 'sw_SW' => 'unknown',
11482 'AQ' => 'unknown',
11483 'CW' => 'unknown',
11484 'IM' => 'unknown',
11485 'JE' => 'unknown',
11486 'MF' => 'unknown',
11487 'BL' => 'unknown',
11488 'SX' => 'unknown'
11489 );
11490
11491 if (isset($langtocountryflag[$codelang])) {
11492 $flagImage = $langtocountryflag[$codelang];
11493 } else {
11494 $tmparray = explode('_', $codelang);
11495 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
11496 }
11497
11498 $morecss = '';
11499 $reg = array();
11500 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
11501 $morecss = $reg[1];
11502 $moreatt = "";
11503 }
11504
11505 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
11506 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
11507}
11508
11516function getLanguageCodeFromCountryCode($countrycode)
11517{
11518 global $mysoc;
11519
11520 if (empty($countrycode)) {
11521 return null;
11522 }
11523
11524 if (strtoupper($countrycode) == 'MQ') {
11525 return 'fr_CA';
11526 }
11527 if (strtoupper($countrycode) == 'SE') {
11528 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
11529 }
11530 if (strtoupper($countrycode) == 'CH') {
11531 if ($mysoc->country_code == 'FR') {
11532 return 'fr_CH';
11533 }
11534 if ($mysoc->country_code == 'DE') {
11535 return 'de_CH';
11536 }
11537 if ($mysoc->country_code == 'IT') {
11538 return 'it_CH';
11539 }
11540 }
11541
11542 // Locale list taken from:
11543 // http://stackoverflow.com/questions/3191664/
11544 // list-of-all-locales-and-their-short-codes
11545 $locales = array(
11546 'af-ZA',
11547 'am-ET',
11548 'ar-AE',
11549 'ar-BH',
11550 'ar-DZ',
11551 'ar-EG',
11552 'ar-IQ',
11553 'ar-JO',
11554 'ar-KW',
11555 'ar-LB',
11556 'ar-LY',
11557 'ar-MA',
11558 'ar-OM',
11559 'ar-QA',
11560 'ar-SA',
11561 'ar-SY',
11562 'ar-TN',
11563 'ar-YE',
11564 //'as-IN', // Moved after en-IN
11565 'ba-RU',
11566 'be-BY',
11567 'bg-BG',
11568 'bn-BD',
11569 //'bn-IN', // Moved after en-IN
11570 'bo-CN',
11571 'br-FR',
11572 'ca-ES',
11573 'co-FR',
11574 'cs-CZ',
11575 'cy-GB',
11576 'da-DK',
11577 'de-AT',
11578 'de-CH',
11579 'de-DE',
11580 'de-LI',
11581 'de-LU',
11582 'dv-MV',
11583 'el-GR',
11584 'en-AU',
11585 'en-BZ',
11586 'en-CA',
11587 'en-GB',
11588 'en-IE',
11589 'en-IN',
11590 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
11591 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
11592 'en-JM',
11593 'en-MY',
11594 'en-NZ',
11595 'en-PH',
11596 'en-SG',
11597 'en-TT',
11598 'en-US',
11599 'en-ZA',
11600 'en-ZW',
11601 'es-AR',
11602 'es-BO',
11603 'es-CL',
11604 'es-CO',
11605 'es-CR',
11606 'es-DO',
11607 'es-EC',
11608 'es-ES',
11609 'es-GT',
11610 'es-HN',
11611 'es-MX',
11612 'es-NI',
11613 'es-PA',
11614 'es-PE',
11615 'es-PR',
11616 'es-PY',
11617 'es-SV',
11618 'es-US',
11619 'es-UY',
11620 'es-VE',
11621 'et-EE',
11622 'eu-ES',
11623 'fa-IR',
11624 'fi-FI',
11625 'fo-FO',
11626 'fr-BE',
11627 'fr-CA',
11628 'fr-CH',
11629 'fr-FR',
11630 'fr-LU',
11631 'fr-MC',
11632 'fy-NL',
11633 'ga-IE',
11634 'gd-GB',
11635 'gl-ES',
11636 'gu-IN',
11637 'he-IL',
11638 'hi-IN',
11639 'hr-BA',
11640 'hr-HR',
11641 'hu-HU',
11642 'hy-AM',
11643 'id-ID',
11644 'ig-NG',
11645 'ii-CN',
11646 'is-IS',
11647 'it-CH',
11648 'it-IT',
11649 'ja-JP',
11650 'ka-GE',
11651 'kk-KZ',
11652 'kl-GL',
11653 'km-KH',
11654 'kn-IN',
11655 'ko-KR',
11656 'ky-KG',
11657 'lb-LU',
11658 'lo-LA',
11659 'lt-LT',
11660 'lv-LV',
11661 'mi-NZ',
11662 'mk-MK',
11663 'ml-IN',
11664 'mn-MN',
11665 'mr-IN',
11666 'ms-BN',
11667 'ms-MY',
11668 'mt-MT',
11669 'nb-NO',
11670 'ne-NP',
11671 'nl-BE',
11672 'nl-NL',
11673 'nn-NO',
11674 'oc-FR',
11675 'or-IN',
11676 'pa-IN',
11677 'pl-PL',
11678 'ps-AF',
11679 'pt-BR',
11680 'pt-PT',
11681 'rm-CH',
11682 'ro-MD',
11683 'ro-RO',
11684 'ru-RU',
11685 'rw-RW',
11686 'sa-IN',
11687 'se-FI',
11688 'se-NO',
11689 'se-SE',
11690 'si-LK',
11691 'sk-SK',
11692 'sl-SI',
11693 'sq-AL',
11694 'sv-FI',
11695 'sv-SE',
11696 'sw-KE',
11697 'ta-IN',
11698 'te-IN',
11699 'th-TH',
11700 'tk-TM',
11701 'tn-ZA',
11702 'tr-TR',
11703 'tt-RU',
11704 'ug-CN',
11705 'uk-UA',
11706 'ur-PK',
11707 'vi-VN',
11708 'wo-SN',
11709 'xh-ZA',
11710 'yo-NG',
11711 'zh-CN',
11712 'zh-HK',
11713 'zh-MO',
11714 'zh-SG',
11715 'zh-TW',
11716 'zu-ZA',
11717 );
11718
11719 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
11720 if (in_array($buildprimarykeytotest, $locales)) {
11721 return strtolower($countrycode).'_'.strtoupper($countrycode);
11722 }
11723
11724 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
11725 foreach ($locales as $locale) {
11726 $locale_language = locale_get_primary_language($locale);
11727 $locale_region = locale_get_region($locale);
11728 if (strtoupper($countrycode) == $locale_region) {
11729 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
11730 return strtolower($locale_language).'_'.strtoupper($locale_region);
11731 }
11732 }
11733 } else {
11734 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
11735 }
11736
11737 return null;
11738}
11739
11770function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11771{
11772 global $hookmanager, $db;
11773
11774 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11775 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11776 $values = explode(':', $value);
11777
11778 $reg = array();
11779 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11780 $newtab = array();
11781 $postab = $h;
11782 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11783 $str = $values[1];
11784 $posstart = strpos($str, '(');
11785 if ($posstart > 0) {
11786 $posend = strpos($str, ')');
11787 if ($posstart > 0) {
11788 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11789 if (is_numeric($res1)) {
11790 $postab = (int) $res1;
11791 $values[1] = '+' . substr($str, $posend + 1);
11792 }
11793 }
11794 }
11795 if (count($values) == 6) {
11796 // new declaration with permissions:
11797 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11798 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11799 if ($values[0] != $type) {
11800 continue;
11801 }
11802
11803 if (verifCond($values[4], '2')) {
11804 if ($values[3]) {
11805 if ($filterorigmodule) { // If a filter of module origin has been requested
11806 if (strpos($values[3], '@')) { // This is an external module
11807 if ($filterorigmodule != 'external') {
11808 continue;
11809 }
11810 } else { // This looks a core module
11811 if ($filterorigmodule != 'core') {
11812 continue;
11813 }
11814 }
11815 }
11816 $langs->load($values[3]);
11817 }
11818 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11819 // If label is "SUBSTITUION_..."
11820 $substitutionarray = array();
11821 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11822 $label = make_substitutions($reg[1], $substitutionarray);
11823 } else {
11824 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11825 $labeltemp = explode(',', $values[2]);
11826 $label = $langs->trans($labeltemp[0]);
11827
11828 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11829 dol_include_once($labeltemp[2]);
11830 $classtoload = $labeltemp[1];
11831 if (class_exists($classtoload)) {
11832 $obj = new $classtoload($db);
11833 $function = $labeltemp[3];
11834 if ($obj && $function && method_exists($obj, $function)) {
11835 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11836 $nbrec = $obj->$function($object->id, $obj);
11837 if (!empty($nbrec)) {
11838 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11839 }
11840 }
11841 }
11842 }
11843 }
11844
11845 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11846 $newtab[1] = $label;
11847 $newtab[2] = str_replace('+', '', $values[1]);
11848 $h++;
11849 } else {
11850 continue;
11851 }
11852 } elseif (count($values) == 5) { // case deprecated
11853 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11854
11855 if ($values[0] != $type) {
11856 continue;
11857 }
11858 if ($values[3]) {
11859 if ($filterorigmodule) { // If a filter of module origin has been requested
11860 if (strpos($values[3], '@')) { // This is an external module
11861 if ($filterorigmodule != 'external') {
11862 continue;
11863 }
11864 } else { // This looks a core module
11865 if ($filterorigmodule != 'core') {
11866 continue;
11867 }
11868 }
11869 }
11870 $langs->load($values[3]);
11871 }
11872 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11873 $substitutionarray = array();
11874 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11875 $label = make_substitutions($reg[1], $substitutionarray);
11876 } else {
11877 $label = $langs->trans($values[2]);
11878 }
11879
11880 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11881 $newtab[1] = $label;
11882 $newtab[2] = str_replace('+', '', $values[1]);
11883 $h++;
11884 }
11885 // set tab at its position
11886 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11887 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11888 if ($values[0] != $type) {
11889 continue;
11890 }
11891 $tabname = str_replace('-', '', $values[1]);
11892 foreach ($head as $key => $val) {
11893 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11894 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11895 if ($head[$key][2] == $tabname && $condition) {
11896 unset($head[$key]);
11897 break;
11898 }
11899 }
11900 }
11901 }
11902 }
11903
11904 // No need to make a return $head. Var is modified as a reference
11905 if (!empty($hookmanager)) {
11906 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11907 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
11908 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11909 if ($reshook > 0) { // Hook ask to replace completely the array
11910 $head = $hookmanager->resArray;
11911 } else { // Hook
11912 $head = array_merge($head, $hookmanager->resArray);
11913 }
11914 $h = count($head);
11915 }
11916}
11917
11929function printCommonFooter($zone = 'private')
11930{
11931 global $conf, $hookmanager, $user, $langs;
11932 global $debugbar;
11933 global $action;
11934 global $micro_start_time;
11935
11936 if ($zone == 'private') {
11937 print "\n".'<!-- Common footer for private page -->'."\n";
11938 } else {
11939 print "\n".'<!-- Common footer for public page -->'."\n";
11940 }
11941
11942 // A div to store page_y POST parameter so we can read it using javascript
11943 print "\n<!-- A div to store page_y POST parameter -->\n";
11944 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11945
11946 $parameters = array();
11947 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11948 if (empty($reshook)) {
11949 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11950 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11951 }
11952
11953 print "\n";
11954 if (!empty($conf->use_javascript_ajax)) {
11955 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11956 print '<script>'."\n";
11957 print 'jQuery(document).ready(function() {'."\n";
11958
11959 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11960 print "\n";
11961 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11962 print 'jQuery("li.menuhider").click(function(event) {';
11963 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11964 print ' console.log("We click on .menuhider");'."\n";
11965 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11966 print '});'."\n";
11967 }
11968
11969 // Management of focus and mandatory for fields
11970 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"])))) {
11971 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11972 $relativepathstring = $_SERVER["PHP_SELF"];
11973 // Clean $relativepathstring
11974 if (constant('DOL_URL_ROOT')) {
11975 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11976 }
11977 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11978 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11979 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11980
11981 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11982 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11983 $qualified = 0;
11984 if ($defkey != '_noquery_') {
11985 $tmpqueryarraytohave = explode('&', $defkey);
11986 $foundintru = 0;
11987 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11988 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11989 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11990 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11991 $foundintru = 1;
11992 }
11993 }
11994 if (!$foundintru) {
11995 $qualified = 1;
11996 }
11997 //var_dump($defkey.'-'.$qualified);
11998 } else {
11999 $qualified = 1;
12000 }
12001
12002 if ($qualified) {
12003 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
12004 foreach ($defval as $paramkey => $paramval) {
12005 // Set focus on field
12006 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
12007 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
12008 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
12009 }
12010 }
12011 }
12012 }
12013 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12014 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12015 $qualified = 0;
12016 if ($defkey != '_noquery_') {
12017 $tmpqueryarraytohave = explode('&', $defkey);
12018 $foundintru = 0;
12019 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12020 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12021 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12022 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12023 $foundintru = 1;
12024 }
12025 }
12026 if (!$foundintru) {
12027 $qualified = 1;
12028 }
12029 //var_dump($defkey.'-'.$qualified);
12030 } else {
12031 $qualified = 1;
12032 }
12033
12034 if ($qualified) {
12035 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
12036
12037 foreach ($defval as $paramkey => $paramval) {
12038 // Solution 1: Add handler on submit to check if mandatory fields are empty
12039 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12040 print "form.on('submit', function(event) {
12041 var submitter = \$(this).find(':submit:focus').get(0);
12042 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12043
12044 if (buttonName == 'cancel') {
12045 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12046 return true;
12047 }
12048
12049 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');
12050
12051 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12052 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12053
12054 if (tmptypefield == 'textarea') {
12055 // We must instead check the content of ckeditor
12056 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
12057 if (tmpeditor) {
12058 tmpvalue = tmpeditor.getData();
12059 console.log('For textarea tmpvalue is '+tmpvalue);
12060 }
12061 }
12062
12063 let tmpvalueisempty = false;
12064 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
12065 tmpvalueisempty = true;
12066 }
12067 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
12068 tmpvalueisempty = true;
12069 }
12070 if (tmpvalueisempty && buttonName !== 'cancel') {
12071 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
12072 event.preventDefault(); // Stop submission of form to allow custom code to decide.
12073 event.stopPropagation(); // Stop other handlers.
12074
12075 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
12076
12077 return false;
12078 }
12079 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
12080 return true;
12081 });
12082 \n";
12083
12084 // Solution 2: Add property 'required' on input
12085 // so browser will check value and try to focus on it when submitting the form.
12086 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
12087 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12088 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12089 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
12090 //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";
12091 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
12092 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
12093 // Add 'field required' class on closest td for all input elements : input, textarea and select
12094 //print '}, 500);'; // 500 milliseconds delay
12095
12096 // Now set the class "fieldrequired"
12097 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
12098 }
12099
12100 // If we submit using the cancel button, we remove the required attributes
12101 print 'jQuery("input[name=\'cancel\']").click(function() {
12102 console.log("We click on cancel button so removed all required attribute");
12103 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
12104 });'."\n";
12105 }
12106 }
12107 }
12108 }
12109
12110 print '});'."\n";
12111
12112 // End of tuning
12113 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
12114 print "\n";
12115 print "/* JS CODE TO ENABLE to add memory info */\n";
12116 print 'window.console && console.log("';
12117 if (getDolGlobalString('MEMCACHED_SERVER')) {
12118 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
12119 }
12120 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
12121 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
12122 $micro_end_time = microtime(true);
12123 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
12124 }
12125
12126 if (function_exists("memory_get_usage")) {
12127 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
12128 }
12129 if (function_exists("memory_get_peak_usage")) {
12130 print ' - Real mem peak: '.memory_get_peak_usage(true);
12131 }
12132 if (function_exists("zend_loader_file_encoded")) {
12133 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
12134 }
12135 print '");'."\n";
12136 }
12137
12138 print "\n".'</script>'."\n";
12139
12140 // Google Analytics
12141 // TODO Add a hook here
12142 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
12143 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
12144 foreach ($tmptagarray as $tmptag) {
12145 print "\n";
12146 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
12147 print '
12148 <!-- Global site tag (gtag.js) - Google Analytics -->
12149 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
12150 <script>
12151 window.dataLayer = window.dataLayer || [];
12152 function gtag(){dataLayer.push(arguments);}
12153 gtag(\'js\', new Date());
12154
12155 gtag(\'config\', \''.trim($tmptag).'\');
12156 </script>';
12157 print "\n";
12158 }
12159 }
12160 }
12161
12162 // Add Xdebug coverage of code
12163 if (defined('XDEBUGCOVERAGE')) {
12164 print_r(xdebug_get_code_coverage());
12165 }
12166
12167 // Add DebugBar data
12168 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
12169 if (isset($debugbar['time'])) {
12170 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12171 $debugbar['time']->stopMeasure('pageaftermaster');
12172 }
12173 print '<!-- Output debugbar data -->'."\n";
12174 $renderer = $debugbar->getJavascriptRenderer();
12175 print $renderer->render();
12176 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
12177 print "\n";
12178 print "<!-- Start of log output\n";
12179 //print '<div class="hidden">'."\n";
12180 foreach ($conf->logbuffer as $logline) {
12181 print $logline."<br>\n";
12182 }
12183 //print '</div>'."\n";
12184 print "End of log output -->\n";
12185 }
12186 }
12187}
12188
12198function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
12199{
12200 if (is_null($string)) {
12201 return array();
12202 }
12203
12204 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
12205 // This is a regex string
12206 $newdelimiter = $delimiter;
12207 } else {
12208 // This is a simple string
12209 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
12210 $newdelimiter = preg_quote($delimiter, '/');
12211 }
12212
12213 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
12214 $ka = array();
12215 foreach ($a as $s) { // each part
12216 if ($s) {
12217 if ($pos = strpos($s, $kv)) { // key/value delimiter
12218 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
12219 } else { // key delimiter not found
12220 $ka[] = trim($s);
12221 }
12222 }
12223 }
12224 return $ka;
12225 }
12226
12227 return array();
12228}
12229
12237function dolExplodeKeepIfQuotes($input)
12238{
12239 // Use regexp to capture words and section in quotes
12240 $matches = array();
12241 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
12242
12243 // Merge result and delete empty values
12244
12245 $result = array_map(
12252 static function ($a, $b, $c) {
12253 if ($a !== '') {
12254 return $a;
12255 }
12256 if ($b !== '') {
12257 return $b;
12258 }
12259 if ($c !== '') {
12260 return $c;
12261 }
12262 return '';
12263 },
12264 $matches[1],
12265 $matches[2],
12266 $matches[3]
12267 );
12268 return array_values(array_filter(
12269 $result,
12276 static function ($val) {
12277 return $val !== '';
12278 }
12279 ));
12280}
12281
12282
12289function dol_set_focus($selector)
12290{
12291 print "\n".'<!-- Set focus onto a specific field -->'."\n";
12292 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
12293}
12294
12295
12303function dol_getmypid()
12304{
12305 if (!function_exists('getmypid')) {
12306 return mt_rand(99900000, 99965535);
12307 } else {
12308 return getmypid(); // May be a number on 64 bits (depending on OS)
12309 }
12310}
12311
12333function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
12334{
12335 global $db, $langs;
12336
12337 $value = trim($value);
12338
12339 if ($mode == 0) {
12340 $value = preg_replace('/\*/', '%', $value); // Replace * with %
12341 }
12342 if ($mode == 1) {
12343 $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
12344 }
12345
12346 $value = preg_replace('/\s*\|\s*/', '|', $value);
12347
12348 // Split criteria on ' ' but not if we are inside quotes.
12349 // For mode 3, the split is done later on the , only and not on the ' '.
12350 if ($mode != -3 && $mode != 3) {
12351 $crits = dolExplodeKeepIfQuotes($value);
12352 } else {
12353 $crits = array($value);
12354 }
12355
12356 $res = '';
12357 if (!is_array($fields)) {
12358 $fields = array($fields);
12359 }
12360 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
12361 foreach ($crits as $crit) { // Loop on each AND criteria
12362 $crit = trim($crit);
12363 $i2 = 0; // count the nb of valid criteria added for this this first criteria
12364 $newres = '';
12365
12366 foreach ($fields as $field) {
12367 if ($mode == 1) {
12368 $tmpcrits = explode('|', $crit);
12369 $i3 = 0; // count the nb of valid criteria added for this current field
12370 foreach ($tmpcrits as $tmpcrit) {
12371 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12372 continue;
12373 }
12374 $tmpcrit = trim($tmpcrit);
12375
12376 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12377
12378 $operator = '=';
12379 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
12380
12381 $reg = array();
12382 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
12383 if (!empty($reg[1])) {
12384 $operator = $reg[1];
12385 }
12386 if ($newcrit != '') {
12387 $numnewcrit = price2num($newcrit);
12388 if (is_numeric($numnewcrit)) {
12389 $newres .= $db->sanitize($field).' '.$operator.' '.((float) $numnewcrit); // should be a numeric
12390 } else {
12391 $newres .= '1 = 2'; // force false, we received a corrupted data
12392 }
12393 $i3++; // a criteria was added to string
12394 }
12395 }
12396 $i2++; // a criteria for 1 more field was added to string
12397 } elseif ($mode == 2 || $mode == -2) {
12398 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
12399 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -2 ? 'NOT ' : '');
12400 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
12401 if ($mode == -2) {
12402 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12403 }
12404 $i2++; // a criteria for 1 more field was added to string
12405 } elseif ($mode == 3 || $mode == -3) {
12406 $tmparray = explode(',', $crit);
12407 if (count($tmparray)) {
12408 $listofcodes = '';
12409 foreach ($tmparray as $val) {
12410 $val = trim($val);
12411 if ($val) {
12412 $listofcodes .= ($listofcodes ? ',' : '');
12413 $listofcodes .= "'".$db->escape($val)."'";
12414 }
12415 }
12416 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
12417 $i2++; // a criteria for 1 more field was added to string
12418 }
12419 if ($mode == -3) {
12420 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12421 }
12422 } elseif ($mode == 4) {
12423 $tmparray = explode(',', $crit);
12424 if (count($tmparray)) {
12425 $listofcodes = '';
12426 foreach ($tmparray as $val) {
12427 $val = trim($val);
12428 if ($val) {
12429 $newres .= ($i2 > 0 ? " OR (" : "(").$db->sanitize($field)." LIKE '".$db->escape($val).",%'";
12430 $newres .= ' OR '.$db->sanitize($field)." = '".$db->escape($val)."'";
12431 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val)."'";
12432 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val).",%'";
12433 $newres .= ')';
12434 $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)
12435 }
12436 }
12437 }
12438 } else { // $mode=0
12439 $tmpcrits = explode('|', $crit);
12440 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
12441 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
12442 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12443 continue;
12444 }
12445 $tmpcrit = trim($tmpcrit);
12446
12447 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
12448 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
12449 } else {
12450 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12451 }
12452
12453 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
12454 $newres .= $db->sanitize($field)." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
12455 } else {
12456 $tmpcrit2 = $tmpcrit;
12457 $tmpbefore = '%';
12458 $tmpafter = '%';
12459 $tmps = '';
12460
12461 if (preg_match('/^!/', $tmpcrit)) {
12462 $tmps .= $db->sanitize($field)." NOT LIKE "; // ! as exclude character
12463 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
12464 } else {
12465 $tmps .= $db->sanitize($field)." LIKE ";
12466 }
12467 $tmps .= "'";
12468
12469 if (preg_match('/^[\^\$]/', $tmpcrit)) {
12470 $tmpbefore = '';
12471 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
12472 }
12473 if (preg_match('/[\^\$]$/', $tmpcrit)) {
12474 $tmpafter = '';
12475 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
12476 }
12477
12478 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12479 $tmps = "(".$tmps;
12480 }
12481 $newres .= $tmps;
12482 $newres .= $tmpbefore;
12483 $newres .= $db->escape($tmpcrit2);
12484 $newres .= $tmpafter;
12485 $newres .= "'";
12486 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12487 $newres .= " OR ".$field." IS NULL)";
12488 }
12489 }
12490
12491 $i3++;
12492 }
12493
12494 $i2++; // a criteria for 1 more field was added to string
12495 }
12496 }
12497 if ($newres) {
12498 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
12499 }
12500 $i1++;
12501 }
12502 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
12503
12504 return $res;
12505}
12506
12513function showDirectDownloadLink($object)
12514{
12515 global $langs;
12516
12517 $out = '';
12518 $url = $object->getLastMainDocLink($object->element);
12519
12520 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
12521 if ($url) {
12522 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
12523 $out .= ajax_autoselect("directdownloadlink", '');
12524 } else {
12525 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
12526 }
12527
12528 return $out;
12529}
12530
12539function getImageFileNameForSize($file, $extName, $extImgTarget = '')
12540{
12541 $dirName = dirname($file);
12542 if ($dirName == '.') {
12543 $dirName = '';
12544 }
12545
12546 if (!in_array($extName, array('', '_small', '_mini'))) {
12547 return 'Bad parameter extName';
12548 }
12549
12550 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
12551 $fileName = basename($fileName);
12552
12553 if (empty($extImgTarget)) {
12554 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
12555 }
12556 if (empty($extImgTarget)) {
12557 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
12558 }
12559 if (empty($extImgTarget)) {
12560 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
12561 }
12562 if (empty($extImgTarget)) {
12563 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
12564 }
12565 if (empty($extImgTarget)) {
12566 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
12567 }
12568 if (empty($extImgTarget)) {
12569 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
12570 }
12571
12572 if (!$extImgTarget) {
12573 return $file;
12574 }
12575
12576 $subdir = '';
12577 if ($extName) {
12578 $subdir = 'thumbs/';
12579 }
12580
12581 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
12582}
12583
12584
12594function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
12595{
12596 global $conf, $langs;
12597
12598 if (empty($conf->use_javascript_ajax)) {
12599 return '';
12600 }
12601
12602 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
12603
12604 if ($alldata == 1) {
12605 if ($isAllowedForPreview) {
12606 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));
12607 } else {
12608 return array();
12609 }
12610 }
12611
12612 // old behavior, return a string
12613 if ($isAllowedForPreview) {
12614 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
12615 $title = $langs->transnoentities("Preview");
12616 //$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().
12617 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
12618
12619 // 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,
12620 // 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.
12621 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
12622 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
12623 } else {
12624 return '';
12625 }
12626}
12627
12634function getLabelSpecialCode($idcode)
12635{
12636 global $langs;
12637
12638 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
12639 if ($idcode > 10) {
12640 return 'Module ID '.$idcode;
12641 }
12642 if (!empty($arrayspecialines[$idcode])) {
12643 return $langs->trans($arrayspecialines[$idcode]);
12644 }
12645 return '';
12646}
12647
12656function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
12657{
12658 global $langs;
12659 $out = '<script nonce="'.getNonce().'">
12660 jQuery(document).ready(function () {
12661 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
12662 });
12663 </script>';
12664 if ($addlink) {
12665 if ($textonlink === 'image') {
12666 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
12667 } else {
12668 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
12669 }
12670 }
12671 return $out;
12672}
12673
12681function dolIsAllowedForPreview($file)
12682{
12683 // Check .noexe extension in filename
12684 if (preg_match('/\.noexe$/i', $file)) {
12685 return 0;
12686 }
12687
12688 // Check mime types
12689 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
12690 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
12691 $mime_preview[] = 'svg+xml';
12692 }
12693 //$mime_preview[]='vnd.oasis.opendocument.presentation';
12694 //$mime_preview[]='archive';
12695 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
12696 if ($num_mime !== false) {
12697 return 1;
12698 }
12699
12700 // By default, not allowed for preview
12701 return 0;
12702}
12703
12704
12714function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
12715{
12716 $mime = $default;
12717 $imgmime = 'other.png';
12718 $famime = 'file-o';
12719 $srclang = '';
12720
12721 $tmpfile = preg_replace('/\.noexe$/', '', $file);
12722
12723 // Plain text files
12724 if (preg_match('/\.txt$/i', $tmpfile)) {
12725 $mime = 'text/plain';
12726 $imgmime = 'text.png';
12727 $famime = 'file-alt';
12728 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
12729 $mime = 'text/richtext';
12730 $imgmime = 'text.png';
12731 $famime = 'file-alt';
12732 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
12733 $mime = 'text/csv';
12734 $imgmime = 'text.png';
12735 $famime = 'file-csv';
12736 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
12737 $mime = 'text/tab-separated-values';
12738 $imgmime = 'text.png';
12739 $famime = 'file-alt';
12740 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
12741 $mime = 'text/plain';
12742 $imgmime = 'text.png';
12743 $famime = 'file-alt';
12744 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
12745 $mime = 'text/plain';
12746 $imgmime = 'text.png';
12747 $srclang = 'ini';
12748 $famime = 'file-alt';
12749 } elseif (preg_match('/\.md$/i', $tmpfile)) {
12750 $mime = 'text/plain';
12751 $imgmime = 'text.png';
12752 $srclang = 'md';
12753 $famime = 'file-alt';
12754 } elseif (preg_match('/\.css$/i', $tmpfile)) {
12755 $mime = 'text/css';
12756 $imgmime = 'css.png';
12757 $srclang = 'css';
12758 $famime = 'file-alt';
12759 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
12760 $mime = 'text/plain';
12761 $imgmime = 'text.png';
12762 $srclang = 'lang';
12763 $famime = 'file-alt';
12764 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
12765 $mime = 'text/plain';
12766 $imgmime = 'text.png';
12767 $famime = 'file-alt';
12768 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
12769 $mime = 'text/html';
12770 $imgmime = 'html.png';
12771 $srclang = 'html';
12772 $famime = 'file-alt';
12773 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
12774 $mime = 'text/xml';
12775 $imgmime = 'other.png';
12776 $srclang = 'xml';
12777 $famime = 'file-alt';
12778 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
12779 $mime = 'text/xml';
12780 $imgmime = 'other.png';
12781 $srclang = 'xaml';
12782 $famime = 'file-alt';
12783 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
12784 $mime = 'text/plain';
12785 $imgmime = 'text.png';
12786 $srclang = 'bas';
12787 $famime = 'file-code';
12788 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
12789 $mime = 'text/plain';
12790 $imgmime = 'text.png';
12791 $srclang = 'c';
12792 $famime = 'file-code';
12793 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
12794 $mime = 'text/plain';
12795 $imgmime = 'text.png';
12796 $srclang = 'cpp';
12797 $famime = 'file-code';
12798 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
12799 $mime = 'text/plain';
12800 $imgmime = 'text.png';
12801 $srclang = 'cs';
12802 $famime = 'file-code';
12803 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
12804 $mime = 'text/plain';
12805 $imgmime = 'text.png';
12806 $srclang = 'h';
12807 $famime = 'file-code';
12808 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
12809 $mime = 'text/plain';
12810 $imgmime = 'text.png';
12811 $srclang = 'java';
12812 $famime = 'file-code';
12813 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
12814 $mime = 'text/plain';
12815 $imgmime = 'php.png';
12816 $srclang = 'php';
12817 $famime = 'file-code';
12818 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12819 $mime = 'text/plain';
12820 $imgmime = 'php.png';
12821 $srclang = 'php';
12822 $famime = 'file-code';
12823 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12824 $mime = 'text/plain';
12825 $imgmime = 'pl.png';
12826 $srclang = 'perl';
12827 $famime = 'file-code';
12828 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12829 $mime = 'text/plain';
12830 $imgmime = 'text.png';
12831 $srclang = 'sql';
12832 $famime = 'file-code';
12833 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12834 $mime = 'text/x-javascript';
12835 $imgmime = 'jscript.png';
12836 $srclang = 'js';
12837 $famime = 'file-code';
12838 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12839 $mime = 'application/vnd.oasis.opendocument.presentation';
12840 $imgmime = 'ooffice.png';
12841 $famime = 'file-powerpoint';
12842 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12843 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12844 $imgmime = 'ooffice.png';
12845 $famime = 'file-excel';
12846 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12847 $mime = 'application/vnd.oasis.opendocument.text';
12848 $imgmime = 'ooffice.png';
12849 $famime = 'file-word';
12850 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12851 $mime = 'application/msaccess';
12852 $imgmime = 'mdb.png';
12853 $famime = 'file';
12854 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12855 $mime = 'application/msword';
12856 $imgmime = 'doc.png';
12857 $famime = 'file-word';
12858 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12859 $mime = 'application/msword';
12860 $imgmime = 'doc.png';
12861 $famime = 'file-word';
12862 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12863 $mime = 'application/vnd.ms-excel';
12864 $imgmime = 'xls.png';
12865 $famime = 'file-excel';
12866 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12867 $mime = 'application/vnd.ms-excel';
12868 $imgmime = 'xls.png';
12869 $famime = 'file-excel';
12870 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12871 $mime = 'application/vnd.ms-excel';
12872 $imgmime = 'xls.png';
12873 $famime = 'file-excel';
12874 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12875 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12876 $imgmime = 'xls.png';
12877 $famime = 'file-excel';
12878 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12879 $mime = 'application/vnd.ms-powerpoint';
12880 $imgmime = 'ppt.png';
12881 $famime = 'file-powerpoint';
12882 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12883 $mime = 'application/x-mspowerpoint';
12884 $imgmime = 'ppt.png';
12885 $famime = 'file-powerpoint';
12886 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12887 $mime = 'application/pdf';
12888 $imgmime = 'pdf.png';
12889 $famime = 'file-pdf';
12890 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12891 $mime = 'text/x-bat';
12892 $imgmime = 'script.png';
12893 $srclang = 'dos';
12894 $famime = 'file-code';
12895 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12896 $mime = 'text/x-sh';
12897 $imgmime = 'script.png';
12898 $srclang = 'bash';
12899 $famime = 'file-code';
12900 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12901 $mime = 'text/x-ksh';
12902 $imgmime = 'script.png';
12903 $srclang = 'bash';
12904 $famime = 'file-code';
12905 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12906 $mime = 'text/x-bash';
12907 $imgmime = 'script.png';
12908 $srclang = 'bash';
12909 $famime = 'file-code';
12910 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12911 $mime = 'image/x-icon';
12912 $imgmime = 'image.png';
12913 $famime = 'file-image';
12914 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12915 $mime = 'image/jpeg';
12916 $imgmime = 'image.png';
12917 $famime = 'file-image';
12918 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12919 $mime = 'image/png';
12920 $imgmime = 'image.png';
12921 $famime = 'file-image';
12922 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12923 $mime = 'image/gif';
12924 $imgmime = 'image.png';
12925 $famime = 'file-image';
12926 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12927 $mime = 'image/bmp';
12928 $imgmime = 'image.png';
12929 $famime = 'file-image';
12930 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12931 $mime = 'image/tiff';
12932 $imgmime = 'image.png';
12933 $famime = 'file-image';
12934 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12935 $mime = 'image/svg+xml';
12936 $imgmime = 'image.png';
12937 $famime = 'file-image';
12938 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12939 $mime = 'image/webp';
12940 $imgmime = 'image.png';
12941 $famime = 'file-image';
12942 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12943 $mime = 'text/calendar';
12944 $imgmime = 'other.png';
12945 $famime = 'file-alt';
12946 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12947 $mime = 'text/calendar';
12948 $imgmime = 'other.png';
12949 $famime = 'file-alt';
12950 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12951 $mime = 'application/x-bittorrent';
12952 $imgmime = 'other.png';
12953 $famime = 'file-o';
12954 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12955 $mime = 'audio';
12956 $imgmime = 'audio.png';
12957 $famime = 'file-audio';
12958 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12959 $mime = 'video/mp4';
12960 $imgmime = 'video.png';
12961 $famime = 'file-video';
12962 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12963 $mime = 'video/ogg';
12964 $imgmime = 'video.png';
12965 $famime = 'file-video';
12966 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12967 $mime = 'video/webm';
12968 $imgmime = 'video.png';
12969 $famime = 'file-video';
12970 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12971 $mime = 'video/x-msvideo';
12972 $imgmime = 'video.png';
12973 $famime = 'file-video';
12974 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12975 $mime = 'video/divx';
12976 $imgmime = 'video.png';
12977 $famime = 'file-video';
12978 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12979 $mime = 'video/xvid';
12980 $imgmime = 'video.png';
12981 $famime = 'file-video';
12982 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12983 $mime = 'video';
12984 $imgmime = 'video.png';
12985 $famime = 'file-video';
12986 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12987 // application/xxx where zzz is zip, ...
12988 $mime = 'archive';
12989 $imgmime = 'archive.png';
12990 $famime = 'file-archive';
12991 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12992 $mime = 'application/octet-stream';
12993 $imgmime = 'other.png';
12994 $famime = 'file-o';
12995 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12996 $mime = 'library';
12997 $imgmime = 'library.png';
12998 $famime = 'file-o';
12999 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13000 $mime = 'error';
13001 $imgmime = 'error.png';
13002 $famime = 'file-alt';
13003 }
13004
13005 if ($famime == 'file-o') {
13006 // file-o seems to not work in fontawesome 5
13007 $famime = 'file';
13008 }
13009
13010 // Return mimetype string
13011 switch ((int) $mode) {
13012 case 1:
13013 $tmp = explode('/', $mime);
13014 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13015 case 2:
13016 return $imgmime;
13017 case 3:
13018 return $srclang;
13019 case 4:
13020 return $famime;
13021 }
13022 return $mime;
13023}
13024
13036function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
13037{
13038 global $conf, $db;
13039
13040 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
13041
13042 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
13043
13044 if (is_null($dictvalues)) {
13045 $dictvalues = array();
13046
13047 $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
13048 if ($checkentity) {
13049 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
13050 }
13051
13052 $resql = $db->query($sql);
13053 if ($resql) {
13054 while ($obj = $db->fetch_object($resql)) {
13055 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
13056 }
13057 } else {
13058 dol_print_error($db);
13059 }
13060
13061 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
13062 }
13063
13064 if (!empty($dictvalues[$id])) {
13065 // Found
13066 $tmp = $dictvalues[$id];
13067 return (property_exists($tmp, $field) ? $tmp->$field : '');
13068 } else {
13069 // Not found
13070 return '';
13071 }
13072}
13073
13080function colorIsLight($stringcolor)
13081{
13082 $stringcolor = str_replace('#', '', $stringcolor);
13083 $res = -1;
13084 if (!empty($stringcolor)) {
13085 $res = 0;
13086 $tmp = explode(',', $stringcolor);
13087 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
13088 $r = $tmp[0];
13089 $g = $tmp[1];
13090 $b = $tmp[2];
13091 } else {
13092 $hexr = $stringcolor[0].$stringcolor[1];
13093 $hexg = $stringcolor[2].$stringcolor[3];
13094 $hexb = $stringcolor[4].$stringcolor[5];
13095 $r = hexdec($hexr);
13096 $g = hexdec($hexg);
13097 $b = hexdec($hexb);
13098 }
13099 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
13100 if ($bright > 0.6) {
13101 $res = 1;
13102 }
13103 }
13104 return $res;
13105}
13106
13115function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
13116{
13117 global $conf;
13118
13119 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
13120 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
13121 if (empty($menuentry['enabled'])) {
13122 return 0; // Entry disabled by condition
13123 }
13124 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
13125 $tmploops = explode('|', $menuentry['module']);
13126 $found = 0;
13127 foreach ($tmploops as $tmploop) {
13128 if (in_array($tmploop, $listofmodulesforexternal)) {
13129 $found++;
13130 break;
13131 }
13132 }
13133 if (!$found) {
13134 return 0; // Entry is for menus all excluded to external users
13135 }
13136 }
13137 if (!$menuentry['perms'] && $type_user) {
13138 return 0; // No permissions and user is external
13139 }
13140 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
13141 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
13142 }
13143 if (!$menuentry['perms']) {
13144 return 2; // No permissions and user is external
13145 }
13146 return 1;
13147}
13148
13156function roundUpToNextMultiple($n, $x = 5)
13157{
13158 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
13159 return (int) $result;
13160}
13161
13173function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
13174{
13175 $csstouse = 'badge';
13176 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
13177 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
13178 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
13179
13180 $attr = array(
13181 'class' => $csstouse
13182 );
13183
13184 if (empty($html)) {
13185 $html = $label;
13186 }
13187
13188 if (!empty($url)) {
13189 $attr['href'] = $url;
13190 }
13191
13192 if ($mode === 'dot') {
13193 $attr['class'] .= ' classfortooltip';
13194 $attr['title'] = $html;
13195 $attr['aria-label'] = $label;
13196 $html = '';
13197 }
13198
13199 // Override attr
13200 if (!empty($params['attr']) && is_array($params['attr'])) {
13201 foreach ($params['attr'] as $key => $value) {
13202 if ($key == 'class') {
13203 $attr['class'] .= ' '.$value;
13204 } elseif ($key == 'classOverride') {
13205 $attr['class'] = $value;
13206 } else {
13207 $attr[$key] = $value;
13208 }
13209 }
13210 }
13211
13212 // TODO: add hook
13213
13214 // escape all attribute
13215 $attr = array_map('dolPrintHTMLForAttribute', $attr);
13216
13217 $TCompiledAttr = array();
13218 foreach ($attr as $key => $value) {
13219 $TCompiledAttr[] = $key.'="'.$value.'"';
13220 }
13221
13222 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
13223
13224 $tag = !empty($url) ? 'a' : 'span';
13225
13226 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
13227}
13228
13229
13242function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
13243{
13244 global $conf;
13245
13246 $return = '';
13247 $dolGetBadgeParams = array();
13248
13249 if (!empty($params['badgeParams'])) {
13250 $dolGetBadgeParams = $params['badgeParams'];
13251 }
13252
13253 // TODO : add a hook
13254 if ($displayMode == 0) {
13255 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
13256 } elseif ($displayMode == 1) {
13257 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13258 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
13259 // Use status with images (for backward compatibility)
13260 $return = '';
13261 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
13262 $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>' : '');
13263
13264 // For small screen, we always use the short label instead of long label.
13265 if (!empty($conf->dol_optimize_smallscreen)) {
13266 if ($displayMode == 0) {
13267 $displayMode = 1;
13268 } elseif ($displayMode == 4) {
13269 $displayMode = 2;
13270 } elseif ($displayMode == 6) {
13271 $displayMode = 5;
13272 }
13273 }
13274
13275 // For backward compatibility. Image's filename are still in French, so we use this array to convert
13276 $statusImg = array(
13277 'status0' => 'statut0',
13278 'status1' => 'statut1',
13279 'status2' => 'statut2',
13280 'status3' => 'statut3',
13281 'status4' => 'statut4',
13282 'status5' => 'statut5',
13283 'status6' => 'statut6',
13284 'status7' => 'statut7',
13285 'status8' => 'statut8',
13286 'status9' => 'statut9'
13287 );
13288
13289 if (!empty($statusImg[$statusType])) {
13290 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
13291 } else {
13292 $htmlImg = img_picto($statusLabel, $statusType);
13293 }
13294
13295 if ($displayMode === 2) {
13296 $return = $htmlImg.' '.$htmlLabelShort;
13297 } elseif ($displayMode === 3) {
13298 $return = $htmlImg;
13299 } elseif ($displayMode === 4) {
13300 $return = $htmlImg.' '.$htmlLabel;
13301 } elseif ($displayMode === 5) {
13302 $return = $htmlLabelShort.' '.$htmlImg;
13303 } else { // $displayMode >= 6
13304 $return = $htmlLabel.' '.$htmlImg;
13305 }
13306 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
13307 // Use new badge
13308 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13309
13310 $dolGetBadgeParams['attr']['class'] = 'badge-status';
13311 if (empty($dolGetBadgeParams['attr']['title'])) {
13312 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
13313 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
13314 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
13315 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
13316 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
13317 }
13318
13319 if ($displayMode == 3) {
13320 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
13321 } elseif ($displayMode === 5) {
13322 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
13323 } else {
13324 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
13325 }
13326 }
13327
13328 return $return;
13329}
13330
13331
13367function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
13368{
13369 global $hookmanager, $action, $object, $langs;
13370
13371 // If $url is an array, we must build a dropdown button or recursively iterate over each value
13372 if (is_array($url)) {
13373 // Loop on $url array to remove entries of disabled modules
13374 foreach ($url as $key => $subbutton) {
13375 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
13376 unset($url[$key]);
13377 }
13378 }
13379
13380 $out = '';
13381
13382 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
13383 foreach ($url as $button) {
13384 if (!empty($button['lang'])) {
13385 $langs->load($button['lang']);
13386 }
13387 $label = $langs->trans($button['label']);
13388 $text = $button['text'] ?? '';
13389 $actionType = $button['actionType'] ?? '';
13390 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
13391 $id = $button['id'] ?? '';
13392 $userRight = $button['perm'] ?? 1;
13393 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
13394
13395 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
13396 }
13397 return $out;
13398 }
13399
13400 if (count($url) > 1) {
13401 $out .= '<div class="dropdown inline-block dropdown-holder">';
13402 $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>';
13403 $out .= '<div class="dropdown-content">';
13404 foreach ($url as $subbutton) {
13405 if (!empty($subbutton['lang'])) {
13406 $langs->load($subbutton['lang']);
13407 }
13408
13409 if (!empty($subbutton['urlraw'])) {
13410 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13411 } else {
13412 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13413 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13414 }
13415
13416 $subbuttonparam = array();
13417 if (!empty($subbutton['attr'])) {
13418 $subbuttonparam['attr'] = $subbutton['attr'];
13419 }
13420 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
13421
13422 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
13423 }
13424 $out .= "</div>";
13425 $out .= "</div>";
13426 } else {
13427 foreach ($url as $subbutton) { // Should loop on 1 record only
13428 if (!empty($subbutton['lang'])) {
13429 $langs->load($subbutton['lang']);
13430 }
13431
13432 if (!empty($subbutton['urlraw'])) {
13433 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13434 } else {
13435 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13436 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13437 }
13438
13439 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
13440 }
13441 }
13442
13443 return $out;
13444 }
13445
13446 // Here, $url is a simple link
13447
13448 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
13449 $class = "dropdown-item";
13450 } else {
13451 $class = 'butAction';
13452 if ($actionType == 'danger' || $actionType == 'delete') {
13453 $class = 'butActionDelete';
13454 if (!empty($url) && strpos($url, 'token=') === false) {
13455 $url .= '&token='.newToken();
13456 }
13457 }
13458 }
13459 $attr = array(
13460 'class' => $class,
13461 'href' => empty($url) ? '' : $url,
13462 'title' => $label
13463 );
13464
13465 if (empty($text)) {
13466 $text = $label;
13467 $attr['title'] = ''; // if html not set, leave label on title is redundant
13468 } else {
13469 $attr['title'] = $label;
13470 $attr['aria-label'] = $label;
13471 }
13472
13473 if (empty($userRight) || $userRight < 0) {
13474 $attr['class'] = 'butActionRefused';
13475 $attr['href'] = '';
13476 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
13477 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '').(empty($userRight) ? $langs->trans('NotEnoughPermissions') : '');
13478 }
13479
13480 if (!empty($id)) {
13481 $attr['id'] = $id;
13482 }
13483
13484 // Override attr
13485 if (!empty($params['attr']) && is_array($params['attr'])) {
13486 foreach ($params['attr'] as $key => $value) {
13487 if ($key == 'class') {
13488 $attr['class'] .= ' '.$value;
13489 } elseif ($key == 'classOverride') {
13490 $attr['class'] = $value;
13491 } else {
13492 $attr[$key] = $value;
13493 }
13494 }
13495 }
13496
13497 // automatic add tooltip when title is detected
13498 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13499 $attr['class'] .= ' classfortooltip';
13500 }
13501
13502 // Js Confirm button
13503 if ($userRight && !empty($params['confirm'])) {
13504 if (!is_array($params['confirm'])) {
13505 $params['confirm'] = array();
13506 }
13507
13508 if (empty($params['confirm']['url'])) {
13509 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
13510 }
13511
13512 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
13513 $attr['data-confirm-url'] = $params['confirm']['url'];
13514 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
13515 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
13516 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
13517 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
13518 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
13519 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
13520
13521 $attr['class'] .= ' butActionConfirm';
13522 }
13523
13524 if (isset($attr['href']) && empty($attr['href'])) {
13525 unset($attr['href']);
13526 }
13527
13528 $TCompiledAttr = array();
13529 foreach ($attr as $key => $value) {
13530 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
13531 // Not recommended
13532 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13533 } elseif ($key == 'href') {
13534 $value = dolPrintHTMLForAttributeUrl($value);
13535 } else {
13536 $value = dolPrintHTMLForAttribute($value);
13537 }
13538
13539 $TCompiledAttr[] = $key.'="'.$value.'"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13540 }
13541
13542 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
13543
13544 $tag = !empty($attr['href']) ? 'a' : 'span';
13545
13546
13547 $parameters = array(
13548 'TCompiledAttr' => $TCompiledAttr, // array
13549 'compiledAttributes' => $compiledAttributes, // string
13550 'attr' => $attr,
13551 'tag' => $tag,
13552 'label' => $label,
13553 'html' => $text,
13554 'actionType' => $actionType,
13555 'url' => $url,
13556 'id' => $id,
13557 'userRight' => $userRight,
13558 'params' => $params
13559 );
13560
13561 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
13562 if ($reshook < 0) {
13563 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
13564 }
13565
13566 if (empty($reshook)) {
13567 if (dol_textishtml($text)) { // If content already HTML encoded
13568 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
13569 } else {
13570 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
13571 }
13572 } else {
13573 return $hookmanager->resPrint;
13574 }
13575}
13576
13577
13586function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
13587{
13588 if (empty($url)) {
13589 return '';
13590 }
13591
13592 $parsedUrl = parse_url($url);
13593 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
13594 return $url;
13595 }
13596
13597 if (!empty($parsedUrl['query'])) {
13598 // Use parse_str() function to parse the string passed via URL
13599 parse_str($parsedUrl['query'], $urlQuery);
13600 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
13601 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
13602 }
13603 }
13604
13605 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
13606 $url = DOL_URL_ROOT.$url;
13607 }
13608
13609 return $url;
13610}
13611
13612
13619function dolGetButtonTitleSeparator($moreClass = "")
13620{
13621 return '<span class="button-title-separator '.$moreClass.'" ></span>';
13622}
13623
13630function getFieldErrorIcon($fieldValidationErrorMsg)
13631{
13632 $out = '';
13633 if (!empty($fieldValidationErrorMsg)) {
13634 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
13635 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
13636 $out .= '</span>';
13637 }
13638
13639 return $out;
13640}
13641
13654function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
13655{
13656 global $langs, $user;
13657
13658 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
13659 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
13660 return '';
13661 }
13662 // Fix old picto fa-th-list to use fa-grid-vertical instead
13663 if ($iconClass == 'fa fa-th-list imgforviewmode') {
13664 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
13665 }
13666
13667 $class = 'btnTitle';
13668 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
13669 $class .= ' btnTitlePlus';
13670 }
13671 $useclassfortooltip = 1;
13672
13673 if (!empty($params['morecss'])) {
13674 $class .= ' '.$params['morecss'];
13675 }
13676
13677 $attr = array(
13678 'class' => $class,
13679 'href' => empty($url) ? '' : $url
13680 );
13681
13682 if (!empty($helpText)) {
13683 $attr['title'] = $helpText;
13684 } elseif ($label) { // empty($attr['title']) &&
13685 $attr['title'] = $label;
13686 $useclassfortooltip = 0;
13687 }
13688
13689 if ($status == 2) {
13690 $attr['class'] .= ' btnTitleSelected';
13691 } elseif ($status <= 0) {
13692 $attr['class'] .= ' refused';
13693
13694 $attr['href'] = '';
13695
13696 if ($status == -1) { // disable
13697 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
13698 } elseif ($status == 0) { // Not enough permissions
13699 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
13700 }
13701 }
13702
13703 if (!empty($attr['title']) && $useclassfortooltip) {
13704 $attr['class'] .= ' classfortooltip';
13705 }
13706
13707 if (!empty($id)) {
13708 $attr['id'] = $id;
13709 }
13710
13711 // Override attr
13712 if (!empty($params['attr']) && is_array($params['attr'])) {
13713 foreach ($params['attr'] as $key => $value) {
13714 if ($key == 'class') {
13715 $attr['class'] .= ' '.$value;
13716 } elseif ($key == 'classOverride') {
13717 $attr['class'] = $value;
13718 } else {
13719 $attr[$key] = $value;
13720 }
13721 }
13722 }
13723
13724 if (isset($attr['href']) && empty($attr['href'])) {
13725 unset($attr['href']);
13726 }
13727
13728 // TODO : add a hook
13729
13730 // Generate attributes with escapement
13731 $TCompiledAttr = array();
13732 foreach ($attr as $key => $value) {
13733 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
13734 }
13735
13736 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
13737
13738 $tag = (empty($attr['href']) ? 'span' : 'a');
13739
13740 $button = '<'.$tag.' '.$compiledAttributes.'>';
13741 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
13742 if (!empty($params['forcenohideoftext'])) {
13743 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
13744 }
13745 $button .= '</'.$tag.'>';
13746
13747 return $button;
13748}
13749
13759function getElementProperties($elementType)
13760{
13761 global $conf, $db, $hookmanager;
13762
13763 $regs = array();
13764
13765 //$element_type='facture';
13766
13767 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
13768
13769 // Parse element/subelement
13770 $module = $elementType;
13771 $element = $elementType;
13772 $subelement = $elementType;
13773 $table_element = $elementType;
13774
13775 // If we ask a resource form external module (instead of default path)
13776 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
13777 $element = $subelement = $regs[1];
13778 $module = $regs[2];
13779 }
13780
13781 // If we ask a resource for a string with an element and a subelement
13782 // Example 'project_task'
13783 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
13784 $module = $element = $regs[1];
13785 $subelement = $regs[2];
13786 }
13787
13788 // Object lines will use parent classpath and module ref
13789 if (substr($elementType, -3) == 'det') {
13790 $module = preg_replace('/det$/', '', $element);
13791 $subelement = preg_replace('/det$/', '', $subelement);
13792 $classpath = $module.'/class';
13793 $classfile = $module;
13794 $classname = preg_replace('/det$/', 'Line', $element);
13795 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
13796 $classname = preg_replace('/det$/', 'Ligne', $element);
13797 }
13798 }
13799 // For compatibility and to work with non standard path
13800 if ($elementType == "action" || $elementType == "actioncomm") {
13801 $classpath = 'comm/action/class';
13802 $subelement = 'Actioncomm';
13803 $module = 'agenda';
13804 $table_element = 'actioncomm';
13805 } elseif ($elementType == 'cronjob') {
13806 $classpath = 'cron/class';
13807 $module = 'cron';
13808 $table_element = 'cron';
13809 } elseif ($elementType == 'adherent_type') {
13810 $classpath = 'adherents/class';
13811 $classfile = 'adherent_type';
13812 $module = 'adherent';
13813 $subelement = 'adherent_type';
13814 $classname = 'AdherentType';
13815 $table_element = 'adherent_type';
13816 } elseif ($elementType == 'bank_account') {
13817 $classpath = 'compta/bank/class';
13818 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13819 $classfile = 'account';
13820 $classname = 'Account';
13821 } elseif ($elementType == 'bank_line') {
13822 $classpath = 'compta/bank/class';
13823 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13824 $classfile = 'account';
13825 $classname = 'AccountLine';
13826 } elseif ($elementType == 'category') {
13827 $classpath = 'categories/class';
13828 $module = 'categorie';
13829 $subelement = 'categorie';
13830 $table_element = 'categorie';
13831 } elseif ($elementType == 'contact') {
13832 $classpath = 'contact/class';
13833 $classfile = 'contact';
13834 $module = 'societe';
13835 $subelement = 'contact';
13836 $table_element = 'socpeople';
13837 } elseif ($elementType == 'inventory') {
13838 $module = 'product';
13839 $classpath = 'product/inventory/class';
13840 } elseif ($elementType == 'inventoryline') {
13841 $module = 'product';
13842 $classpath = 'product/inventory/class';
13843 $table_element = 'inventorydet';
13844 $parent_element = 'inventory';
13845 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
13846 $module = 'stock';
13847 $classpath = 'product/stock/class';
13848 $classfile = 'entrepot';
13849 $classname = 'Entrepot';
13850 $table_element = 'entrepot';
13851 } elseif ($elementType == 'project') {
13852 $classpath = 'projet/class';
13853 $module = 'projet';
13854 $table_element = 'projet';
13855 } elseif ($elementType == 'project_task') {
13856 $classpath = 'projet/class';
13857 $module = 'projet';
13858 $subelement = 'task';
13859 $table_element = 'projet_task';
13860 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13861 $classpath = 'compta/facture/class';
13862 $module = 'facture';
13863 $subelement = 'facture';
13864 $table_element = 'facture';
13865 } elseif ($elementType == 'facturedet') {
13866 $classpath = 'compta/facture/class';
13867 $classfile = 'facture';
13868 $classname = 'FactureLigne';
13869 $module = 'facture';
13870 $table_element = 'facturedet';
13871 $parent_element = 'facture';
13872 } elseif ($elementType == 'facturerec') {
13873 $classpath = 'compta/facture/class';
13874 $classfile = 'facture-rec';
13875 $module = 'facture';
13876 $classname = 'FactureRec';
13877 } elseif ($elementType == 'commande' || $elementType == 'order') {
13878 $classpath = 'commande/class';
13879 $module = 'commande';
13880 $subelement = 'commande';
13881 $table_element = 'commande';
13882 } elseif ($elementType == 'commandedet') {
13883 $classpath = 'commande/class';
13884 $classfile = 'commande';
13885 $classname = 'OrderLine';
13886 $module = 'commande';
13887 $table_element = 'commandedet';
13888 $parent_element = 'commande';
13889 } elseif ($elementType == 'propal') {
13890 $classpath = 'comm/propal/class';
13891 $table_element = 'propal';
13892 } elseif ($elementType == 'propaldet') {
13893 $classpath = 'comm/propal/class';
13894 $classfile = 'propal';
13895 $subelement = 'propaleligne';
13896 $module = 'propal';
13897 $table_element = 'propaldet';
13898 $parent_element = 'propal';
13899 } elseif ($elementType == 'shipping') {
13900 $classpath = 'expedition/class';
13901 $classfile = 'expedition';
13902 $classname = 'Expedition';
13903 $module = 'expedition';
13904 $table_element = 'expedition';
13905 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13906 $classpath = 'expedition/class';
13907 $classfile = 'expedition';
13908 $classname = 'ExpeditionLigne';
13909 $module = 'expedition';
13910 $table_element = 'expeditiondet';
13911 $parent_element = 'expedition';
13912 } elseif ($elementType == 'delivery_note') {
13913 $classpath = 'delivery/class';
13914 $subelement = 'delivery';
13915 $module = 'expedition';
13916 } elseif ($elementType == 'delivery') {
13917 $classpath = 'delivery/class';
13918 $subelement = 'delivery';
13919 $module = 'expedition';
13920 } elseif ($elementType == 'deliverydet') {
13921 // @todo
13922 } elseif ($elementType == 'supplier_proposal') {
13923 $classpath = 'supplier_proposal/class';
13924 $module = 'supplier_proposal';
13925 $element = 'supplierproposal';
13926 $classfile = 'supplier_proposal';
13927 $subelement = 'supplierproposal';
13928 } elseif ($elementType == 'supplier_proposaldet') {
13929 $classpath = 'supplier_proposal/class';
13930 $module = 'supplier_proposal';
13931 $classfile = 'supplier_proposal';
13932 $classname = 'SupplierProposalLine';
13933 $table_element = 'supplier_proposaldet';
13934 $parent_element = 'supplier_proposal';
13935 } elseif ($elementType == 'contract') {
13936 $classpath = 'contrat/class';
13937 $module = 'contrat';
13938 $subelement = 'contrat';
13939 $table_element = 'contract';
13940 } elseif ($elementType == 'contratdet') {
13941 $classpath = 'contrat/class';
13942 $module = 'contrat';
13943 $table_element = 'contratdet';
13944 $parent_element = 'contrat';
13945 } elseif ($elementType == 'mailing') {
13946 $classpath = 'comm/mailing/class';
13947 $module = 'mailing';
13948 $classfile = 'mailing';
13949 $classname = 'Mailing';
13950 $subelement = '';
13951 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13952 $classpath = 'adherents/class';
13953 $module = 'adherent';
13954 $subelement = 'adherent';
13955 $table_element = 'adherent';
13956 } elseif ($elementType == 'subscription') {
13957 $classpath = 'adherents/class';
13958 $classfile = 'subscription';
13959 $module = 'adherent';
13960 $subelement = 'subscription';
13961 $classname = 'Subscription';
13962 $table_element = 'subscription';
13963 } elseif ($elementType == 'usergroup') {
13964 $classpath = 'user/class';
13965 $module = 'user';
13966 } elseif ($elementType == 'mo') {
13967 $classpath = 'mrp/class';
13968 $classfile = 'mo';
13969 $classname = 'Mo';
13970 $module = 'mrp';
13971 $subelement = '';
13972 $table_element = 'mrp_mo';
13973 } elseif ($elementType == 'mrp_production') {
13974 $classpath = 'mrp/class';
13975 $classfile = 'mo';
13976 $classname = 'MoLine';
13977 $module = 'mrp';
13978 $subelement = '';
13979 $table_element = 'mrp_production';
13980 $parent_element = 'mo';
13981 } elseif ($elementType == 'cabinetmed_cons') {
13982 $classpath = 'cabinetmed/class';
13983 $module = 'cabinetmed';
13984 $subelement = 'cabinetmedcons';
13985 $table_element = 'cabinetmedcons';
13986 } elseif ($elementType == 'fichinter') {
13987 $classpath = 'fichinter/class';
13988 $module = 'ficheinter';
13989 $subelement = 'fichinter';
13990 $table_element = 'fichinter';
13991 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13992 $classpath = 'resource/class';
13993 $module = 'resource';
13994 $subelement = 'dolresource';
13995 $table_element = 'resource';
13996 } elseif ($elementType == 'opensurvey_sondage') {
13997 $classpath = 'opensurvey/class';
13998 $module = 'opensurvey';
13999 $subelement = 'opensurveysondage';
14000 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' ||$elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
14001 $classpath = 'fourn/class';
14002 $module = 'fournisseur';
14003 $classfile = 'fournisseur.commande';
14004 $element = 'order_supplier';
14005 $subelement = '';
14006 $classname = 'CommandeFournisseur';
14007 $table_element = 'commande_fournisseur';
14008 } elseif ($elementType == 'commande_fournisseurdet') {
14009 $classpath = 'fourn/class';
14010 $module = 'fournisseur';
14011 $classfile = 'fournisseur.commande';
14012 $element = 'commande_fournisseurdet';
14013 $subelement = '';
14014 $classname = 'CommandeFournisseurLigne';
14015 $table_element = 'commande_fournisseurdet';
14016 $parent_element = 'commande_fournisseur';
14017 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
14018 $classpath = 'fourn/class';
14019 $module = 'fournisseur';
14020 $classfile = 'fournisseur.facture';
14021 $element = 'invoice_supplier';
14022 $subelement = '';
14023 $classname = 'FactureFournisseur';
14024 $table_element = 'facture_fourn';
14025 } elseif ($elementType == 'facture_fourn_det') {
14026 $classpath = 'fourn/class';
14027 $module = 'fournisseur';
14028 $classfile = 'fournisseur.facture';
14029 $element = 'facture_fourn_det';
14030 $subelement = '';
14031 $classname = 'SupplierInvoiceLine';
14032 $table_element = 'facture_fourn_det';
14033 $parent_element = 'invoice_supplier';
14034 } elseif ($elementType == "service") {
14035 $classpath = 'product/class';
14036 $subelement = 'product';
14037 $table_element = 'product';
14038 } elseif ($elementType == 'salary') {
14039 $classpath = 'salaries/class';
14040 $module = 'salaries';
14041 } elseif ($elementType == 'payment_salary') {
14042 $classpath = 'salaries/class';
14043 $classfile = 'paymentsalary';
14044 $classname = 'PaymentSalary';
14045 $module = 'salaries';
14046 } elseif ($elementType == 'productlot') {
14047 $module = 'productbatch';
14048 $classpath = 'product/stock/class';
14049 $classfile = 'productlot';
14050 $classname = 'Productlot';
14051 $element = 'productlot';
14052 $subelement = '';
14053 $table_element = 'product_lot';
14054 } elseif ($elementType == 'societeaccount') {
14055 $classpath = 'societe/class';
14056 $classfile = 'societeaccount';
14057 $classname = 'SocieteAccount';
14058 $module = 'societe';
14059 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
14060 $classpath = 'website/class';
14061 $classfile = 'websitepage';
14062 $classname = 'Websitepage';
14063 $module = 'website';
14064 $subelement = 'websitepage';
14065 $table_element = 'website_page';
14066 } elseif ($elementType == 'fiscalyear') {
14067 $classpath = 'core/class';
14068 $module = 'accounting';
14069 $subelement = 'fiscalyear';
14070 } elseif ($elementType == 'chargesociales') {
14071 $classpath = 'compta/sociales/class';
14072 $module = 'tax';
14073 $table_element = 'chargesociales';
14074 } elseif ($elementType == 'tva') {
14075 $classpath = 'compta/tva/class';
14076 $module = 'tax';
14077 $subdir = '/vat';
14078 $table_element = 'tva';
14079 } elseif ($elementType == 'emailsenderprofile') {
14080 $module = '';
14081 $classpath = 'core/class';
14082 $classfile = 'emailsenderprofile';
14083 $classname = 'EmailSenderProfile';
14084 $table_element = 'c_email_senderprofile';
14085 $subelement = '';
14086 } elseif ($elementType == 'conferenceorboothattendee') {
14087 $classpath = 'eventorganization/class';
14088 $classfile = 'conferenceorboothattendee';
14089 $classname = 'ConferenceOrBoothAttendee';
14090 $module = 'eventorganization';
14091 } elseif ($elementType == 'conferenceorbooth') {
14092 $classpath = 'eventorganization/class';
14093 $classfile = 'conferenceorbooth';
14094 $classname = 'ConferenceOrBooth';
14095 $module = 'eventorganization';
14096 } elseif ($elementType == 'ccountry') {
14097 $module = '';
14098 $classpath = 'core/class';
14099 $classfile = 'ccountry';
14100 $classname = 'Ccountry';
14101 $table_element = 'c_country';
14102 $subelement = '';
14103 } elseif ($elementType == 'ecmfiles') {
14104 $module = 'ecm';
14105 $classpath = 'ecm/class';
14106 $classfile = 'ecmfiles';
14107 $classname = 'Ecmfiles';
14108 $table_element = 'ecmfiles';
14109 $subelement = '';
14110 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
14111 $module = 'knowledgemanagement';
14112 $classpath = 'knowledgemanagement/class';
14113 $classfile = 'knowledgerecord';
14114 $classname = 'KnowledgeRecord';
14115 $table_element = 'knowledgemanagement_knowledgerecord';
14116 $subelement = '';
14117 } elseif ($elementType == 'customer') {
14118 $module = 'societe';
14119 $classpath = 'societe/class';
14120 $classfile = 'client';
14121 $classname = 'Client';
14122 $table_element = 'societe';
14123 $subelement = '';
14124 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
14125 $module = 'societe';
14126 $classpath = 'fourn/class';
14127 $classfile = 'fournisseur';
14128 $classname = 'Fournisseur';
14129 $table_element = 'societe';
14130 $subelement = '';
14131 }
14132
14133
14134 if (empty($classfile)) {
14135 $classfile = strtolower($subelement);
14136 }
14137 if (empty($classname)) {
14138 $classname = ucfirst($subelement);
14139 }
14140 if (empty($classpath)) {
14141 $classpath = $module.'/class';
14142 }
14143
14144 //print 'getElementProperties subdir='.$subdir;
14145
14146 // Set dir_output
14147 if ($module && isset($conf->$module)) { // The generic case
14148 if (!empty($conf->$module->multidir_output[$conf->entity])) {
14149 $dir_output = $conf->$module->multidir_output[$conf->entity];
14150 } elseif (!empty($conf->$module->output[$conf->entity])) {
14151 $dir_output = $conf->$module->output[$conf->entity];
14152 } elseif (!empty($conf->$module->dir_output)) {
14153 $dir_output = $conf->$module->dir_output;
14154 }
14155 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
14156 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
14157 } elseif (!empty($conf->$module->temp[$conf->entity])) {
14158 $dir_temp = $conf->$module->temp[$conf->entity];
14159 } elseif (!empty($conf->$module->dir_temp)) {
14160 $dir_temp = $conf->$module->dir_temp;
14161 }
14162 }
14163
14164 // Overwrite value for special cases
14165 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
14166 $dir_output = $conf->fournisseur->commande->dir_output;
14167 $dir_temp = $conf->fournisseur->commande->dir_temp;
14168 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
14169 $dir_output = $conf->fournisseur->facture->dir_output;
14170 $dir_temp = $conf->fournisseur->facture->dir_temp;
14171 }
14172 $dir_output .= $subdir;
14173 $dir_temp .= $subdir;
14174
14175 $elementProperties = array(
14176 'module' => $module,
14177 'element' => $element,
14178 'table_element' => $table_element,
14179 'subelement' => $subelement,
14180 'classpath' => $classpath,
14181 'classfile' => $classfile,
14182 'classname' => $classname,
14183 'dir_output' => $dir_output,
14184 'dir_temp' => $dir_temp,
14185 'parent_element' => $parent_element,
14186 );
14187
14188
14189 // Add hook
14190 if (!is_object($hookmanager)) {
14191 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14192 $hookmanager = new HookManager($db);
14193 }
14194 $hookmanager->initHooks(array('elementproperties'));
14195
14196
14197 // Hook params
14198 $parameters = array(
14199 'elementType' => $elementType,
14200 'elementProperties' => $elementProperties
14201 );
14202
14203 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
14204
14205 if ($reshook) {
14206 $elementProperties = $hookmanager->resArray;
14207 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
14208 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
14209 }
14210
14211 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
14212 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
14213 unset($hookmanager->contextarray[$key]);
14214 }
14215
14216 return $elementProperties;
14217}
14218
14231function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
14232{
14233 global $db, $conf;
14234
14235 $ret = 0;
14236
14237 $element_prop = getElementProperties($element_type);
14238
14239 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
14240 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
14241 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
14242 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
14243 // of service and we will return properties of a product.
14244 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
14245 } elseif ($element_prop['module'] == 'societeaccount') {
14246 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
14247 } else {
14248 $ismodenabled = isModEnabled($element_prop['module']);
14249 }
14250 //var_dump('element_type='.$element_type);
14251 //var_dump($element_prop);
14252 //var_dump($element_prop['module'].' '.$ismodenabled);
14253 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
14254 if ($useCache === 1 && $element_id > 0
14255 && !empty($conf->cache['fetchObjectByElement'][$element_type])
14256 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14257 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14258 ) {
14259 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
14260 }
14261
14262 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14263
14264 if (class_exists($element_prop['classname'])) {
14265 $className = $element_prop['classname'];
14266 $objecttmp = new $className($db);
14267 '@phan-var-force CommonObject $objecttmp';
14268
14269 if ($element_id > 0 || !empty($element_ref)) {
14270 $ret = $objecttmp->fetch($element_id, $element_ref);
14271 if ($ret >= 0) {
14272 if (empty($objecttmp->module)) {
14273 $objecttmp->module = $element_prop['module'];
14274 }
14275
14276 if ($useCache > 0) {
14277 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
14278 $conf->cache['fetchObjectByElement'][$element_type] = [];
14279 }
14280
14281 // Manage cache limit
14282 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
14283 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
14284 }
14285
14286 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
14287 }
14288
14289 return $objecttmp;
14290 }
14291 } else {
14292 return $objecttmp; // returned an object without fetch
14293 }
14294 } else {
14295 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14296 return -1;
14297 }
14298 }
14299
14300 return $ret;
14301}
14302
14308function getExecutableContent()
14309{
14310 $arrayofregexextension = array(
14311 'htm', 'html', 'shtml', 'js', 'phar', 'php', 'php3', 'php4', 'php5', 'phtml', 'pht', 'pl', 'py', 'cgi', 'ksh', 'sh', 'shtml',
14312 'bash', 'bat', 'cmd', 'wpk', 'exe', 'dmg', 'appimage'
14313 );
14314
14315 return $arrayofregexextension;
14316}
14317
14324function isAFileWithExecutableContent($filename)
14325{
14326 $arrayofregexextension = getExecutableContent();
14327
14328 foreach ($arrayofregexextension as $fileextension) {
14329 if (preg_match('/\.'.preg_quote($fileextension, '/').'$/i', $filename)) {
14330 return true;
14331 }
14332 }
14333
14334 return false;
14335}
14336
14344function newToken()
14345{
14346 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
14347}
14348
14356function currentToken()
14357{
14358 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
14359}
14360
14366function getNonce()
14367{
14368 global $conf;
14369
14370 if (empty($conf->cache['nonce'])) {
14371 $conf->cache['nonce'] = dolGetRandomBytes(8);
14372 }
14373
14374 return $conf->cache['nonce'];
14375}
14376
14377
14391function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
14392{
14393 global $langs;
14394
14395 print '<div class="div-table-responsive-no-min">';
14396 print '<table class="noborder centpercent">';
14397 print '<tr class="liste_titre">';
14398
14399 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
14400
14401 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
14402
14403 if (!empty($link)) {
14404 if (!empty($arguments)) {
14405 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
14406 } else {
14407 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
14408 }
14409 }
14410
14411 if ($number > -1) {
14412 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
14413 } elseif (!empty($link)) {
14414 print '<span class="badge marginleftonlyshort">...</span>';
14415 }
14416
14417 if (!empty($link)) {
14418 print '</a>';
14419 }
14420
14421 print '</th>';
14422
14423 if ($number < 0 && !empty($link)) {
14424 print '<th class="right">';
14425 print '</th>';
14426 }
14427
14428 print '</tr>';
14429}
14430
14439function finishSimpleTable($addLineBreak = false)
14440{
14441 print '</table>';
14442 print '</div>';
14443
14444 if ($addLineBreak) {
14445 print '<br>';
14446 }
14447}
14448
14460function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
14461{
14462 global $langs;
14463
14464 if ($num === 0) {
14465 print '<tr class="oddeven">';
14466 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
14467 print '</tr>';
14468 return;
14469 }
14470
14471 if ($nbofloop === 0) {
14472 // don't show a summary line
14473 return;
14474 }
14475
14476 /* Case already handled above, commented to satisfy phpstan.
14477 if ($num === 0) {
14478 $colspan = $tableColumnCount;
14479 } else
14480 */
14481 if ($num > $nbofloop) {
14482 $colspan = $tableColumnCount;
14483 } else {
14484 $colspan = $tableColumnCount - 1;
14485 }
14486
14487 if ($extraRightColumn) {
14488 $colspan--;
14489 }
14490
14491 print '<tr class="liste_total">';
14492
14493 if ($nbofloop > 0 && $num > $nbofloop) {
14494 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
14495 } else {
14496 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
14497 print '<td class="right centpercent">'.price($total).'</td>';
14498 }
14499
14500 if ($extraRightColumn) {
14501 print '<td></td>';
14502 }
14503
14504 print '</tr>';
14505}
14506
14515function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
14516{
14517 if ($method == -1) {
14518 $method = 0;
14519 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
14520 $method = 1;
14521 }
14522 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
14523 $method = 2;
14524 }
14525 }
14526
14527 // Be sure we don't have output buffering enabled to have readfile working correctly
14528 while (ob_get_level()) {
14529 ob_end_flush();
14530 }
14531
14532 // Solution 0
14533 if ($method == 0) {
14534 readfile($fullpath_original_file_osencoded);
14535 } elseif ($method == 1) {
14536 // Solution 1
14537 $handle = fopen($fullpath_original_file_osencoded, "rb");
14538 while (!feof($handle)) {
14539 print fread($handle, 8192);
14540 }
14541 fclose($handle);
14542 } elseif ($method == 2) {
14543 // Solution 2
14544 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
14545 $handle2 = fopen("php://output", "wb");
14546 stream_copy_to_stream($handle1, $handle2);
14547 fclose($handle1);
14548 fclose($handle2);
14549 }
14550}
14551
14561function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
14562{
14563 global $langs;
14564
14565 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
14566
14567 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
14568 if ($texttoshow === 'none') {
14569 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14570 $result .= '<span class="clipboardCPValueToPrint"></span>';
14571 } elseif ($texttoshow) {
14572 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14573 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
14574 } else {
14575 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14576 }
14577 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
14578 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
14579 $result .= '<span class="clipboardCPText"></span>';
14580 $result .= '</span>';
14581
14582 return $result;
14583}
14584
14585
14592function jsonOrUnserialize($stringtodecode)
14593{
14594 $result = json_decode($stringtodecode);
14595 if ($result === null) {
14596 $result = unserialize($stringtodecode);
14597 }
14598
14599 return $result;
14600}
14601
14602
14619function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
14620{
14621 global $db, $user;
14622
14623 if (is_null($filter) || !is_string($filter) || $filter === '') {
14624 return '';
14625 }
14626 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
14627 $filter = '(' . $filter . ')';
14628 }
14629
14630 $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'
14631 $firstandlastparenthesis = 0;
14632
14633 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
14634 if ($noerror) {
14635 return '1 = 2';
14636 } else {
14637 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
14638 }
14639 }
14640
14641 // Test the filter syntax
14642 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
14643 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
14644 // If the string result contains something else than '()', the syntax was wrong
14645
14646 if (preg_match('/[^\‍(\‍)]/', $t)) {
14647 $tmperrorstr = 'Bad syntax of the search string';
14648 $errorstr = 'Bad syntax of the search string: '.$filter;
14649 if ($noerror) {
14650 return '1 = 2';
14651 } else {
14652 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
14653 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
14654 }
14655 }
14656
14657 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
14658
14659 if (is_object($db)) {
14660 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
14661 }
14662 if (is_object($user)) {
14663 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
14664 }
14665
14666 return $ret;
14667}
14668
14676function dolForgeExplodeAnd($sqlfilters)
14677{
14678 $arrayofandtags = array();
14679 $nbofchars = dol_strlen($sqlfilters);
14680
14681 $error = '';
14682 $parenthesislevel = 0;
14683 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
14684 if (!$result) {
14685 return array();
14686 }
14687 if ($parenthesislevel >= 1) {
14688 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
14689 }
14690
14691 $i = 0;
14692 $s = '';
14693 $countparenthesis = 0;
14694 while ($i < $nbofchars) {
14695 $char = dol_substr($sqlfilters, $i, 1);
14696
14697 if ($char == '(') {
14698 $countparenthesis++;
14699 } elseif ($char == ')') {
14700 $countparenthesis--;
14701 }
14702
14703 if ($countparenthesis == 0) {
14704 $char2 = dol_substr($sqlfilters, $i + 1, 1);
14705 $char3 = dol_substr($sqlfilters, $i + 2, 1);
14706 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
14707 // We found a AND
14708 $s = trim($s);
14709 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14710 $s = '('.$s.')';
14711 }
14712 $arrayofandtags[] = $s;
14713 $s = '';
14714 $i += 2;
14715 } else {
14716 $s .= $char;
14717 }
14718 } else {
14719 $s .= $char;
14720 }
14721 $i++;
14722 }
14723 if ($s) {
14724 $s = trim($s);
14725 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14726 $s = '('.$s.')';
14727 }
14728 $arrayofandtags[] = $s;
14729 }
14730
14731 return $arrayofandtags;
14732}
14733
14743function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
14744{
14745 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
14746 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
14747 $tmp = $sqlfilters;
14748
14749 $nb = dol_strlen($tmp);
14750 $counter = 0;
14751 $parenthesislevel = 0;
14752
14753 $error = '';
14754
14755 $i = 0;
14756 while ($i < $nb) {
14757 $char = dol_substr($tmp, $i, 1);
14758
14759 if ($char == '(') {
14760 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
14761 // We open a parenthesis and it is the first char
14762 $parenthesislevel++;
14763 }
14764 $counter++;
14765 } elseif ($char == ')') {
14766 $nbcharremaining = ($nb - $i - 1);
14767 if ($nbcharremaining >= $counter) {
14768 $parenthesislevel = min($parenthesislevel, $counter - 1);
14769 }
14770 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
14771 $parenthesislevel = $counter;
14772 }
14773 $counter--;
14774 }
14775
14776 if ($counter < 0) {
14777 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14778 $parenthesislevel = 0;
14779 dol_syslog($error, LOG_WARNING);
14780 return false;
14781 }
14782
14783 $i++;
14784 }
14785
14786 if ($counter > 0) {
14787 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14788 $parenthesislevel = 0;
14789 dol_syslog($error, LOG_WARNING);
14790 return false;
14791 }
14792
14793 return true;
14794}
14795
14803function dolForgeDummyCriteriaCallback($matches)
14804{
14805 //dol_syslog("Convert matches ".$matches[1]);
14806 if (empty($matches[1])) {
14807 return '';
14808 }
14809 $tmp = explode(':', $matches[1]);
14810 if (count($tmp) < 3) {
14811 return '';
14812 }
14813
14814 return '()'; // An empty criteria
14815}
14816
14825function dolForgeSQLCriteriaCallback($matches)
14826{
14827 global $db;
14828
14829 //dol_syslog("Convert matches ".$matches[1]);
14830 if (empty($matches[1])) {
14831 return '';
14832 }
14833 $tmp = explode(':', $matches[1], 3);
14834 if (count($tmp) < 3) {
14835 return '';
14836 }
14837
14838 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
14839
14840 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
14841
14842 $realOperator = [
14843 'NOTLIKE' => 'NOT LIKE',
14844 'ISNOT' => 'IS NOT',
14845 'NOTIN' => 'NOT IN',
14846 '!=' => '<>',
14847 ];
14848
14849 if (array_key_exists($operator, $realOperator)) {
14850 $operator = $realOperator[$operator];
14851 }
14852
14853 $tmpescaped = $tmp[2];
14854
14855 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
14856
14857 $regbis = array();
14858
14859 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)
14860 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
14861 $tmpescaped2 = '(';
14862 // Explode and sanitize each element in list
14863 $tmpelemarray = explode(',', $tmpescaped);
14864 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
14865 $reg = array();
14866 $tmpelem = trim($tmpelem);
14867 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
14868 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
14869 } elseif (ctype_digit((string) $tmpelem)) { // if only 0-9 chars, no .
14870 $tmpelemarray[$tmpkey] = (int) $tmpelem;
14871 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
14872 $tmpelemarray[$tmpkey] = (float) $tmpelem;
14873 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
14874 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
14875 } else {
14876 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
14877 }
14878 }
14879 $tmpescaped2 .= implode(',', $tmpelemarray);
14880 $tmpescaped2 .= ')';
14881
14882 $tmpescaped = $tmpescaped2;
14883 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14884 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14885 $tmpescaped = $regbis[1];
14886 }
14887 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14888 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14889 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14890 // TODO Retrieve type of field for $operand field name.
14891 // So we can complete format. For example we could complete a year with month and day.
14892 $tmpescaped = "'".$db->escape($regbis[1])."'";
14893 } else {
14894 if (strtoupper($tmpescaped) == 'NULL') {
14895 $tmpescaped = 'NULL';
14896 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
14897 $tmpescaped = (int) $tmpescaped;
14898 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
14899 $tmpescaped = (float) $tmpescaped;
14900 } else {
14901 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
14902 }
14903 }
14904
14905 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14906}
14907
14908
14918function getTimelineIcon($actionstatic, &$histo, $key)
14919{
14920 global $langs;
14921
14922 $out = '<!-- timeline icon -->'."\n";
14923 $iconClass = 'fa fa-comments';
14924 $img_picto = '';
14925 $colorClass = '';
14926 $pictoTitle = '';
14927
14928 if ($histo[$key]['percent'] == -1) {
14929 $colorClass = 'timeline-icon-not-applicble';
14930 $pictoTitle = $langs->trans('StatusNotApplicable');
14931 } elseif ($histo[$key]['percent'] == 0) {
14932 $colorClass = 'timeline-icon-todo';
14933 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14934 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14935 $colorClass = 'timeline-icon-in-progress';
14936 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14937 } elseif ($histo[$key]['percent'] >= 100) {
14938 $colorClass = 'timeline-icon-done';
14939 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14940 }
14941
14942 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14943 $iconClass = 'fa fa-ticket';
14944 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14945 $iconClass = 'fa fa-pencilxxx';
14946 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14947 $iconClass = 'fa fa-comments';
14948 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14949 $iconClass = 'fa fa-mask';
14950 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14951 if ($actionstatic->type_picto) {
14952 $img_picto = img_picto('', $actionstatic->type_picto);
14953 } else {
14954 if ($actionstatic->type_code == 'AC_RDV') {
14955 $iconClass = 'fa fa-handshake';
14956 } elseif ($actionstatic->type_code == 'AC_TEL') {
14957 $iconClass = 'fa fa-phone';
14958 } elseif ($actionstatic->type_code == 'AC_FAX') {
14959 $iconClass = 'fa fa-fax';
14960 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14961 $iconClass = 'fa fa-envelope';
14962 } elseif ($actionstatic->type_code == 'AC_INT') {
14963 $iconClass = 'fa fa-shipping-fast';
14964 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14965 $iconClass = 'fa fa-robot';
14966 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14967 $iconClass = 'fa fa-robot';
14968 }
14969 }
14970 }
14971
14972 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14973 return $out;
14974}
14975
14983{
14984 global $conf, $db;
14985
14986 $documents = array();
14987
14988 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
14989 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14990 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14991 //$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
14992 $sql.= ' OR ecm.agenda_id = '.(int) $object->id;
14993 $sql .= ' ORDER BY ecm.position ASC';
14994
14995 $resql = $db->query($sql);
14996 if ($resql) {
14997 if ($db->num_rows($resql)) {
14998 while ($obj = $db->fetch_object($resql)) {
14999 $documents[$obj->id] = $obj;
15000 }
15001 }
15002 }
15003
15004 return $documents;
15005}
15006
15007
15025function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
15026{
15027 global $user, $conf;
15028 global $form;
15029
15030 global $param, $massactionbutton;
15031
15032 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
15033
15034 // Check parameters
15035 if (!is_object($filterobj) && !is_object($objcon)) {
15036 dol_print_error(null, 'BadParameter');
15037 }
15038
15039 $histo = array();
15040 '@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';
15041
15042 $numaction = 0;
15043 $now = dol_now();
15044
15045 $sortfield_list = explode(',', $sortfield);
15046 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
15047 $sortfield_new_list = array();
15048 foreach ($sortfield_list as $sortfield_value) {
15049 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
15050 }
15051 $sortfield_new = implode(',', $sortfield_new_list);
15052
15053 $sql = null;
15054 $sql2 = null;
15055
15056 if (isModEnabled('agenda')) {
15057 // Search histo on actioncomm
15058 if (is_object($objcon) && $objcon->id > 0) {
15059 $sql = "SELECT DISTINCT a.id, a.label as label,";
15060 } else {
15061 $sql = "SELECT a.id, a.label as label,";
15062 }
15063 $sql .= " a.datep as dp,";
15064 $sql .= " a.note as message,";
15065 $sql .= " a.datep2 as dp2,";
15066 $sql .= " a.percent as percent, 'action' as type,";
15067 $sql .= " a.fk_element, a.elementtype,";
15068 $sql .= " a.fk_contact,";
15069 $sql .= " a.email_from as msg_from,";
15070 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
15071 $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";
15072 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15073 $sql .= ", sp.lastname, sp.firstname";
15074 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15075 $sql .= ", m.lastname, m.firstname";
15076 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
15077 $sql .= ", o.ref";
15078 }
15079 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
15080 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
15081 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
15082
15083 $force_filter_contact = $filterobj instanceof User;
15084
15085 if (is_object($objcon) && $objcon->id > 0) {
15086 $force_filter_contact = true;
15087 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
15088 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
15089 }
15090
15091 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
15092 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
15093 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
15094 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
15095 $sql .= " ON er.resource_type = 'dolresource'";
15096 $sql .= " AND er.element_id = a.id";
15097 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
15098 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15099 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
15100 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15101 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
15102 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15103 $sql .= ", ".MAIN_DB_PREFIX."product as o";
15104 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15105 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
15106 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15107 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
15108 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15109 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
15110 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15111 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
15112 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15113 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
15114 }
15115
15116 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
15117 if (!$force_filter_contact) {
15118 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
15119 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
15120 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
15121 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
15122 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15123 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
15124 if ($filterobj->id) {
15125 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15126 }
15127 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
15128 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
15129 if ($filterobj->id) {
15130 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15131 }
15132 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15133 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
15134 if ($filterobj->id) {
15135 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15136 }
15137 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15138 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
15139 if ($filterobj->id) {
15140 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15141 }
15142 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15143 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
15144 if ($filterobj->id) {
15145 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15146 }
15147 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15148 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
15149 if ($filterobj->id) {
15150 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15151 }
15152 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15153 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
15154 if ($filterobj->id) {
15155 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15156 }
15157 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
15158 $sql .= " AND a.fk_contact = sp.rowid";
15159 if ($filterobj->id) {
15160 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
15161 }
15162 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15163 $sql .= " AND a.fk_element = o.rowid";
15164 if ($filterobj->id) {
15165 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
15166 }
15167 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15168 $sql .= " AND a.fk_element = o.rowid";
15169 if ($filterobj->id) {
15170 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
15171 }
15172 }
15173 } else {
15174 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
15175 }
15176
15177 // Condition on actioncode
15178 if (!empty($actioncode) && $actioncode != '-1') {
15179 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
15180 if ($actioncode == 'AC_NON_AUTO') {
15181 $sql .= " AND c.type != 'systemauto'";
15182 } elseif ($actioncode == 'AC_ALL_AUTO') {
15183 $sql .= " AND c.type = 'systemauto'";
15184 } else {
15185 if ($actioncode == 'AC_OTH') {
15186 $sql .= " AND c.type != 'systemauto'";
15187 } elseif ($actioncode == 'AC_OTH_AUTO') {
15188 $sql .= " AND c.type = 'systemauto'";
15189 }
15190 }
15191 } else {
15192 if ($actioncode == 'AC_NON_AUTO') {
15193 $sql .= " AND c.type != 'systemauto'";
15194 } elseif ($actioncode == 'AC_ALL_AUTO') {
15195 $sql .= " AND c.type = 'systemauto'";
15196 } else {
15197 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
15198 }
15199 }
15200 }
15201 if ($donetodo == 'todo') {
15202 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15203 } elseif ($donetodo == 'done') {
15204 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15205 }
15206 if (is_array($filters) && $filters['search_agenda_label']) {
15207 $sql .= natural_search('a.label', $filters['search_agenda_label']);
15208 }
15209 }
15210
15211 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
15212 if (isModEnabled('mailing') && !empty($objcon->email)
15213 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
15214 $langs->load("mails");
15215
15216 $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";
15217 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
15218 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
15219 $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
15220 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15221 $sql2 .= ", '' as lastname, '' as firstname";
15222 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15223 $sql2 .= ", '' as lastname, '' as firstname";
15224 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15225 $sql2 .= ", '' as ref";
15226 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15227 $sql2 .= ", '' as ref";
15228 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15229 $sql2 .= ", '' as ref";
15230 }
15231 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
15232 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
15233 $sql2 .= " AND mc.statut = 1";
15234 $sql2 .= " AND u.rowid = m.fk_user_valid";
15235 $sql2 .= " AND mc.fk_mailing=m.rowid";
15236 }
15237
15238 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
15239 if (!empty($sql) && !empty($sql2)) {
15240 $sql = $sql." UNION ".$sql2;
15241 } elseif (empty($sql) && !empty($sql2)) {
15242 $sql = $sql2;
15243 }
15244
15245 //TODO Add navigation with this limits...
15246 $offset = 0;
15247 $limit = 1000;
15248
15249 // Complete request and execute it with limit
15250 $sql .= $db->order($sortfield_new, $sortorder);
15251 if ($limit) {
15252 $sql .= $db->plimit($limit + 1, $offset);
15253 }
15254
15255 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
15256 $resql = $db->query($sql);
15257 if ($resql) {
15258 $i = 0;
15259 $num = $db->num_rows($resql);
15260
15261 $imaxinloop = ($limit ? min($num, $limit) : $num);
15262 while ($i < $imaxinloop) {
15263 $obj = $db->fetch_object($resql);
15264
15265 if ($obj->type == 'action') {
15266 $contactaction = new ActionComm($db);
15267 $contactaction->id = $obj->id;
15268 $result = $contactaction->fetchResources();
15269 if ($result < 0) {
15270 dol_print_error($db);
15271 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
15272 }
15273
15274 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15275 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15276 $tododone = '';
15277 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
15278 $tododone = 'todo';
15279 }
15280
15281 $histo[$numaction] = array(
15282 'type' => $obj->type,
15283 'tododone' => $tododone,
15284 'id' => $obj->id,
15285 'datestart' => $db->jdate($obj->dp),
15286 'dateend' => $db->jdate($obj->dp2),
15287 'note' => $obj->label,
15288 'message' => dol_htmlentitiesbr($obj->message),
15289 'percent' => $obj->percent,
15290
15291 'userid' => $obj->user_id,
15292 'login' => $obj->user_login,
15293 'userfirstname' => $obj->user_firstname,
15294 'userlastname' => $obj->user_lastname,
15295 'userphoto' => $obj->user_photo,
15296 'msg_from' => $obj->msg_from,
15297
15298 'contact_id' => $obj->fk_contact,
15299 'socpeopleassigned' => $contactaction->socpeopleassigned,
15300 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
15301 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
15302 'fk_element' => $obj->fk_element,
15303 'elementtype' => $obj->elementtype,
15304 // Type of event
15305 'acode' => $obj->acode,
15306 'alabel' => $obj->alabel,
15307 'libelle' => $obj->alabel, // deprecated
15308 'apicto' => $obj->apicto
15309 );
15310 } else {
15311 $histo[$numaction] = array(
15312 'type' => $obj->type,
15313 'tododone' => 'done',
15314 'id' => $obj->id,
15315 'datestart' => $db->jdate($obj->dp),
15316 'dateend' => $db->jdate($obj->dp2),
15317 'note' => $obj->label,
15318 'message' => dol_htmlentitiesbr($obj->message),
15319 'percent' => $obj->percent,
15320 'acode' => $obj->acode,
15321
15322 'userid' => $obj->user_id,
15323 'login' => $obj->user_login,
15324 'userfirstname' => $obj->user_firstname,
15325 'userlastname' => $obj->user_lastname,
15326 'userphoto' => $obj->user_photo
15327 );
15328 }
15329
15330 $numaction++;
15331 $i++;
15332 }
15333 } else {
15334 dol_print_error($db);
15335 }
15336 }
15337
15338 // Set $out to show events
15339 $out = '';
15340
15341 if (!isModEnabled('agenda')) {
15342 $langs->loadLangs(array("admin", "errors"));
15343 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
15344 }
15345
15346 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
15347 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
15348
15349 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
15350 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
15351 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
15352 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
15353
15354 $formactions = new FormActions($db);
15355
15356 $actionstatic = new ActionComm($db);
15357 $userstatic = new User($db);
15358 $contactstatic = new Contact($db);
15359 $userGetNomUrlCache = array();
15360 $contactGetNomUrlCache = array();
15361
15362 $out .= '<div class="filters-container" >';
15363 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
15364 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
15365
15366 if ($objcon && get_class($objcon) == 'Contact' &&
15367 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
15368 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
15369 } else {
15370 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
15371 }
15372 if (($filterobj && get_class($filterobj) == 'Societe')) {
15373 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
15374 } else {
15375 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
15376 }
15377
15378 $out .= "\n";
15379
15380 $out .= '<div class="div-table-responsive-no-min">';
15381 $out .= '<table class="noborder borderbottom centpercent">';
15382
15383 $out .= '<tr class="liste_titre">';
15384
15385 // Action column
15386 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15387 $out .= '<th class="liste_titre width50 middle">';
15388 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15389 $out .= $searchpicto;
15390 $out .= '</th>';
15391 }
15392
15393 // Date
15394 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
15395
15396 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
15397 if ($donetodo) {
15398 $out .= '<th class="liste_titre"></th>';
15399 }
15400 // Type of event
15401 $out .= '<th class="liste_titre">';
15402 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
15403 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
15404 $out .= '</th>';
15405 // Label
15406 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
15407 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
15408 $out .= '</th>';
15409
15410 // Action column
15411 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15412 $out .= '<th class="liste_titre width50 middle">';
15413 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15414 $out .= $searchpicto;
15415 $out .= '</th>';
15416 }
15417
15418 $out .= '</tr>';
15419
15420 $out .= '</table>';
15421
15422 $out .= '</form>';
15423 $out .= '</div>';
15424
15425 $out .= "\n";
15426
15427 $out .= '<ul class="timeline">';
15428
15429 if ($donetodo) {
15430 $tmp = '';
15431 if ($filterobj instanceof Societe) {
15432 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15433 }
15434 if ($filterobj instanceof User) {
15435 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15436 }
15437 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
15438 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
15439 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
15440 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
15441 if ($filterobj instanceof Societe) {
15442 $tmp .= '</a>';
15443 }
15444 if ($filterobj instanceof User) {
15445 $tmp .= '</a>';
15446 }
15447 $out .= getTitleFieldOfList($tmp);
15448 }
15449
15450 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
15451 $caction = new CActionComm($db);
15452 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
15453
15454 $actualCycleDate = false;
15455
15456 // Loop on each event to show it
15457 foreach ($histo as $key => $value) {
15458 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
15459
15460 $actionstatic->type_picto = $histo[$key]['apicto'];
15461 $actionstatic->type_code = $histo[$key]['acode'];
15462
15463 $labeltype = $actionstatic->type_code;
15464 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
15465 $labeltype = 'AC_OTH';
15466 }
15467 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15468 $labeltype = $langs->trans("Message");
15469 } else {
15470 if (!empty($arraylist[$labeltype])) {
15471 $labeltype = $arraylist[$labeltype];
15472 }
15473 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
15474 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
15475 }
15476 }
15477
15478 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
15479
15480 $tmpa = dol_getdate($histo[$key]['datestart'], false);
15481
15482 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
15483 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
15484 $out .= '<!-- timeline time label -->';
15485 $out .= '<li class="time-label">';
15486 $out .= '<span class="timeline-badge-date">';
15487 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
15488 $out .= '</span>';
15489 $out .= '</li>';
15490 $out .= '<!-- /.timeline-label -->';
15491 }
15492
15493
15494 $out .= '<!-- timeline item -->'."\n";
15495 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
15496
15497 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
15498 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
15499 //$out .= $timelineicon;
15500 //var_dump($timelineicon);
15501 $out .= $typeicon;
15502
15503 $out .= '<div class="timeline-item">'."\n";
15504
15505 $out .= '<span class="time timeline-header-action2">';
15506
15507 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
15508 $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").' ';
15509 $out .= $histo[$key]['id'];
15510 $out .= '</a> ';
15511 } else {
15512 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
15513 }
15514
15515 if ($user->hasRight('agenda', 'allactions', 'create') ||
15516 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
15517 $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).'">';
15518 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
15519 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
15520 $out .= '</a>';
15521 }
15522
15523 $out .= '</span>';
15524
15525 // Date
15526 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
15527 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
15528 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
15529 $tmpa = dol_getdate($histo[$key]['datestart'], true);
15530 $tmpb = dol_getdate($histo[$key]['dateend'], true);
15531 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
15532 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
15533 } else {
15534 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
15535 }
15536 }
15537 $late = 0;
15538 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15539 $late = 1;
15540 }
15541 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15542 $late = 1;
15543 }
15544 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
15545 $late = 1;
15546 }
15547 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15548 $late = 1;
15549 }
15550 if ($late) {
15551 $out .= img_warning($langs->trans("Late")).' ';
15552 }
15553 $out .= "</span></span>\n";
15554
15555 $out .= '<span class="time">';
15556 $out .= $actionstatic->getLibStatut(2);
15557 $out .= '</span>';
15558
15559 // Ref
15560 $out .= '<h3 class="timeline-header">';
15561
15562 // Author of event
15563 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
15564 if ($histo[$key]['userid'] > 0) {
15565 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
15566 $userstatic->fetch($histo[$key]['userid']);
15567 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
15568 }
15569 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
15570 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
15571 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
15572 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
15573 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
15574 } else {
15575 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
15576 }
15577 }
15578 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
15579 }
15580 $out .= '</div>';
15581
15582 // Title
15583 $out .= ' <div class="messaging-title inline-block">';
15584 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
15585 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
15586 $out .= $labeltype.' - ';
15587 }
15588
15589 $libelle = '';
15590
15591 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
15592 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
15593 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15594 $out .= $langs->trans('TicketNewMessage');
15595 } elseif (isset($histo[$key]['type'])) {
15596 if ($histo[$key]['type'] == 'action') {
15597 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15598 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
15599 $libelle = $histo[$key]['note'];
15600 $actionstatic->id = $histo[$key]['id'];
15601 if ($libelle != $labeltype) {
15602 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15603 }
15604 } elseif ($histo[$key]['type'] == 'mailing') {
15605 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
15606 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15607 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
15608 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15609 } else {
15610 $libelle .= $histo[$key]['note'];
15611 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15612 }
15613 }
15614 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
15615
15616 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
15617 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
15618 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
15619 } else {
15620 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
15621 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
15622 }
15623 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
15624 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
15625 }
15626 if ($link) {
15627 $out .= ' - '.$link;
15628 }
15629 }
15630
15631 $out .= '</div>';
15632
15633 $out .= '</h3>';
15634
15635 // Message
15636 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
15637 && $actionstatic->code != 'AC_TICKET_CREATE'
15638 && $actionstatic->code != 'AC_TICKET_MODIFY'
15639 ) {
15640 $out .= '<div class="timeline-body wordbreak small">';
15641 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
15642 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
15643 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
15644 $out .= '<div class="readmore-block --closed" >';
15645 $out .= ' <div class="readmore-block__excerpt">';
15646 $out .= dolPrintHTML($truncatedText);
15647 $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>';
15648 $out .= ' </div>';
15649 $out .= ' <div class="readmore-block__full-text" >';
15650 $out .= dolPrintHTML($histo[$key]['message']);
15651 $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>';
15652 $out .= ' </div>';
15653 $out .= '</div>';
15654 } else {
15655 $out .= dolPrintHTML($histo[$key]['message']);
15656 }
15657
15658 $out .= '</div>';
15659 }
15660
15661 // Timeline footer
15662 $footer = '';
15663
15664 // Contact for this action
15665 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
15666 $contactList = '';
15667 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
15668 if (empty($conf->cache['contact'][$cid])) {
15669 $contact = new Contact($db);
15670 $contact->fetch($cid);
15671 $conf->cache['contact'][$cid] = $contact;
15672 } else {
15673 $contact = $conf->cache['contact'][$cid];
15674 }
15675
15676 if ($contact) {
15677 $contactList .= !empty($contactList) ? ', ' : '';
15678 $contactList .= $contact->getNomUrl(1);
15679 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15680 if (!empty($contact->phone_pro)) {
15681 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
15682 }
15683 }
15684 }
15685 }
15686
15687 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
15688 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
15689 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
15690 $contact = new Contact($db);
15691 $result = $contact->fetch($histo[$key]['contact_id']);
15692 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
15693 } else {
15694 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
15695 $result = ($contact instanceof Contact) ? $contact->id : 0;
15696 }
15697
15698 if ($result > 0) {
15699 $footer .= $contact->getNomUrl(1);
15700 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15701 if (!empty($contact->phone_pro)) {
15702 $footer .= '('.dol_print_phone($contact->phone_pro).')';
15703 }
15704 }
15705 }
15706 }
15707
15708 $documents = getActionCommEcmList($actionstatic);
15709 if (!empty($documents)) {
15710 $footer .= '<div class="timeline-documents-container">';
15711 foreach ($documents as $doc) {
15712 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
15713 $footer .= ' data-id="'.$doc->id.'" ';
15714 $footer .= ' data-path="'.$doc->filepath.'"';
15715 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
15716 $footer .= '>';
15717
15718 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
15719 $mime = dol_mimetype($filePath);
15720 if (empty($doc->agenda_id)) {
15721 $dir_ref = $actionstatic->id;
15722 $modulepart = 'actions';
15723 } else {
15724 $split_dir = explode('/', $doc->filepath);
15725 $modulepart = array_shift($split_dir);
15726 $dir_ref = implode('/', $split_dir);
15727 }
15728
15729 $file = $dir_ref.'/'.$doc->filename;
15730 $thumb = $dir_ref.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
15731 $doclink = dol_buildpath('document.php', 1).'?modulepart='.$modulepart.'&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
15732 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart='.$modulepart.'&file='.urlencode($thumb).'&entity='.$conf->entity;
15733
15734
15735
15736 $mimeAttr = ' mime="'.$mime.'" ';
15737 $class = '';
15738 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
15739 $class .= ' documentpreview';
15740 }
15741
15742 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
15743 $footer .= img_mime($filePath).' '.$doc->filename;
15744 $footer .= '</a>';
15745
15746 $footer .= '</span>';
15747 }
15748 $footer .= '</div>';
15749 }
15750
15751 if (!empty($footer)) {
15752 $out .= '<div class="timeline-footer">'.$footer.'</div>';
15753 }
15754
15755 $out .= '</div>'."\n"; // end timeline-item
15756
15757 $out .= '</li>';
15758 $out .= '<!-- END timeline item -->';
15759 }
15760
15761 $out .= "</ul>\n";
15762
15763 // Code to manage the click on button data-read-more-action to show full description of an event
15764 $out .= '<script>
15765 jQuery(document).ready(function () {
15766 $(document).on("click", "[data-read-more-action]", function(e){
15767 console.log("We click on data-read-more-action");
15768 let readMoreBloc = $(this).closest(".readmore-block");
15769 if(readMoreBloc.length > 0){
15770 e.preventDefault();
15771 if($(this).attr("data-read-more-action") == "close"){
15772 readMoreBloc.addClass("--closed").removeClass("--open");
15773 $("html, body").animate({
15774 scrollTop: readMoreBloc.offset().top - 200
15775 }, 100);
15776 }else{
15777 readMoreBloc.addClass("--open").removeClass("--closed");
15778 }
15779 }
15780 });
15781 });
15782 </script>';
15783
15784
15785 if (empty($histo)) {
15786 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
15787 }
15788 }
15789
15790 if ($noprint) {
15791 return $out;
15792 } else {
15793 print $out;
15794 return null;
15795 }
15796}
15797
15809function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
15810{
15811 if ($timestamp === null) {
15812 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
15813 }
15814 $TParam = array(
15815 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
15816 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
15817 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
15818 );
15819 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
15820 $TParam = array_merge($TParam, array(
15821 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
15822 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
15823 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
15824 ));
15825 }
15826
15827 return '&' . http_build_query($TParam);
15828}
15829
15848function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
15849{
15850 global $conf, $db, $langs, $hookmanager;
15851 global $action, $object;
15852
15853 if (!is_object($langs)) {
15854 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
15855 $langs = new Translate('', $conf);
15856 $langs->setDefaultLang();
15857 }
15858
15859 $langs->load("errors");
15860
15861 if ($printheader) {
15862 if (function_exists("llxHeader")) {
15863 llxHeader('');
15864 } elseif (function_exists("llxHeaderVierge")) {
15865 llxHeaderVierge('');
15866 }
15867 }
15868
15869 print '<div class="error">';
15870 if (empty($message)) {
15871 print $langs->trans("ErrorRecordNotFound");
15872 } else {
15873 print $langs->trans($message);
15874 }
15875 print '</div>';
15876 print '<br>';
15877
15878 if (empty($showonlymessage)) {
15879 if (empty($hookmanager)) {
15880 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
15881 $hookmanager = new HookManager($db);
15882 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
15883 $hookmanager->initHooks(array('main'));
15884 }
15885
15886 $parameters = array('message' => $message, 'params' => $params);
15887 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
15888 print $hookmanager->resPrint;
15889 }
15890
15891 if ($printfooter && function_exists("llxFooter")) {
15892 llxFooter();
15893 if (is_object($db)) {
15894 $db->close();
15895 }
15896 }
15897 exit(0);
15898}
$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