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 .= " AND t.active > 0";
7973 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7974 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7975
7976 $res = $db->query($sql);
7977 if ($res) {
7978 if ($db->num_rows($res)) {
7979 $obj = $db->fetch_object($res);
7980 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7981 }
7982 $db->free($res);
7983 }
7984 }
7985
7986 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7987 if (($seller_country_code == $buyer_country_code)
7988 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7989 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7990 ) { // Warning ->country_code not always defined
7991 //print 'VATRULE 3';
7992 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7993
7994 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7995 // Special case for india.
7996 //print 'VATRULE 3b';
7997 $reg = array();
7998 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7999 // we must revert the C+S into I
8000 $tmpvat = str_replace("C+S", "I", $tmpvat);
8001 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
8002 // we must revert the I into C+S
8003 $tmpvat = str_replace("I", "C+S", $tmpvat);
8004 }
8005 }
8006
8007 return $tmpvat;
8008 }
8009
8010 // 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.
8011 // 'VATRULE 4' - Not supported
8012
8013 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
8014 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
8015 if (($seller_in_cee && $buyer_in_cee)) {
8016 $isacompany = $thirdparty_buyer->isACompany();
8017 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
8018 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
8019 if (!isValidVATID($thirdparty_buyer)) {
8020 $isacompany = 0;
8021 }
8022 }
8023
8024 if (!$isacompany) {
8025 //print 'VATRULE 5';
8026 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8027 } else {
8028 //print 'VATRULE 6';
8029 return 0;
8030 }
8031 }
8032
8033 // 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
8034 // I don't see any use case that need this rule.
8035 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
8036 $isacompany = $thirdparty_buyer->isACompany();
8037 if (!$isacompany) {
8038 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
8039 //print 'VATRULE extra';
8040 }
8041 }
8042
8043 // Otherwise the VAT proposed by default=0. End of rule.
8044 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
8045 //print 'VATRULE 7';
8046 return 0;
8047}
8048
8049
8060function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
8061{
8062 global $db;
8063
8064 if ($idprodfournprice > 0) {
8065 if (!class_exists('ProductFournisseur')) {
8066 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8067 }
8068 $prodprice = new ProductFournisseur($db);
8069 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
8070 return $prodprice->fourn_tva_npr;
8071 } elseif ($idprod > 0) {
8072 if (!class_exists('Product')) {
8073 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8074 }
8075 $prod = new Product($db);
8076 $prod->fetch($idprod);
8077 return $prod->tva_npr;
8078 }
8079
8080 return 0;
8081}
8082
8096function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
8097{
8098 global $mysoc;
8099
8100 if (!is_object($thirdparty_seller)) {
8101 return -1;
8102 }
8103 if (!is_object($thirdparty_buyer)) {
8104 return -1;
8105 }
8106
8107 if (empty($thirdparty_seller->country_code)) {
8108 $thirdparty_seller->country_code = $mysoc->country_code;
8109 }
8110 $seller_country_code = $thirdparty_seller->country_code;
8111 //$seller_in_cee = isInEEC($thirdparty_seller);
8112
8113 if (empty($thirdparty_buyer->country_code)) {
8114 $thirdparty_buyer->country_code = $mysoc->country_code;
8115 }
8116 $buyer_country_code = $thirdparty_buyer->country_code;
8117 //$buyer_in_cee = isInEEC($thirdparty_buyer);
8118
8119 if ($local == 1) { // Localtax 1
8120 if ($mysoc->country_code == 'ES') {
8121 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
8122 return 0;
8123 }
8124 } else {
8125 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
8126 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
8127 return 0;
8128 }
8129 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
8130 return 0;
8131 }
8132 }
8133 } elseif ($local == 2) { //I Localtax 2
8134 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
8135 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
8136 return 0;
8137 }
8138 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
8139 return 0;
8140 }
8141 }
8142
8143 if ($seller_country_code == $buyer_country_code) {
8144 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
8145 }
8146
8147 return 0;
8148}
8149
8158function yn($yesno, $format = 1, $color = 0)
8159{
8160 global $langs;
8161
8162 $result = 'unknown';
8163 $classname = '';
8164 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
8165 $result = $langs->trans('yes');
8166 if ($format == 1 || $format == 3) {
8167 $result = $langs->trans("Yes");
8168 }
8169 if ($format == 2) {
8170 $result = '<input type="checkbox" value="1" checked disabled>';
8171 }
8172 if ($format == 3) {
8173 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
8174 }
8175 if ($format == 4 || !is_numeric($format)) {
8176 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
8177 }
8178
8179 $classname = 'ok';
8180 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
8181 $result = $langs->trans("no");
8182 if ($format == 1 || $format == 3) {
8183 $result = $langs->trans("No");
8184 }
8185 if ($format == 2) {
8186 $result = '<input type="checkbox" value="0" disabled>';
8187 }
8188 if ($format == 3) {
8189 $result = '<input type="checkbox" value="0" disabled> '.$result;
8190 }
8191 if ($format == 4 || !is_numeric($format)) {
8192 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8193 }
8194
8195 if ($color == 2) {
8196 $classname = 'ok';
8197 } else {
8198 $classname = 'error';
8199 }
8200 }
8201 if ($color) {
8202 return '<span class="'.$classname.'">'.$result.'</span>';
8203 }
8204 return $result;
8205}
8206
8225function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8226{
8227 if (empty($modulepart) && is_object($object)) {
8228 if (!empty($object->module)) {
8229 $modulepart = $object->module;
8230 } elseif (!empty($object->element)) {
8231 $modulepart = $object->element;
8232 }
8233 }
8234
8235 $path = '';
8236
8237 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8238 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8239 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8240 $arrayforoldpath['product'] = 2;
8241 }
8242
8243 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8244 $level = $arrayforoldpath[$modulepart];
8245 }
8246 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8247 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8248 if (empty($num) && is_object($object)) {
8249 $num = $object->id;
8250 }
8251 if (empty($alpha)) {
8252 $num = preg_replace('/([^0-9])/i', '', $num);
8253 } else {
8254 $num = preg_replace('/^.*\-/i', '', $num);
8255 }
8256 $num = substr("000".$num, -$level);
8257 if ($level == 1) {
8258 $path = substr($num, 0, 1);
8259 }
8260 if ($level == 2) {
8261 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
8262 }
8263 if ($level == 3) {
8264 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
8265 }
8266 } else {
8267 // We will enhance here a common way of forging path for document storage.
8268 // In a future, we may distribute directories on several levels depending on setup and object.
8269 // Here, $object->id, $object->ref and $modulepart are required.
8270 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
8271 // 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
8272 $path = dol_sanitizeFileName((string) $object->id);
8273 } else {
8274 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8275 }
8276 }
8277
8278 if (empty($withoutslash) && !empty($path)) {
8279 $path .= '/';
8280 }
8281
8282 return $path;
8283}
8284
8293function dol_mkdir($dir, $dataroot = '', $newmask = '')
8294{
8295 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
8296
8297 $dir = dol_sanitizePathName($dir, '_', 0);
8298
8299 $dir_osencoded = dol_osencode($dir);
8300 if (@is_dir($dir_osencoded)) {
8301 return 0;
8302 }
8303
8304 $nberr = 0;
8305 $nbcreated = 0;
8306
8307 $ccdir = '';
8308 if (!empty($dataroot)) {
8309 // Remove data root from loop
8310 $dir = str_replace($dataroot.'/', '', $dir);
8311 $ccdir = $dataroot.'/';
8312 }
8313
8314 $cdir = explode("/", $dir);
8315 $num = count($cdir);
8316 for ($i = 0; $i < $num; $i++) {
8317 if ($i > 0) {
8318 $ccdir .= '/'.$cdir[$i];
8319 } else {
8320 $ccdir .= $cdir[$i];
8321 }
8322 $regs = array();
8323 if (preg_match("/^.:$/", $ccdir, $regs)) {
8324 continue; // If the Windows path is incomplete, continue with next directory
8325 }
8326
8327 // Attention, is_dir() can fail event if the directory exists
8328 // (i.e. according the open_basedir configuration)
8329 if ($ccdir) {
8330 $ccdir_osencoded = dol_osencode($ccdir);
8331 if (!@is_dir($ccdir_osencoded)) {
8332 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8333
8334 umask(0);
8335 $dirmaskdec = octdec((string) $newmask);
8336 if (empty($newmask)) {
8337 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8338 }
8339 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8340 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8341 // If the is_dir has returned a false information, we arrive here
8342 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' (no permission to write into parent or directory already exists).", LOG_WARNING);
8343 $nberr++;
8344 } else {
8345 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
8346 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8347 $nbcreated++;
8348 }
8349 } else {
8350 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8351 }
8352 }
8353 }
8354 return ($nberr ? -$nberr : $nbcreated);
8355}
8356
8357
8365function dolChmod($filepath, $newmask = '')
8366{
8367 if (!empty($newmask)) {
8368 @chmod($filepath, octdec($newmask));
8369 } elseif (getDolGlobalString('MAIN_UMASK')) {
8370 @chmod($filepath, octdec(getDolGlobalString('MAIN_UMASK')));
8371 }
8372}
8373
8374
8380function picto_required()
8381{
8382 return '<span class="fieldrequired">*</span>';
8383}
8384
8385
8402function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8403{
8404 if (is_null($stringtoclean)) {
8405 return '';
8406 }
8407
8408 if ($removelinefeed == 2) {
8409 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8410 }
8411 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8412
8413 // 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)
8414 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8415
8416 $temp = str_replace('< ', '__ltspace__', $temp);
8417 $temp = str_replace('<:', '__lttwopoints__', $temp);
8418
8419 if ($strip_tags) {
8420 $temp = strip_tags($temp);
8421 } else {
8422 // 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).
8423 $pattern = "/<[^<>]+>/";
8424 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8425 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8426 // pass 2 - $temp after pass 2: 0000-021
8427 $tempbis = $temp;
8428 do {
8429 $temp = $tempbis;
8430 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8431 $tempbis = preg_replace($pattern, '', $tempbis);
8432 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8433 } while ($tempbis != $temp);
8434
8435 $temp = $tempbis;
8436
8437 // Remove '<' into remaining, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
8438 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8439 }
8440
8441 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8442
8443 // Remove also carriage returns
8444 if ($removelinefeed == 1) {
8445 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8446 }
8447
8448 // And double spaces
8449 if ($removedoublespaces) {
8450 while (strpos($temp, " ") !== false) {
8451 $temp = str_replace(" ", " ", $temp);
8452 }
8453 }
8454
8455 $temp = str_replace('__ltspace__', '< ', $temp);
8456 $temp = str_replace('__lttwopoints__', '<:', $temp);
8457
8458 return trim($temp);
8459}
8460
8480function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
8481{
8482 if (empty($allowed_tags)) {
8483 $allowed_tags = array(
8484 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8485 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8486 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8487 );
8488 }
8489 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8490 if ($allowiframe) {
8491 if (!in_array('iframe', $allowed_tags)) {
8492 $allowed_tags[] = "iframe";
8493 }
8494 }
8495 if ($allowlink) {
8496 if (!in_array('link', $allowed_tags)) {
8497 $allowed_tags[] = "link";
8498 }
8499 }
8500 if ($allowscript) {
8501 if (!in_array('script', $allowed_tags)) {
8502 $allowed_tags[] = "script";
8503 }
8504 }
8505 if ($allowstyle) {
8506 if (!in_array('style', $allowed_tags)) {
8507 $allowed_tags[] = "style";
8508 }
8509 }
8510
8511 $allowed_tags_string = implode("><", $allowed_tags);
8512 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8513
8514 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8515
8516 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8517
8518 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8519 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8520
8521 if ($allowphp) {
8522 $allowed_tags[] = "commentphp";
8523 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
8524 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
8525 }
8526
8527 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8528 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8529
8530 // Remove all HTML tags
8531 $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
8532
8533 if ($cleanalsosomestyles) { // Clean for remaining html tags
8534 $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
8535 }
8536 if ($removeclassattribute) { // Clean for remaining html tags
8537 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8538 }
8539
8540 // Remove 'javascript:' that we should not find into a text
8541 // 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)).
8542 if ($cleanalsojavascript) {
8543 $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);
8544 }
8545
8546 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8547
8548 if ($allowphp) {
8549 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
8550 }
8551
8552 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8553
8554
8555 return $temp;
8556}
8557
8558
8571function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8572{
8573 if (is_null($allowed_attributes)) {
8574 $allowed_attributes = array(
8575 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8576 // HTML5
8577 "header", "footer", "nav", "section", "menu", "menuitem"
8578 );
8579 }
8580 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8581 if (!in_array("content", $allowed_attributes)) {
8582 $allowed_attributes[] = "content";
8583 }
8584 if (!in_array("http-equiv", $allowed_attributes)) {
8585 $allowed_attributes[] = "http-equiv";
8586 }
8587
8588 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8589 //$stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8590 $stringtoclean = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8591
8592 // Warning: loadHTML does not support HTML5 on old libxml versions.
8593 $dom = new DOMDocument('', 'UTF-8');
8594 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8595 $savwarning = error_reporting();
8596 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8597 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8598 error_reporting($savwarning);
8599
8600 if ($dom instanceof DOMDocument) {
8601 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8602 $el = $els->item($i);
8603 if (!$el instanceof DOMElement) {
8604 continue;
8605 }
8606 $attrs = $el->attributes;
8607 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8608 //var_dump($attrs->item($ii));
8609 if (!empty($attrs->item($ii)->name)) {
8610 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8611 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8612 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8613 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8614 // If attribute is 'style'
8615 $valuetoclean = $attrs->item($ii)->value;
8616
8617 if (isset($valuetoclean)) {
8618 do {
8619 $oldvaluetoclean = $valuetoclean;
8620 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8621 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8622 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8623 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8624 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8625 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8626 }
8627
8628 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8629 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8630 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8631 } while ($oldvaluetoclean != $valuetoclean);
8632 }
8633
8634 $attrs->item($ii)->value = $valuetoclean;
8635 }
8636 }
8637 }
8638 }
8639 }
8640
8641 $dom->encoding = 'UTF-8';
8642
8643 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8644 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8645
8646 //$return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8647 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8648 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8649
8650 return trim($return);
8651 } else {
8652 return $stringtoclean;
8653 }
8654}
8655
8667function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8668{
8669 $temp = $stringtoclean;
8670 foreach ($disallowed_tags as $tagtoremove) {
8671 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8672 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8673 }
8674
8675 if ($cleanalsosomestyles) {
8676 $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
8677 }
8678
8679 return $temp;
8680}
8681
8682
8692function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8693{
8694 if ($nboflines == 1) {
8695 if (dol_textishtml($text)) {
8696 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8697 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8698 } else {
8699 if (isset($text)) {
8700 $firstline = preg_replace('/[\n\r].*/', '', $text);
8701 } else {
8702 $firstline = '';
8703 }
8704 }
8705 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8706 } else {
8707 $ishtml = 0;
8708 if (dol_textishtml($text)) {
8709 $text = preg_replace('/\n/', '', $text);
8710 $ishtml = 1;
8711 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8712 } else {
8713 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8714 }
8715
8716 $text = strtr($text, $repTable);
8717 if ($charset == 'UTF-8') {
8718 $pattern = '/(<br[^>]*>)/Uu';
8719 } else {
8720 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8721 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8722 }
8723 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8724
8725 $firstline = '';
8726 $i = 0;
8727 $countline = 0;
8728 $lastaddediscontent = 1;
8729 while ($countline < $nboflines && isset($a[$i])) {
8730 if (preg_match('/<br[^>]*>/', $a[$i])) {
8731 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8732 $firstline .= ($ishtml ? "<br>\n" : "\n");
8733 // Is it a br for a new line of after a printed line ?
8734 if (!$lastaddediscontent) {
8735 $countline++;
8736 }
8737 $lastaddediscontent = 0;
8738 }
8739 } else {
8740 $firstline .= $a[$i];
8741 $lastaddediscontent = 1;
8742 $countline++;
8743 }
8744 $i++;
8745 }
8746
8747 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8748 //unset($a);
8749 $ret = $firstline.($adddots ? '...' : '');
8750 //exit;
8751 return $ret;
8752 }
8753}
8754
8755
8767function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8768{
8769 if (is_null($stringtoencode)) {
8770 return '';
8771 }
8772
8773 if (!$nl2brmode) {
8774 return nl2br($stringtoencode, $forxml);
8775 } else {
8776 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8777 return $ret;
8778 }
8779}
8780
8790function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8791{
8792 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8793 // TODO using sandbox on inline html content is not possible yet with current browsers
8794 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8795 //$s .= $stringtoencode;
8796 //$s .= '</body></html></iframe>';
8797 return $stringtoencode;
8798 } else {
8799 $out = $stringtoencode;
8800
8801 // First clean HTML content
8802 do {
8803 $oldstringtoclean = $out;
8804
8805 $outishtml = 0;
8806 if (dol_textishtml($out)) {
8807 $outishtml = 1;
8808 }
8809
8810 // HTML sanitizer by DOMDocument
8811 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8812 try {
8813 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8814 if (LIBXML_VERSION < 20900) {
8815 // Avoid load of external entities (security problem).
8816 // Required only if LIBXML_VERSION < 20900
8817 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8818 libxml_disable_entity_loader(true);
8819 }
8820
8821 $dom = new DOMDocument();
8822 // Add a trick '<div class="tricktoremove">' to solve pb with text without parent tag
8823 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8824 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8825 // Add also a trick <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"> to solve utf8 lost.
8826 // I don't know what the xml encoding is the trick for
8827 if ($outishtml) {
8828 //$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>';
8829 $out = '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8830 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8831 } else {
8832 //$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>';
8833 $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>';
8834 //$out = '<html><head><meta charset="utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
8835 }
8836
8837 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8838
8839 $dom->encoding = 'UTF-8';
8840
8841 $out = trim($dom->saveHTML());
8842
8843 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8844 //$out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8845 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8846 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8847 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8848 // $out = preg_replace('/<\/div>$/', '', $out);
8849 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8850
8851 if (!$outishtml) { // If $out was not HTML content we made before a dol_nl2br so we must do the opposite operation now
8852 $out = str_replace('<br>', '', $out);
8853 }
8854 } catch (Exception $e) {
8855 // If error, invalid HTML string with no way to clean it
8856 //print $e->getMessage();
8857 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8858 }
8859 }
8860
8861 // HTML sanitizer by Tidy
8862 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8863 // Tidy can't be used for non html text content as it is corrupting the new lines fields.
8864 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript')) && $outishtml) {
8865 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8866 try {
8867 //var_dump($out);
8868
8869 // Try cleaning using tidy
8870 if (extension_loaded('tidy') && class_exists("tidy")) {
8871 //print "aaa".$out."\n";
8872
8873 // See options at https://tidy.sourceforge.net/docs/quickref.html
8874 $config = array(
8875 'clean' => false,
8876 // 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;
8877 'quote-marks' => false,
8878 'doctype' => 'strict',
8879 'show-body-only' => true,
8880 "indent-attributes" => false,
8881 "vertical-space" => false,
8882 //'ident' => false, // Not always supported
8883 "wrap" => 0,
8884 'preserve-entities' => true
8885 // HTML5 tags
8886 //'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',
8887 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8888 //'new-empty-tags' => 'command embed keygen source track wbr',
8889 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8890 );
8891
8892 // Tidy
8893 $tidy = new tidy();
8894 $out = $tidy->repairString($out, $config, 'utf8');
8895
8896 //print "xxx".$out;exit;
8897 }
8898
8899 //var_dump($out);
8900 } catch (Exception $e) {
8901 // If error, invalid HTML string with no way to clean it
8902 //print $e->getMessage();
8903 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8904 }
8905 }
8906
8907 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8908 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8909
8910 // Clean some html entities that are useless so text is cleaner
8911 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8912
8913 // Ckeditor uses the numeric entity for apostrophe, so we force it to
8914 // the text entity (all other special chars are encoded using text entities) so we can then exclude all numeric entities.
8915 $out = preg_replace('/&#39;/i', '&apos;', $out);
8916
8917 // 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).
8918 // 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
8919 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8920 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
8921 $out = preg_replace_callback(
8922 '/&#(x?[0-9][0-9a-f]+;?)/i',
8927 static function ($m) {
8928 return realCharForNumericEntities($m);
8929 },
8930 $out
8931 );
8932 }
8933
8934 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8935 $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'.
8936
8937 // Keep only some html tags and remove also some 'javascript:' strings
8938 if ($check == 'restricthtmlallowlinkscript') {
8939 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
8940 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8941 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8942 } elseif ($check == 'restricthtmlallowiframe') {
8943 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
8944 } else {
8945 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8946 }
8947
8948 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8949 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8951 }
8952
8953 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity) because it is
8954 // compatible with HTML 4 used y CKEditor, and HTML 5 (when &apos; works only with HTML5).
8955 $out = preg_replace('/&apos;/i', "&#39;", $out);
8956
8957 // Now remove js
8958 // 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
8959 $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)>
8960 $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);
8961 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8962 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8963 $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);
8964 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8965 // More not into the previous list
8966 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8967 } while ($oldstringtoclean != $out);
8968
8969 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8970 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8971 // 'url(' to avoid inline style like background: url(http...
8972 // '<link' to avoid <link href="http...">
8973 $reg = array();
8974 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8975 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8976 $nblinks = count($reg[0]);
8977 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8978 $out = 'ErrorTooManyLinksIntoHTMLString';
8979 }
8980
8981 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8982 if ($nblinks > 0) {
8983 $out = 'ErrorHTMLLinksNotAllowed';
8984 }
8985 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8986 $nblinks = 0;
8987 // Loop on each url in src= and url(
8988 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8989
8990 $matches = array();
8991 if (preg_match_all($pattern, $out, $matches)) {
8992 // URLs are into $matches[1]
8993 $urls = $matches[1];
8994
8995 // Affiche les URLs
8996 foreach ($urls as $url) {
8997 $nblinks++;
8998 echo "Found url = ".$url . "\n";
8999 }
9000 if ($nblinks > 0) {
9001 $out = 'ErrorHTMLExternalLinksNotAllowed';
9002 }
9003 }
9004 }
9005
9006 return $out;
9007 }
9008}
9009
9030function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
9031{
9032 if (is_null($stringtoencode)) {
9033 return '';
9034 }
9035
9036 $newstring = $stringtoencode;
9037 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
9038 $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.
9039 if ($removelasteolbr) {
9040 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
9041 }
9042 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
9043 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
9044 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
9045 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
9046 } else {
9047 if ($removelasteolbr) {
9048 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
9049 }
9050 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
9051 }
9052 // Other substitutions that htmlentities does not do
9053 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
9054 return $newstring;
9055}
9056
9064function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
9065{
9066 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
9067 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
9068 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
9069 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
9070 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
9071 return $ret;
9072}
9073
9080function dol_htmlcleanlastbr($stringtodecode)
9081{
9082 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
9083 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
9084 return $ret;
9085}
9086
9096function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
9097{
9098 $newstring = $a;
9099 if ($keepsomeentities) {
9100 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
9101 }
9102 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
9103 if ($keepsomeentities) {
9104 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
9105 }
9106 return $newstring;
9107}
9108
9120function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
9121{
9122 return htmlentities($string, $flags, $encoding, $double_encode);
9123}
9124
9136function dol_string_is_good_iso($s, $clean = 0)
9137{
9138 $len = dol_strlen($s);
9139 $out = '';
9140 $ok = 1;
9141 for ($scursor = 0; $scursor < $len; $scursor++) {
9142 $ordchar = ord($s[$scursor]);
9143 //print $scursor.'-'.$ordchar.'<br>';
9144 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
9145 $ok = 0;
9146 break;
9147 } elseif ($ordchar > 126 && $ordchar < 160) {
9148 $ok = 0;
9149 break;
9150 } elseif ($clean) {
9151 $out .= $s[$scursor];
9152 }
9153 }
9154 if ($clean) {
9155 return $out;
9156 }
9157 return $ok;
9158}
9159
9168function dol_nboflines($s, $maxchar = 0)
9169{
9170 if ($s == '') {
9171 return 0;
9172 }
9173 $arraystring = explode("\n", $s);
9174 $nb = count($arraystring);
9175
9176 return $nb;
9177}
9178
9179
9189function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
9190{
9191 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
9192 if (dol_textishtml($text)) {
9193 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
9194 }
9195
9196 $text = strtr($text, $repTable);
9197 if ($charset == 'UTF-8') {
9198 $pattern = '/(<br[^>]*>)/Uu';
9199 } else {
9200 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
9201 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
9202 }
9203 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9204
9205 $nblines = (int) floor((count($a) + 1) / 2);
9206 // count possible auto line breaks
9207 if ($maxlinesize) {
9208 foreach ($a as $line) {
9209 if (dol_strlen($line) > $maxlinesize) {
9210 //$line_dec = html_entity_decode(strip_tags($line));
9211 $line_dec = html_entity_decode($line);
9212 if (dol_strlen($line_dec) > $maxlinesize) {
9213 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
9214 $nblines += substr_count($line_dec, '\n');
9215 }
9216 }
9217 }
9218 }
9219
9220 unset($a);
9221 return $nblines;
9222}
9223
9232function dol_textishtml($msg, $option = 0)
9233{
9234 if (is_null($msg)) {
9235 return false;
9236 }
9237
9238 if ($option == 1) {
9239 if (preg_match('/<(html|link|script)/i', $msg)) {
9240 return true;
9241 } elseif (preg_match('/<body/i', $msg)) {
9242 return true;
9243 } elseif (preg_match('/<\/textarea/i', $msg)) {
9244 return true;
9245 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9246 return true;
9247 } elseif (preg_match('/<br/i', $msg)) {
9248 return true;
9249 }
9250 return false;
9251 } else {
9252 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9253 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9254 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9255 return true;
9256 } elseif (preg_match('/<\/textarea/i', $msg)) {
9257 return true;
9258 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9259 return true;
9260 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9261 return true;
9262 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9263 return true;
9264 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9265 return true;
9266 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9267 return true; // must accept <img src="http://example.com/aaa.png" />
9268 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9269 return true; // must accept <a href="http://example.com/aaa.png" />
9270 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9271 return true;
9272 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9273 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9274 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9275 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9276 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9277 } elseif (preg_match('/&#x[a-f0-9][a-f0-9];/i', $msg)) {
9278 return true; // Html entities numbers in hexa
9279 }
9280
9281 return false;
9282 }
9283}
9284
9299function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
9300{
9301 if (!empty($invert)) {
9302 $tmp = $text1;
9303 $text1 = $text2;
9304 $text2 = $tmp;
9305 }
9306
9307 $ret = '';
9308 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
9309 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
9310 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
9311 return $ret;
9312}
9313
9314
9315
9329function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
9330{
9331 global $db, $conf, $mysoc, $user, $extrafields;
9332
9333 $substitutionarray = array();
9334
9335 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
9336 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
9337 // this will include signature content first and then replace var found into content of signature
9338 //var_dump($onlykey);
9339 $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()
9340 $usersignature = $user->signature;
9341 $substitutionarray = array_merge($substitutionarray, array(
9342 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
9343 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
9344 ));
9345
9346 if (is_object($user) && ($user instanceof User)) {
9347 $substitutionarray = array_merge($substitutionarray, array(
9348 '__USER_ID__' => (string) $user->id,
9349 '__USER_LOGIN__' => (string) $user->login,
9350 '__USER_EMAIL__' => (string) $user->email,
9351 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
9352 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
9353 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
9354 '__USER_FAX__' => (string) $user->office_fax,
9355 '__USER_LASTNAME__' => (string) $user->lastname,
9356 '__USER_FIRSTNAME__' => (string) $user->firstname,
9357 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
9358 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
9359 '__USER_JOB__' => (string) $user->job,
9360 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
9361 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
9362 ));
9363 }
9364 }
9365 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
9366 $substitutionarray = array_merge($substitutionarray, array(
9367 '__MYCOMPANY_NAME__' => $mysoc->name,
9368 '__MYCOMPANY_EMAIL__' => $mysoc->email,
9369 '__MYCOMPANY_URL__' => $mysoc->url,
9370 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
9371 '__MYCOMPANY_PHONEMOBILE__' => dol_print_phone($mysoc->phone_mobile, '', 0, 0, '', " ", '', '', -1),
9372 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
9373 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
9374 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
9375 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
9376 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
9377 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
9378 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
9379 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
9380 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
9381 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
9382 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
9383 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
9384 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
9385 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
9386 '__MYCOMPANY_ZIP__' => $mysoc->zip,
9387 '__MYCOMPANY_TOWN__' => $mysoc->town,
9388 '__MYCOMPANY_STATE__' => $mysoc->state,
9389 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
9390 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
9391 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
9392 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
9393 ));
9394 }
9395
9396 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
9397 if ($onlykey) {
9398 $substitutionarray['__ID__'] = '__ID__';
9399 $substitutionarray['__REF__'] = '__REF__';
9400 $substitutionarray['__NEWREF__'] = '__NEWREF__';
9401 $substitutionarray['__LABEL__'] = '__LABEL__';
9402 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
9403 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
9404 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
9405 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
9406 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
9407
9408 if (isModEnabled("societe")) { // Most objects are concerned
9409 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
9410 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
9411 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
9412 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
9413 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
9414 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
9415 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
9416 $substitutionarray['__THIRDPARTY_URL__'] = '__THIRDPARTY_URL__';
9417 //$substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = '__THIRDPARTY_URL_URLENCODED__'; // We hide this one
9418 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
9419 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
9420 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
9421 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
9422 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
9423 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
9424 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
9425 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
9426 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
9427 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
9428 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
9429 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
9430 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
9431 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
9432 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
9433 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
9434 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
9435 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
9436 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
9437 }
9438 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9439 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9440 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9441 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9442 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9443 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9444 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9445 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9446 }
9447 // add substitution variables for ticket
9448 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9449 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9450 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9451 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9452 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9453 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9454 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9455 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9456 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9457 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9458 }
9459
9460 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9461 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9462 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9463 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9464 }
9465 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9466 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9467 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9468 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9469 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9470 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9471 }
9472 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9473 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9474 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9475 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9476 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9477 }
9478 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9479 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9480 }
9481 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9482 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9483 }
9484 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9485 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9486 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9487 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9488 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9489 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9490 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9491
9492 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9493 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9494 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9495 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9496 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9497
9498 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9499 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9500 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9501 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9502 }
9503 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9504 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9505 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9506 }
9507 } else {
9508 '@phan-var-force Adherent|Delivery $object';
9510 $substitutionarray['__ID__'] = $object->id;
9511 $substitutionarray['__REF__'] = $object->ref;
9512 $substitutionarray['__NEWREF__'] = $object->newref;
9513 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9514 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9515 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9516 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9517 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9518
9519 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9520 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9521 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9522
9523 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
9524 $date_delivery = null;
9525 if (property_exists($object, 'date_delivery')) {
9526 $date_delivery = $object->date_delivery;
9527 } elseif (property_exists($object, 'delivery_date')) {
9528 $date_delivery = $object->delivery_date;
9529 }
9530 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9531 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
9532 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
9533 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
9534 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
9535 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
9536 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
9537 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
9538 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
9539
9540 // For backward compatibility (deprecated)
9541 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9542 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9543
9544 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9545 $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 : '')) : '');
9546 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9547
9548 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9549 '@phan-var-force Adherent $object';
9551 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9552
9553 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9554 if (method_exists($object, 'getCivilityLabel')) {
9555 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9556 }
9557 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9558 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9559 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9560 if (method_exists($object, 'getFullName')) {
9561 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9562 }
9563 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9564 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9565 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9566 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9567 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9568 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9569 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9570 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9571 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9572 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9573 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9574 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9575 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9576 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9577 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9578 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9579
9580 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9581 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9582 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9583 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9584 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9585 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9586 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9587 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9588 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9589 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9590 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9591 }
9592
9593 if (is_object($object) && $object->element == 'societe') {
9595 '@phan-var-force Societe $object';
9596 $substitutionarray['__THIRDPARTY_ID__'] = $object->id ?? '';
9597 $substitutionarray['__THIRDPARTY_NAME__'] = $object->name ?? '';
9598 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->name_alias ?? '';
9599 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->code_client ?? '';
9600 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->code_fournisseur ?? '';
9601 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->email ?? '';
9602 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->email ?? '');
9603 $substitutionarray['__THIRDPARTY_URL__'] = $object->url ?? '';
9604 $substitutionarray['__THIRDPARTY_URL_URLENCODED__'] = urlencode($object->url ?? '');
9605 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->phone ?? '');
9606 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->fax ?? '');
9607 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->address ?? '';
9608 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->zip ?? '';
9609 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->town ?? '';
9610 $substitutionarray['__THIRDPARTY_STATE__'] = $object->state ?? '';
9611 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->country_id > 0 ?: '');
9612 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->country_code ?? '';
9613 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->idprof1 ?? '';
9614 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->idprof2 ?? '';
9615 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->idprof3 ?? '';
9616 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->idprof4 ?? '';
9617 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->idprof5 ?? '';
9618 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->idprof6 ?? '';
9619 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->tva_intra ?? '';
9620 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->note_public ?? '');
9621 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->note_private ?? '');
9622 } elseif (is_object($object) && is_object($object->thirdparty)) {
9623 $substitutionarray['__THIRDPARTY_ID__'] = $object->thirdparty->id ?? '';
9624 $substitutionarray['__THIRDPARTY_NAME__'] = $object->thirdparty->name ?? '';
9625 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = $object->thirdparty->name_alias ?? '';
9626 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = $object->thirdparty->code_client ?? '';
9627 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = $object->thirdparty->code_fournisseur ?? '';
9628 $substitutionarray['__THIRDPARTY_EMAIL__'] = $object->thirdparty->email ?? '';
9629 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode($object->thirdparty->email ?? '');
9630 $substitutionarray['__THIRDPARTY_PHONE__'] = dol_print_phone($object->thirdparty->phone ?? '');
9631 $substitutionarray['__THIRDPARTY_FAX__'] = dol_print_phone($object->thirdparty->fax ?? '');
9632 $substitutionarray['__THIRDPARTY_ADDRESS__'] = $object->thirdparty->address ?? '';
9633 $substitutionarray['__THIRDPARTY_ZIP__'] = $object->thirdparty->zip ?? '';
9634 $substitutionarray['__THIRDPARTY_TOWN__'] = $object->thirdparty->town ?? '';
9635 $substitutionarray['__THIRDPARTY_STATE__'] = $object->thirdparty->state ?? '';
9636 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = ($object->thirdparty->country_id > 0 ?: '');
9637 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = $object->thirdparty->country_code ?? '';
9638 $substitutionarray['__THIRDPARTY_IDPROF1__'] = $object->thirdparty->idprof1 ?? '';
9639 $substitutionarray['__THIRDPARTY_IDPROF2__'] = $object->thirdparty->idprof2 ?? '';
9640 $substitutionarray['__THIRDPARTY_IDPROF3__'] = $object->thirdparty->idprof3 ?? '';
9641 $substitutionarray['__THIRDPARTY_IDPROF4__'] = $object->thirdparty->idprof4 ?? '';
9642 $substitutionarray['__THIRDPARTY_IDPROF5__'] = $object->thirdparty->idprof5 ?? '';
9643 $substitutionarray['__THIRDPARTY_IDPROF6__'] = $object->thirdparty->idprof6 ?? '';
9644 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = $object->thirdparty->tva_intra ?? '';
9645 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = dol_htmlentitiesbr($object->thirdparty->note_public ?? '');
9646 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = dol_htmlentitiesbr($object->thirdparty->note_private ?? '');
9647 }
9648
9649 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9650 '@phan-var-force RecruitmentCandidature $object';
9652 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9653 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9654 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9655 }
9656 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9657 '@phan-var-force ConferenceOrBoothAttendee $object';
9659 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9660 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9661 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9662 }
9663
9664 if (is_object($object) && $object->element == 'project') {
9665 '@phan-var-force Project $object';
9667 $substitutionarray['__PROJECT_ID__'] = $object->id;
9668 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9669 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9670 } elseif (is_object($object)) {
9671 $project = null;
9672 if (!empty($object->project)) {
9673 $project = $object->project;
9674 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9675 $project = $object->projet;
9676 }
9677 if (!is_null($project) && is_object($project)) {
9678 $substitutionarray['__PROJECT_ID__'] = $project->id;
9679 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9680 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9681 } else {
9682 // can substitute variables for project : uses lazy load in "make_substitutions" method
9683 $project_id = 0;
9684 if (!empty($object->fk_project) && $object->fk_project > 0) {
9685 $project_id = $object->fk_project;
9686 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9687 $project_id = $object->fk_project;
9688 }
9689 if ($project_id > 0) {
9690 // path:class:method:id
9691 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9692 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9693 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9694 }
9695 }
9696 }
9697
9698 if (is_object($object) && $object->element == 'facture') {
9699 '@phan-var-force Facture $object';
9701 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9702 }
9703 if (is_object($object) && $object->element == 'shipping') {
9704 '@phan-var-force Expedition $object';
9706 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9707 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9708 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9709 }
9710 if (is_object($object) && $object->element == 'reception') {
9711 '@phan-var-force Reception $object';
9713 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9714 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9715 }
9716
9717 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9718 '@phan-var-force Contrat $object';
9720 $dateplannedstart = '';
9721 $datenextexpiration = '';
9722 foreach ($object->lines as $line) {
9723 if ($line->date_start > $dateplannedstart) {
9724 $dateplannedstart = $line->date_start;
9725 }
9726 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9727 $datenextexpiration = $line->date_end;
9728 }
9729 }
9730 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9731 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9732 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9733
9734 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9735 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9736 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9737 }
9738 // add substitution variables for ticket
9739 if (is_object($object) && $object->element == 'ticket') {
9740 '@phan-var-force Ticket $object';
9742 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9743 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9744 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9745 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9746 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9747 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9748 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9749 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9750 $userstat = new User($db);
9751 if ($object->fk_user_assign > 0) {
9752 $userstat->fetch($object->fk_user_assign);
9753 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9754 }
9755
9756 if ($object->fk_user_create > 0) {
9757 $userstat->fetch($object->fk_user_create);
9758 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9759 }
9760 }
9761
9762 // Create dynamic tags for __EXTRAFIELD_FIELD__
9763 if ($object->table_element && $object->id > 0) {
9764 if (!is_object($extrafields)) {
9765 $extrafields = new ExtraFields($db);
9766 }
9767 $extrafields->fetch_name_optionals_label($object->table_element, true);
9768
9769 if ($object->fetch_optionals() > 0) {
9770 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9771 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9772 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9773 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9774 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9775 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9776 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9777 $datetime = $object->array_options['options_'.$key];
9778 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9779 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9780 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9781 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9782 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9783 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9784 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9785 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9786 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9787 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9788 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9789 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9790 }
9791 }
9792 }
9793 }
9794 }
9795
9796 // Complete substitution array with the url to make online payment
9797 if (empty($substitutionarray['__REF__'])) {
9798 $paymenturl = '';
9799 } else {
9800 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9801 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9802 $outputlangs->loadLangs(array('paypal', 'other'));
9803
9804 $amounttouse = 0;
9805 $typeforonlinepayment = 'free';
9806 if (is_object($object) && $object->element == 'commande') {
9807 $typeforonlinepayment = 'order';
9808 }
9809 if (is_object($object) && $object->element == 'facture') {
9810 $typeforonlinepayment = 'invoice';
9811 }
9812 if (is_object($object) && $object->element == 'member') {
9813 $typeforonlinepayment = 'member';
9814 if (!empty($object->last_subscription_amount)) {
9815 $amounttouse = $object->last_subscription_amount;
9816 }
9817 }
9818 if (is_object($object) && $object->element == 'contrat') {
9819 $typeforonlinepayment = 'contract';
9820 }
9821 if (is_object($object) && $object->element == 'fichinter') {
9822 $typeforonlinepayment = 'ficheinter';
9823 }
9824
9825 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9826 $paymenturl = $url;
9827 }
9828
9829 if ($object->id > 0) {
9830 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9831 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9832
9833 // Show structured communication
9834 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9835 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9836 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9837 }
9838
9839 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9840 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9841 } else {
9842 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9843 }
9844 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9845 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9846 } else {
9847 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9848 }
9849 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9850 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9851 } else {
9852 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9853 }
9854 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9855 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9856 } else {
9857 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9858 }
9859 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9860 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9861 } else {
9862 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9863 }
9864 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9865 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9866 } else {
9867 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9868 }
9869
9870 if (is_object($object) && $object->element == 'propal') {
9871 '@phan-var-force Propal $object';
9873 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9874 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9875 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9876 }
9877 if (is_object($object) && $object->element == 'commande') {
9878 '@phan-var-force Commande $object';
9880 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9881 }
9882 if (is_object($object) && $object->element == 'facture') {
9883 '@phan-var-force Facture $object';
9885 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9886 }
9887 if (is_object($object) && $object->element == 'contrat') {
9888 '@phan-var-force Contrat $object';
9890 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9891 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9892 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9893 }
9894 if (is_object($object) && $object->element == 'fichinter') {
9895 '@phan-var-force Fichinter $object';
9897 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9898 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9899 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9900 }
9901 if (is_object($object) && $object->element == 'supplier_proposal') {
9902 '@phan-var-force SupplierProposal $object';
9904 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9905 }
9906 if (is_object($object) && $object->element == 'invoice_supplier') {
9907 '@phan-var-force FactureFournisseur $object';
9909 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9910 }
9911 if (is_object($object) && $object->element == 'shipping') {
9912 '@phan-var-force Expedition $object';
9914 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9915 }
9916 }
9917
9918 if (is_object($object) && $object->element == 'action') {
9919 '@phan-var-force ActionComm $object';
9921 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9922 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9923 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9924 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9925 }
9926 }
9927 }
9928 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9929 '@phan-var-force Facture|FactureRec $object';
9931 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9932
9933 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9934 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9935 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9936 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9937
9938 $already_payed_all = 0;
9939 if (is_object($object) && ($object instanceof Facture)) {
9940 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9941 }
9942
9943 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9944 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9945 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9946
9947 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9948 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9949 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9950
9951 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9952
9953 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9954 $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)) : '';
9955 $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)) : '';
9956
9957 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9958 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9959 }
9960 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9961 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9962 }
9963
9964 // Amount keys formatted in a currency
9965 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9966 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9967 $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) : '';
9968 $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)) : '';
9969 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9970 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9971 }
9972 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9973 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9974 }
9975 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9976 if ($onlykey != 2) {
9977 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9978 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9979 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9980 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9981 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9982 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9983 }
9984 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9985 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9986 }
9987 }
9988
9989 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9990 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9991 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9992 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9993 // TODO Add other keys for foreign multicurrency
9994
9995 // For backward compatibility
9996 if ($onlykey != 2) {
9997 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9998 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9999 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
10000 }
10001 }
10002
10003
10004 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
10005 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
10006
10007 $now = dol_now();
10008
10009 $tmp = dol_getdate($now, true);
10010 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10011 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
10012 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
10013 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
10014
10015 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
10016
10017 $substitutionarray = array_merge($substitutionarray, array(
10018 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
10019 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
10020 '__DAY__' => (string) $tmp['mday'],
10021 '__DAY_TEXT__' => $daytext, // Monday
10022 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
10023 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
10024 '__MONTH__' => (string) $tmp['mon'],
10025 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
10026 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
10027 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
10028 '__YEAR__' => (string) $tmp['year'],
10029 '__YEAR_PREVIOUS_MONTH__' => (string) $tmp3['year'],
10030 '__YEAR_NEXT_MONTH__' => (string) $tmp5['year'],
10031 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
10032 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
10033 '__PREVIOUS_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp3['month'])),
10034 '__PREVIOUS_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp3['month'])),
10035 '__PREVIOUS_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp3['month'])),
10036 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
10037 '__NEXT_DAY__' => (string) $tmp4['day'],
10038 '__NEXT_MONTH__' => (string) $tmp5['month'],
10039 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
10040 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
10041 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
10042 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
10043 ));
10044 }
10045
10046 if (isModEnabled('multicompany')) {
10047 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
10048 }
10049 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
10050 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
10051 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
10052 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
10053 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
10054 }
10055
10056 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
10057
10058 return $substitutionarray;
10059}
10060
10077function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
10078{
10079 global $conf, $db, $langs;
10080
10081 if (!is_array($substitutionarray)) {
10082 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
10083 }
10084
10085 if (empty($outputlangs)) {
10086 $outputlangs = $langs;
10087 }
10088
10089 // Is initial text HTML or simple text ?
10090 $msgishtml = 0;
10091 if (dol_textishtml($text, 1)) {
10092 $msgishtml = 1;
10093 }
10094
10095 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
10096 if (is_object($outputlangs)) {
10097 $reg = array();
10098 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
10099 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
10100 $tmp = explode('|', $reg[1]);
10101 if (!empty($tmp[1])) {
10102 $outputlangs->load($tmp[1]);
10103 }
10104
10105 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
10106
10107 if (empty($converttextinhtmlifnecessary)) {
10108 // convert $newval into HTML is necessary
10109 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10110 } else {
10111 if (! $msgishtml) {
10112 $valueishtml = dol_textishtml($value, 1);
10113 //var_dump("valueishtml=".$valueishtml);
10114
10115 if ($valueishtml) {
10116 $text = dol_htmlentitiesbr($text);
10117 $msgishtml = 1;
10118 }
10119 } else {
10120 $value = dol_nl2br((string) $value);
10121 }
10122
10123 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
10124 }
10125 }
10126 }
10127
10128 // Make substitution for constant keys.
10129 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
10130 $reg = array();
10131 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
10132 $keyfound = $reg[1];
10133 if (isASecretKey($keyfound)) {
10134 $value = '*****forbidden*****';
10135 } else {
10136 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
10137 }
10138
10139 if (empty($converttextinhtmlifnecessary)) {
10140 // convert $newval into HTML is necessary
10141 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
10142 } else {
10143 if (! $msgishtml) {
10144 $valueishtml = dol_textishtml($value, 1);
10145
10146 if ($valueishtml) {
10147 $text = dol_htmlentitiesbr($text);
10148 $msgishtml = 1;
10149 }
10150 } else {
10151 $value = dol_nl2br((string) $value);
10152 }
10153
10154 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
10155 }
10156 }
10157
10158 // Make substitution for array $substitutionarray
10159 foreach ($substitutionarray as $key => $value) {
10160 if (!isset($value)) {
10161 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
10162 }
10163
10164 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
10165 $value = ''; // Protection
10166 }
10167
10168 if (empty($converttextinhtmlifnecessary)) {
10169 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed when value is 123.5 for example
10170 } else {
10171 if (! $msgishtml) {
10172 $valueishtml = dol_textishtml($value, 1);
10173
10174 if ($valueishtml) {
10175 $text = dol_htmlentitiesbr($text);
10176 $msgishtml = 1;
10177 }
10178 } else {
10179 $value = dol_nl2br((string) $value);
10180 }
10181 $text = str_replace((string) $key, (string) $value, $text); // Cast to string is needed 123.5 for example
10182 }
10183 }
10184
10185 // TODO Implement the lazyload substitution
10186 /*
10187 add a loop to scan $substitutionarray:
10188 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.
10189 If no, we don't need to make replacement, so we do nothing.
10190 If yes, we can make the substitution:
10191
10192 include_once $path;
10193 $tmpobj = new $class($db);
10194 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
10195 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
10196 */
10197 $memory_object_list = array();
10198 foreach ($substitutionarray as $key => $value) {
10199 $lazy_load_arr = array();
10200 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
10201 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
10202 $key_to_substitute = $lazy_load_arr[1];
10203 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
10204 $param_arr = explode(':', (string) $value);
10205 // path:class:method:id
10206 if (count($param_arr) == 4) {
10207 $path = $param_arr[0];
10208 $class = $param_arr[1];
10209 $method = $param_arr[2];
10210 $id = (int) $param_arr[3];
10211
10212 // load class file and init object list in memory
10213 if (!isset($memory_object_list[$class])) {
10214 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
10215 require_once DOL_DOCUMENT_ROOT . $path;
10216 if (class_exists($class)) {
10217 $memory_object_list[$class] = array(
10218 'list' => array(),
10219 );
10220 }
10221 }
10222 }
10223
10224 // fetch object and set substitution
10225 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
10226 if (method_exists($class, $method)) {
10227 if (!isset($memory_object_list[$class]['list'][$id])) {
10228 $tmpobj = new $class($db);
10229 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10230 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
10231 $memory_object_list[$class]['list'][$id] = $tmpobj;
10232 } else {
10233 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10234 $tmpobj = $memory_object_list[$class]['list'][$id];
10235 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10236 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
10237 }
10238
10239 $text = str_replace((string) $key_to_substitute, (string) $valuetouseforsubstitution, $text); // Cast to string in case value is 123.5 for example
10240 }
10241 }
10242 }
10243 }
10244 }
10245 }
10246 }
10247
10248 return $text;
10249}
10250
10263function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
10264{
10265 global $conf, $user;
10266
10267 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
10268
10269 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
10270
10271 // Check if there is external substitution to do, requested by plugins
10272 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
10273
10274 foreach ($dirsubstitutions as $reldir) {
10275 $dir = dol_buildpath($reldir, 0);
10276
10277 // Check if directory exists
10278 if (!dol_is_dir($dir)) {
10279 continue;
10280 }
10281
10282 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
10283 foreach ($substitfiles as $substitfile) {
10284 $reg = array();
10285 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
10286 $module = $reg[1];
10287
10288 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
10289 // Include the user's functions file
10290 require_once $dir.$substitfile['name'];
10291 // Call the user's function, and only if it is defined
10292 $function_name = $module."_".$callfunc;
10293 if (function_exists($function_name)) {
10294 $function_name($substitutionarray, $outputlangs, $object, $parameters);
10295 }
10296 }
10297 }
10298 }
10299 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
10300 // to list all tags in odt template
10301 $tags = '';
10302 foreach ($substitutionarray as $key => $value) {
10303 $tags .= '{'.$key.'} => '.$value."\n";
10304 }
10305 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
10306 }
10307}
10308
10318function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
10319{
10320 print get_date_range($date_start, $date_end, $format, $outputlangs);
10321}
10322
10333function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
10334{
10335 global $langs;
10336
10337 $out = '';
10338
10339 if (!is_object($outputlangs)) {
10340 $outputlangs = $langs;
10341 }
10342
10343 if ($date_start && $date_end) {
10344 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10345 }
10346 if ($date_start && !$date_end) {
10347 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10348 }
10349 if (!$date_start && $date_end) {
10350 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10351 }
10352
10353 return $out;
10354}
10355
10364function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
10365{
10366 global $conf;
10367
10368 $ret = '';
10369 // If order not defined, we use the setup
10370 if ($nameorder < 0) {
10371 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
10372 }
10373 if ($nameorder == 1) {
10374 $ret .= $firstname;
10375 if ($firstname && $lastname) {
10376 $ret .= ' ';
10377 }
10378 $ret .= $lastname;
10379 } elseif ($nameorder == 2 || $nameorder == 3) {
10380 $ret .= $firstname;
10381 if (empty($ret) && $nameorder == 3) {
10382 $ret .= $lastname;
10383 }
10384 } else { // 0, 4 or 5
10385 $ret .= $lastname;
10386 if (empty($ret) && $nameorder == 5) {
10387 $ret .= $firstname;
10388 }
10389 if ($nameorder == 0) {
10390 if ($firstname && $lastname) {
10391 $ret .= ' ';
10392 }
10393 $ret .= $firstname;
10394 }
10395 }
10396 return $ret;
10397}
10398
10399
10412function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
10413{
10414 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
10415 if (!is_array($mesgs)) {
10416 $mesgs = trim((string) $mesgs);
10417 // If mesgs is a not an empty string
10418 if ($mesgs) {
10419 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
10420 return;
10421 }
10422 if ($attop) {
10423 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10424 } else {
10425 $_SESSION['dol_events'][$style][] = $mesgs;
10426 }
10427 }
10428 } else {
10429 // If mesgs is an array
10430 foreach ($mesgs as $mesg) {
10431 $mesg = trim((string) $mesg);
10432 if ($mesg) {
10433 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
10434 return;
10435 }
10436 if ($attop) {
10437 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10438 } else {
10439 $_SESSION['dol_events'][$style][] = $mesg;
10440 }
10441 }
10442 }
10443 }
10444}
10445
10459function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
10460{
10461 if (empty($mesg) && empty($mesgs)) {
10462 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
10463 } else {
10464 if ($messagekey) {
10465 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
10466 // TODO
10467 $mesg .= '';
10468 }
10469 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
10470 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
10471 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
10472 }
10473 if (empty($mesgs)) {
10474 setEventMessage((string) $mesg, $style, $noduplicate, $attop);
10475 } else {
10476 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10477 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10478 }
10479 setEventMessage($mesgs, $style, $noduplicate, $attop);
10480 }
10481 }
10482 }
10483}
10484
10494function dol_htmloutput_events($disabledoutputofmessages = 0)
10495{
10496 // Show mesgs
10497 if (isset($_SESSION['dol_events']['mesgs'])) {
10498 if (empty($disabledoutputofmessages)) {
10499 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10500 }
10501 unset($_SESSION['dol_events']['mesgs']);
10502 }
10503 // Show errors
10504 if (isset($_SESSION['dol_events']['errors'])) {
10505 if (empty($disabledoutputofmessages)) {
10506 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10507 }
10508 unset($_SESSION['dol_events']['errors']);
10509 }
10510
10511 // Show warnings
10512 if (isset($_SESSION['dol_events']['warnings'])) {
10513 if (empty($disabledoutputofmessages)) {
10514 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10515 }
10516 unset($_SESSION['dol_events']['warnings']);
10517 }
10518}
10519
10534function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10535{
10536 global $conf, $langs;
10537
10538 $ret = 0;
10539 $return = '';
10540 $out = '';
10541 $divstart = $divend = '';
10542
10543 // If inline message with no format, we add it.
10544 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10545 $divstart = '<div class="'.$style.' clearboth">';
10546 $divend = '</div>';
10547 }
10548
10549 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10550 $langs->load("errors");
10551 $out .= $divstart;
10552 if (is_array($mesgarray) && count($mesgarray)) {
10553 foreach ($mesgarray as $message) {
10554 $ret++;
10555 $out .= $langs->trans($message);
10556 if ($ret < count($mesgarray)) {
10557 $out .= "<br>\n";
10558 }
10559 }
10560 }
10561 if ($mesgstring) {
10562 $ret++;
10563 $out .= $langs->trans($mesgstring);
10564 }
10565 $out .= $divend;
10566 }
10567
10568 if ($out) {
10569 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10570 $return = '<script nonce="'.getNonce().'">
10571 $(document).ready(function() {
10572 /* jnotify(message, preset of message type, keepmessage) */
10573 $.jnotify("'.dol_escape_js($out).'", "'.($style == "ok" ? 3000 : $style).'", '.($style == "ok" ? "false" : "true").',{ remove: function (){} } );
10574 });
10575 </script>';
10576 } else {
10577 $return = $out;
10578 }
10579 }
10580
10581 return $return;
10582}
10583
10595function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10596{
10597 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10598}
10599
10613function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10614{
10615 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10616 return;
10617 }
10618
10619 $iserror = 0;
10620 $iswarning = 0;
10621 if (is_array($mesgarray)) {
10622 foreach ($mesgarray as $val) {
10623 if ($val && preg_match('/class="error"/i', $val)) {
10624 $iserror++;
10625 break;
10626 }
10627 if ($val && preg_match('/class="warning"/i', $val)) {
10628 $iswarning++;
10629 break;
10630 }
10631 }
10632 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10633 $iserror++;
10634 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10635 $iswarning++;
10636 }
10637 if ($style == 'error') {
10638 $iserror++;
10639 }
10640 if ($style == 'warning') {
10641 $iswarning++;
10642 }
10643
10644 if ($iserror || $iswarning) {
10645 // Remove div from texts
10646 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10647 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10648 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10649 // Remove div from texts array
10650 if (is_array($mesgarray)) {
10651 $newmesgarray = array();
10652 foreach ($mesgarray as $val) {
10653 if (is_string($val)) {
10654 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10655 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10656 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10657 $newmesgarray[] = $tmpmesgstring;
10658 } else {
10659 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10660 }
10661 }
10662 $mesgarray = $newmesgarray;
10663 }
10664 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10665 } else {
10666 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10667 }
10668}
10669
10681function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10682{
10683 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10684}
10685
10706function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10707{
10708 // Clean parameters
10709 $order = strtolower($order);
10710
10711 if (is_array($array)) {
10712 $sizearray = count($array);
10713 if ($sizearray > 0) {
10714 $temp = array();
10715 foreach (array_keys($array) as $key) {
10716 if (is_object($array[$key])) {
10717 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10718 } else {
10719 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10720 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10721 }
10722 if ($natsort == -1) {
10723 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10724 }
10725 }
10726
10727 if (empty($natsort) || $natsort == -1) {
10728 if ($order == 'asc') {
10729 asort($temp);
10730 } else {
10731 arsort($temp);
10732 }
10733 } else {
10734 if ($case_sensitive) {
10735 natsort($temp);
10736 } else {
10737 natcasesort($temp); // natecasesort is not sensible to case
10738 }
10739 if ($order != 'asc') {
10740 $temp = array_reverse($temp, true);
10741 }
10742 }
10743
10744 $sorted = array();
10745
10746 foreach (array_keys($temp) as $key) {
10747 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10748 }
10749
10750 return $sorted;
10751 }
10752 }
10753 return $array;
10754}
10755
10756
10764function utf8_check($str)
10765{
10766 $str = (string) $str; // Sometimes string is an int.
10767
10768 // We must use here a binary strlen function (so not dol_strlen)
10769 $strLength = strlen($str);
10770 for ($i = 0; $i < $strLength; $i++) {
10771 if (ord($str[$i]) < 0x80) {
10772 continue; // 0bbbbbbb
10773 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10774 $n = 1; // 110bbbbb
10775 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10776 $n = 2; // 1110bbbb
10777 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10778 $n = 3; // 11110bbb
10779 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10780 $n = 4; // 111110bb
10781 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10782 $n = 5; // 1111110b
10783 } else {
10784 return false; // Does not match any model
10785 }
10786 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10787 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10788 return false;
10789 }
10790 }
10791 }
10792 return true;
10793}
10794
10802function utf8_valid($str)
10803{
10804 /* 2 other methods to test if string is utf8
10805 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10806 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10807 */
10808 return preg_match('//u', $str) ? true : false;
10809}
10810
10811
10818function ascii_check($str)
10819{
10820 if (function_exists('mb_check_encoding')) {
10821 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10822 if (!mb_check_encoding($str, 'ASCII')) {
10823 return false;
10824 }
10825 } else {
10826 if (preg_match('/[^\x00-\x7f]/', $str)) {
10827 return false; // Contains a byte > 7f
10828 }
10829 }
10830
10831 return true;
10832}
10833
10834
10842function dol_osencode($str)
10843{
10844 $tmp = ini_get("unicode.filesystem_encoding");
10845 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10846 $tmp = 'iso-8859-1'; // By default for windows
10847 }
10848 if (empty($tmp)) {
10849 $tmp = 'utf-8'; // By default for other
10850 }
10851 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10852 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10853 }
10854
10855 if ($tmp == 'iso-8859-1') {
10856 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10857 }
10858 return $str;
10859}
10860
10861
10877function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10878{
10879 global $conf;
10880
10881 // If key empty
10882 if ($key == '') {
10883 return 0;
10884 }
10885
10886 // Check in cache
10887 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10888 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10889 }
10890
10891 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10892
10893 $sql = "SELECT ".$fieldid." as valuetoget";
10894 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10895 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10896 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10897 } else {
10898 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10899 }
10900 if (!empty($entityfilter)) {
10901 $sql .= " AND entity IN (".getEntity($tablename).")";
10902 }
10903 if ($filters) {
10904 $sql .= $filters;
10905 }
10906
10907 $resql = $db->query($sql);
10908 if ($resql) {
10909 $obj = $db->fetch_object($resql);
10910 $valuetoget = '';
10911 if ($obj) {
10912 $valuetoget = $obj->valuetoget;
10913 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10914 } else {
10915 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10916 }
10917 $db->free($resql);
10918
10919 return $valuetoget;
10920 } else {
10921 return -1;
10922 }
10923}
10924
10934function isStringVarMatching($var, $regextext, $matchrule = 1)
10935{
10936 if ($matchrule == 1) {
10937 if ($var == 'mainmenu') {
10938 global $mainmenu;
10939 return (preg_match('/^'.$regextext.'/', $mainmenu));
10940 } elseif ($var == 'leftmenu') {
10941 global $leftmenu;
10942 return (preg_match('/^'.$regextext.'/', $leftmenu));
10943 } else {
10944 return 'This variable is not accessible with dol_eval';
10945 }
10946 } else {
10947 return 'This value for matchrule is not implemented';
10948 }
10949}
10950
10951
10961function verifCond($strToEvaluate, $onlysimplestring = '1')
10962{
10963 //print $strToEvaluate."<br>\n";
10964 $rights = true;
10965 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10966 //var_dump($strToEvaluate);
10967 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10968 $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
10969 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10970 //var_dump($rights);
10971 }
10972 return $rights;
10973}
10974
10989function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10990{
10991 if ($returnvalue != 1) {
10992 dol_syslog("Use of dol_eval with parameter returnvalue = 0 is now forbidden. Please fix this", LOG_ERR);
10993 }
10994
10995 if (getDolGlobalString("MAIN_USE_DOL_EVAL_NEW")) {
10996 return dol_eval_new($s);
10997 } else {
10998 return dol_eval_standard($s, $returnvalue, $hideerrors, $onlysimplestring);
10999 }
11000}
11001
11011function dol_eval_new($s)
11012{
11013 // Only this global variables can be read by eval function and returned to caller
11014 global $conf, // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11015 $db, $langs, $user, $website, $websitepage,
11016 $action, $mainmenu, $leftmenu,
11017 $mysoc,
11018 $objectoffield, // To allow the use of $objectoffield in computed fields
11019
11020 // Old variables used
11021 $object,
11022 $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11023
11024 // PHP < 7.4.0
11025 defined('T_COALESCE_EQUAL') || define('T_COALESCE_EQUAL', PHP_INT_MAX);
11026 defined('T_FN') || define('T_FN', PHP_INT_MAX);
11027
11028 // PHP < 8.0.0
11029 defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', PHP_INT_MAX);
11030 defined('T_MATCH') || define('T_MATCH', PHP_INT_MAX);
11031 defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', PHP_INT_MAX);
11032 defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', PHP_INT_MAX);
11033 defined('T_NAME_RELATIVE') || define('T_NAME_RELATIVE', PHP_INT_MAX);
11034
11035 // PHP < 8.1.0
11036 defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11037 defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') || define('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', PHP_INT_MAX);
11038 defined('T_ENUM') || define('T_ENUM', PHP_INT_MAX);
11039 defined('T_READONLY') || define('T_READONLY', PHP_INT_MAX);
11040
11041 // PHP < 8.4.0
11042 defined('T_PRIVATE_SET') || define('T_PRIVATE_SET', PHP_INT_MAX);
11043 defined('T_PROTECTED_SET') || define('T_PROTECTED_SET', PHP_INT_MAX);
11044 defined('T_PUBLIC_SET') || define('T_PUBLIC_SET', PHP_INT_MAX);
11045
11046 $prohibited_token_ids = [
11047 /*
11048 * Prohibited int tokens
11049 */
11050
11051 // T_AND_EQUAL', 'T_ARRAY', 'T_ARRAY_CAST', 'T_AS',
11052 'T_ABSTRACT', 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG', 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG', 'T_ATTRIBUTE',
11053 // 'T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_BOOL_CAST', 'T_BREAK',
11054 'T_BAD_CHARACTER',
11055 // 'T_CASE', 'T_CLASS_C', 'T_CLONE', 'T_COALESCE', 'T_COALESCE_EQUAL', 'T_COMMENT', 'T_CONCAT_EQUAL',
11056 // 'T_CONSTANT_ENCAPSED_STRING', 'T_CONTINUE', 'T_CURLY_OPEN',
11057 'T_CALLABLE', 'T_CATCH', 'T_CLASS', 'T_CLOSE_TAG', 'T_CONST',
11058 // 'T_DEC', 'T_DEFAULT', 'T_DIV_EQUAL', 'T_DNUMBER', 'T_DO', 'T_DOC_COMMENT',
11059 // 'T_DOLLAR_OPEN_CURLY_BRACES', 'T_DOUBLE_ARROW', 'T_DOUBLE_CAST', 'T_DOUBLE_COLON',
11060 'T_DECLARE', 'T_DIR',
11061 // 'T_ELLIPSIS', 'T_ELSE', 'T_ELSEIF', 'T_EMPTY', 'T_ENCAPSED_AND_WHITESPACE', 'T_ENDFOR',
11062 // 'T_ENDFOREACH', 'T_ENDIF', 'T_ENDSWITCH', 'T_ENDWHILE', 'T_END_HEREDOC',
11063 'T_ECHO', 'T_ENDDECLARE', 'T_ENUM', 'T_EVAL', 'T_EXIT', 'T_EXTENDS',
11064 // 'T_FOR', 'T_FOREACH',
11065 'T_FILE', 'T_FINAL', 'T_FINALLY', 'T_FN', 'T_FUNCTION', 'T_FUNC_C',
11066 'T_GLOBAL', 'T_GOTO',
11067 'T_HALT_COMPILER',
11068 // 'T_IF', 'T_INC', 'T_INLINE_HTML', 'T_INSTANCEOF', 'T_INT_CAST', 'T_ISSET', 'T_IS_EQUAL', 'T_IS_GREATER_OR_EQUAL',
11069 // 'T_IS_IDENTICAL', 'T_IS_NOT_EQUAL', 'T_IS_NOT_IDENTICAL', 'T_IS_SMALLER_OR_EQUAL',
11070 'T_IMPLEMENTS', 'T_INCLUDE', 'T_INCLUDE_ONCE', 'T_INSTEADOF', 'T_INTERFACE',
11071 // 'T_LIST', 'T_LNUMBER', 'T_LOGICAL_AND', 'T_LOGICAL_OR', 'T_LOGICAL_XOR',
11072 'T_LINE',
11073 // 'T_MINUS_EQUAL', 'T_MOD_EQUAL', 'T_MUL_EQUAL',
11074 'T_METHOD_C',
11075 // 'T_NEW',
11076 // 'T_NS_SEPARATOR', 'T_NUM_STRING',
11077 'T_NAMESPACE',
11078 // 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED', 'T_NAME_RELATIVE', 'T_NS_C',
11079 // 'T_OBJECT_CAST', 'T_OBJECT_OPERATOR', 'T_OR_EQUAL',
11080 'T_OPEN_TAG', 'T_OPEN_TAG_WITH_ECHO',
11081 // 'T_PAAMAYIM_NEKUDOTAYIM', 'T_PLUS_EQUAL', 'T_POW', 'T_POW_EQUAL',
11082 'T_PRINT', 'T_PRIVATE', 'T_PROTECTED', 'T_PUBLIC',
11083 // 'T_PROPERTY_C',
11084 'T_READONLY', 'T_REQUIRE', 'T_REQUIRE_ONCE', 'T_RETURN',
11085 // 'T_SL', 'T_SL_EQUAL', 'T_SPACESHIP', 'T_SR', 'T_SR_EQUAL', 'T_START_HEREDOC', 'T_STATIC',
11086 // 'T_STRING', 'T_STRING_CAST', 'T_STRING_VARNAME', 'T_SWITCH',
11087 'T_STATIC',
11088 'T_THROW', 'T_TRAIT', 'T_TRAIT_C', 'T_TRY',
11089 'T_UNSET', 'T_UNSET_CAST', 'T_USE',
11090 // 'T_VARIABLE',
11091 'T_VAR',
11092 // 'T_WHILE', 'T_WHITESPACE',
11093 // 'T_XOR_EQUAL',
11094 // 'T_YIELD', 'T_YIELD_FROM',
11095
11096 /*
11097 * Prohibited string tokens
11098 */
11099 ';', '`',
11100 ];
11101
11102 $prohibited_variables = [
11103 '$_COOKIE', '$_ENV', '$_FILES', '$GLOBALS', '$_GET', '$_POST', '$_REQUEST', '$_SERVER', '$_SESSION',
11104 ];
11105
11106 $prohibited_functions = [
11107 // 'base64_decode', 'rawurldecode', 'urldecode', 'str_rot13', 'hex2bin', // I haven't managed to inject anything with these functions yet, can someone confirm?
11108 // 'get_defined_functions', 'get_defined_vars', 'get_defined_constants', 'get_declared_classes', // Should we really block the admin from viewing these lists?
11109 'override_function', 'session_id', 'session_create_id', 'session_regenerate_id',
11110 'call_user_func', 'call_user_func_array', // PREVENT calling forbidden functions
11111 'exec', 'passthru', 'shell_exec', 'system', 'proc_open', 'popen',
11112 'dol_eval', 'dol_eval_new', 'dol_eval_standard', 'dol_contctdesc', 'executeCLI', 'verifCond', 'GETPOST', // Native Dolibarr functions
11113 'create_function', 'assert', 'mb_ereg_replace', 'mb_eregi_replace', // function with eval capabilities
11114 'dol_compress_dir', 'dol_decode', 'dol_delete_file', 'dol_delete_dir', 'dol_delete_dir_recursive', 'dol_copy', 'archiveOrBackupFile', // more dolibarr functions
11115 'fopen', 'file_put_contents', 'fputs', 'fputscsv', 'fwrite', 'fpassthru', 'mkdir', 'rmdir', 'symlink', 'touch', 'unlink', 'umask', // PHP functions related to file operations
11116 'invoke', 'invokeArgs', // Method of ReflectionFunction to execute a function
11117 'filter_input', 'filter_input_array', 'GETPOST', // PREVENT CODE INJECTION
11118 ];
11119
11120 $prohibited_token_arrangements = [
11121 // Variable functions « $a( », « "$a"( », « 'FN_NAME'( », ('FN_NAME')()
11122 ' T_VARIABLE ( ', ' " ( ', ' \' ( ', ' T_CONSTANT_ENCAPSED_STRING ( ', ' ) ( ',
11123 ];
11124
11125 $tokens = token_get_all("<?php return {$s};", TOKEN_PARSE);
11126
11127 $tokens_arrangement = ' ';
11128
11129 for ($i = 2, $c = count($tokens) - 1; $i < $c; ++$i) { // ignore <?php return and ;
11130 if (is_array($tokens[$i])) {
11131 $token_id = $tokens[$i][0];
11132 $token_value = $tokens[$i][1];
11133 $token_name = token_name($tokens[$i][0]);
11134 } else {
11135 $token_id = $tokens[$i];
11136 $token_value = $tokens[$i];
11137 $token_name = $tokens[$i];
11138 }
11139
11140 // Ignore whitespaces
11141 if (T_WHITESPACE === $token_id) {
11142 continue;
11143 }
11144
11145 // Keep history to check arrangements
11146 $tokens_arrangement .= "{$token_name} ";
11147
11148 // Prohibited Variables
11149 if (T_VARIABLE === $token_id
11150 && in_array($token_value, $prohibited_variables, true)
11151 ) {
11152 return "« {$token_value} » is prohibited in « {$s} »";
11153 }
11154
11155 // Prohibited Functions
11156 if (T_STRING === $token_id
11157 && in_array($token_value, $prohibited_functions, true)
11158 ) {
11159 return "« {$token_value} » is prohibited in « {$s} »";
11160 }
11161 }
11162
11163 // Prohibited Token IDs
11164 $maxi = count($prohibited_token_ids);
11165 for ($i = 0; $i < $maxi; ++$i) {
11166 if (false !== strpos($tokens_arrangement, " {$prohibited_token_ids[$i]} ")) {
11167 return "« {$prohibited_token_ids[$i]} » is prohibited in « {$s} »";
11168 }
11169 }
11170
11171 // Prohibited token arrangements
11172 $maxi = count($prohibited_token_arrangements);
11173 for ($i = 0; $i < $maxi; ++$i) {
11174 if (false !== strpos($tokens_arrangement, $prohibited_token_arrangements[$i])) {
11175 return "« {$prohibited_token_arrangements[$i]} » is prohibited in « {$s} »";
11176 }
11177 }
11178
11179 // Return result
11180 try {
11181 return @eval("return {$s};") ?? '';
11182 } catch (Throwable $ex) {
11183 return "Exception during evaluation: ".$s." - ".$ex->getMessage();
11184 }
11185}
11186
11201function dol_eval_standard($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
11202{
11203 // Only this global variables can be read by eval function and returned to caller
11204 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
11205 global $db, $langs, $user, $website, $websitepage;
11206 global $action, $mainmenu, $leftmenu;
11207 global $mysoc;
11208 global $objectoffield; // To allow the use of $objectoffield in computed fields
11209
11210 // Old variables used (deprecated)
11211 global $object;
11212 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
11213
11214 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
11215 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
11216 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
11217 }
11218 if (!is_scalar($s)) {
11219 return "Bad call of dol_eval. First parameter must be a string, found ".var_export($s, true);
11220 }
11221
11222 try {
11223 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
11224 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
11225 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
11226 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
11227 // 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"
11228
11229 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
11230 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
11231 if ($onlysimplestring == '2') {
11232 $specialcharsallowed .= '<[]';
11233 }
11234 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
11235 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
11236 }
11237 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
11238 if ($returnvalue) {
11239 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s;
11240 } else {
11241 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING);
11242 return '';
11243 }
11244 }
11245
11246 // Check if we found a ? without a space before and after
11247 $tmps = str_replace(' ? ', '__XXX__', $s);
11248 if (strpos($tmps, '?') !== false) {
11249 if ($returnvalue) {
11250 return 'Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s;
11251 } else {
11252 dol_syslog('Bad string syntax to evaluate (The char ? can be used only with a space before and after): '.$s, LOG_WARNING);
11253 return '';
11254 }
11255 }
11256
11257 // Check if there is a < or <= without spaces before/after
11258 if (preg_match('/<=?[^\s]/', $s)) {
11259 if ($returnvalue) {
11260 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s;
11261 } else {
11262 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING);
11263 return '';
11264 }
11265 }
11266
11267 // Check if there is dynamic call (first we use black list patterns)
11268 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
11269 if ($returnvalue) {
11270 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;
11271 } else {
11272 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);
11273 return '';
11274 }
11275 }
11276
11277 // Now we check if we try dynamic call
11278 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
11279 $savescheck = '';
11280 $scheck = $s;
11281 while ($scheck && $savescheck != $scheck) {
11282 $savescheck = $scheck;
11283 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
11284 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
11285 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
11286 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '&& ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11287 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '|| ('. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
11288 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
11289 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
11290 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
11291 $scheck = preg_replace('/\s!\‍(/', ' __NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
11292 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
11293 }
11294 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
11295
11296 // Now test if it remains 1 open parenthesis.
11297 if (strpos($scheck, '(') !== false) {
11298 if ($returnvalue) {
11299 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
11300 } else {
11301 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);
11302 return '';
11303 }
11304 }
11305
11306 // TODO
11307 // We can exclude $ char that are not in dol_eval global, so that are not:
11308 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object, $obj, ...,
11309 }
11310 if ($s === 'Array') {
11311 if ($returnvalue) {
11312 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
11313 } else {
11314 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
11315 return '';
11316 }
11317 }
11318
11319 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
11320 if ($returnvalue) {
11321 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
11322 } else {
11323 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
11324 return '';
11325 }
11326 }
11327
11328 if (strpos($s, '`') !== false) {
11329 if ($returnvalue) {
11330 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
11331 } else {
11332 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
11333 return '';
11334 }
11335 }
11336
11337 // Disallow also concat
11338 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
11339 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
11340 if ($returnvalue) {
11341 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
11342 } else {
11343 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
11344 return '';
11345 }
11346 }
11347 }
11348
11349 // We block use of php exec or php file functions
11350 $forbiddenphpstrings = array('$$', '$_', '}[', ')(');
11351 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_GLOBAL', '_POST', '_REQUEST', 'ReflectionFunction'));
11352
11353 // 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)
11354 // 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
11355 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
11356 $forbiddenphpfunctions = array();
11357 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
11358 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
11359 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
11360
11361 $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"));
11362 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
11363 $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"));
11364 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
11365 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
11366
11367 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
11368
11369 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
11370 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
11371 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "dol_concatdesc", "executeCLI", "verifCond", "GETPOST", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
11372 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
11373 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
11374 $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
11375 $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"));
11376 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
11377 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) { // We disabllow all function that allow to obfuscate the real name of a function
11378 // @phpcs:ignore
11379 $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
11380 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_concatdesc")); // native dolibarr functions
11381 }
11382
11383 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
11384
11385 $forbiddenphpregex = 'global\s*\$';
11386 $forbiddenphpregex .= '|';
11387 $forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
11388
11389 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
11390
11391 do {
11392 $oldstringtoclean = $s;
11393 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
11394 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
11395 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
11396 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
11397 } while ($oldstringtoclean != $s);
11398
11399
11400 if (strpos($s, '__forbiddenstring__') !== false) {
11401 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
11402 if ($returnvalue) {
11403 return 'Bad string syntax to evaluate: '.$s;
11404 } else {
11405 dol_syslog('Bad string syntax to evaluate: '.$s);
11406 return '';
11407 }
11408 }
11409
11410 //print $s."<br>\n";
11411 if ($returnvalue) {
11412 ob_start(); // An evaluation has no reason to output data
11413 $isObBufferActive = true;
11414 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
11415 $tmpo = ob_get_clean();
11416 $isObBufferActive = false;
11417 if ($tmpo) {
11418 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
11419 }
11420 return $tmps;
11421 } else {
11422 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
11423 if ($hideerrors) {
11424 @eval($s);
11425 } else {
11426 eval($s);
11427 }
11428 return '';
11429 }
11430 } catch (Error $e) {
11431 if ($isObBufferActive) {
11432 // Clean up buffer which was left behind due to exception.
11433 $tmpo = ob_get_clean();
11434 $isObBufferActive = false;
11435 }
11436 $error = 'dol_eval try/catch error : ';
11437 $error .= $e->getMessage();
11438 dol_syslog($error, LOG_WARNING);
11439 if ($returnvalue) {
11440 return 'Exception during evaluation: '.$s;
11441 } else {
11442 return '';
11443 }
11444 }
11445}
11446
11454function dol_validElement($element)
11455{
11456 return (trim($element) != '');
11457}
11458
11467function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
11468{
11469 if (empty($codelang)) {
11470 return '';
11471 }
11472
11473 if ($codelang == 'auto') {
11474 return '<span class="fa fa-language"></span>';
11475 }
11476
11477 $langtocountryflag = array(
11478 'ar_AR' => '',
11479 'ca_ES' => 'catalonia',
11480 'da_DA' => 'dk',
11481 'fr_CA' => 'mq',
11482 'sv_SV' => 'se',
11483 'sw_SW' => 'unknown',
11484 'AQ' => 'unknown',
11485 'CW' => 'unknown',
11486 'IM' => 'unknown',
11487 'JE' => 'unknown',
11488 'MF' => 'unknown',
11489 'BL' => 'unknown',
11490 'SX' => 'unknown'
11491 );
11492
11493 if (isset($langtocountryflag[$codelang])) {
11494 $flagImage = $langtocountryflag[$codelang];
11495 } else {
11496 $tmparray = explode('_', $codelang);
11497 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
11498 }
11499
11500 $morecss = '';
11501 $reg = array();
11502 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
11503 $morecss = $reg[1];
11504 $moreatt = "";
11505 }
11506
11507 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
11508 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
11509}
11510
11518function getLanguageCodeFromCountryCode($countrycode)
11519{
11520 global $mysoc;
11521
11522 if (empty($countrycode)) {
11523 return null;
11524 }
11525
11526 if (strtoupper($countrycode) == 'MQ') {
11527 return 'fr_CA';
11528 }
11529 if (strtoupper($countrycode) == 'SE') {
11530 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
11531 }
11532 if (strtoupper($countrycode) == 'CH') {
11533 if ($mysoc->country_code == 'FR') {
11534 return 'fr_CH';
11535 }
11536 if ($mysoc->country_code == 'DE') {
11537 return 'de_CH';
11538 }
11539 if ($mysoc->country_code == 'IT') {
11540 return 'it_CH';
11541 }
11542 }
11543
11544 // Locale list taken from:
11545 // http://stackoverflow.com/questions/3191664/
11546 // list-of-all-locales-and-their-short-codes
11547 $locales = array(
11548 'af-ZA',
11549 'am-ET',
11550 'ar-AE',
11551 'ar-BH',
11552 'ar-DZ',
11553 'ar-EG',
11554 'ar-IQ',
11555 'ar-JO',
11556 'ar-KW',
11557 'ar-LB',
11558 'ar-LY',
11559 'ar-MA',
11560 'ar-OM',
11561 'ar-QA',
11562 'ar-SA',
11563 'ar-SY',
11564 'ar-TN',
11565 'ar-YE',
11566 //'as-IN', // Moved after en-IN
11567 'ba-RU',
11568 'be-BY',
11569 'bg-BG',
11570 'bn-BD',
11571 //'bn-IN', // Moved after en-IN
11572 'bo-CN',
11573 'br-FR',
11574 'ca-ES',
11575 'co-FR',
11576 'cs-CZ',
11577 'cy-GB',
11578 'da-DK',
11579 'de-AT',
11580 'de-CH',
11581 'de-DE',
11582 'de-LI',
11583 'de-LU',
11584 'dv-MV',
11585 'el-GR',
11586 'en-AU',
11587 'en-BZ',
11588 'en-CA',
11589 'en-GB',
11590 'en-IE',
11591 'en-IN',
11592 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
11593 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
11594 'en-JM',
11595 'en-MY',
11596 'en-NZ',
11597 'en-PH',
11598 'en-SG',
11599 'en-TT',
11600 'en-US',
11601 'en-ZA',
11602 'en-ZW',
11603 'es-AR',
11604 'es-BO',
11605 'es-CL',
11606 'es-CO',
11607 'es-CR',
11608 'es-DO',
11609 'es-EC',
11610 'es-ES',
11611 'es-GT',
11612 'es-HN',
11613 'es-MX',
11614 'es-NI',
11615 'es-PA',
11616 'es-PE',
11617 'es-PR',
11618 'es-PY',
11619 'es-SV',
11620 'es-US',
11621 'es-UY',
11622 'es-VE',
11623 'et-EE',
11624 'eu-ES',
11625 'fa-IR',
11626 'fi-FI',
11627 'fo-FO',
11628 'fr-BE',
11629 'fr-CA',
11630 'fr-CH',
11631 'fr-FR',
11632 'fr-LU',
11633 'fr-MC',
11634 'fy-NL',
11635 'ga-IE',
11636 'gd-GB',
11637 'gl-ES',
11638 'gu-IN',
11639 'he-IL',
11640 'hi-IN',
11641 'hr-BA',
11642 'hr-HR',
11643 'hu-HU',
11644 'hy-AM',
11645 'id-ID',
11646 'ig-NG',
11647 'ii-CN',
11648 'is-IS',
11649 'it-CH',
11650 'it-IT',
11651 'ja-JP',
11652 'ka-GE',
11653 'kk-KZ',
11654 'kl-GL',
11655 'km-KH',
11656 'kn-IN',
11657 'ko-KR',
11658 'ky-KG',
11659 'lb-LU',
11660 'lo-LA',
11661 'lt-LT',
11662 'lv-LV',
11663 'mi-NZ',
11664 'mk-MK',
11665 'ml-IN',
11666 'mn-MN',
11667 'mr-IN',
11668 'ms-BN',
11669 'ms-MY',
11670 'mt-MT',
11671 'nb-NO',
11672 'ne-NP',
11673 'nl-BE',
11674 'nl-NL',
11675 'nn-NO',
11676 'oc-FR',
11677 'or-IN',
11678 'pa-IN',
11679 'pl-PL',
11680 'ps-AF',
11681 'pt-BR',
11682 'pt-PT',
11683 'rm-CH',
11684 'ro-MD',
11685 'ro-RO',
11686 'ru-RU',
11687 'rw-RW',
11688 'sa-IN',
11689 'se-FI',
11690 'se-NO',
11691 'se-SE',
11692 'si-LK',
11693 'sk-SK',
11694 'sl-SI',
11695 'sq-AL',
11696 'sv-FI',
11697 'sv-SE',
11698 'sw-KE',
11699 'ta-IN',
11700 'te-IN',
11701 'th-TH',
11702 'tk-TM',
11703 'tn-ZA',
11704 'tr-TR',
11705 'tt-RU',
11706 'ug-CN',
11707 'uk-UA',
11708 'ur-PK',
11709 'vi-VN',
11710 'wo-SN',
11711 'xh-ZA',
11712 'yo-NG',
11713 'zh-CN',
11714 'zh-HK',
11715 'zh-MO',
11716 'zh-SG',
11717 'zh-TW',
11718 'zu-ZA',
11719 );
11720
11721 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
11722 if (in_array($buildprimarykeytotest, $locales)) {
11723 return strtolower($countrycode).'_'.strtoupper($countrycode);
11724 }
11725
11726 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
11727 foreach ($locales as $locale) {
11728 $locale_language = locale_get_primary_language($locale);
11729 $locale_region = locale_get_region($locale);
11730 if (strtoupper($countrycode) == $locale_region) {
11731 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
11732 return strtolower($locale_language).'_'.strtoupper($locale_region);
11733 }
11734 }
11735 } else {
11736 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
11737 }
11738
11739 return null;
11740}
11741
11772function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11773{
11774 global $hookmanager, $db;
11775
11776 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11777 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11778 $values = explode(':', $value);
11779
11780 $reg = array();
11781 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11782 $newtab = array();
11783 $postab = $h;
11784 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11785 $str = $values[1];
11786 $posstart = strpos($str, '(');
11787 if ($posstart > 0) {
11788 $posend = strpos($str, ')');
11789 if ($posstart > 0) {
11790 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11791 if (is_numeric($res1)) {
11792 $postab = (int) $res1;
11793 $values[1] = '+' . substr($str, $posend + 1);
11794 }
11795 }
11796 }
11797 if (count($values) == 6) {
11798 // new declaration with permissions:
11799 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11800 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11801 if ($values[0] != $type) {
11802 continue;
11803 }
11804
11805 if (verifCond($values[4], '2')) {
11806 if ($values[3]) {
11807 if ($filterorigmodule) { // If a filter of module origin has been requested
11808 if (strpos($values[3], '@')) { // This is an external module
11809 if ($filterorigmodule != 'external') {
11810 continue;
11811 }
11812 } else { // This looks a core module
11813 if ($filterorigmodule != 'core') {
11814 continue;
11815 }
11816 }
11817 }
11818 $langs->load($values[3]);
11819 }
11820 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11821 // If label is "SUBSTITUION_..."
11822 $substitutionarray = array();
11823 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11824 $label = make_substitutions($reg[1], $substitutionarray);
11825 } else {
11826 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11827 $labeltemp = explode(',', $values[2]);
11828 $label = $langs->trans($labeltemp[0]);
11829
11830 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11831 dol_include_once($labeltemp[2]);
11832 $classtoload = $labeltemp[1];
11833 if (class_exists($classtoload)) {
11834 $obj = new $classtoload($db);
11835 $function = $labeltemp[3];
11836 if ($obj && $function && method_exists($obj, $function)) {
11837 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11838 $nbrec = $obj->$function($object->id, $obj);
11839 if (!empty($nbrec)) {
11840 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11841 }
11842 }
11843 }
11844 }
11845 }
11846
11847 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11848 $newtab[1] = $label;
11849 $newtab[2] = str_replace('+', '', $values[1]);
11850 $h++;
11851 } else {
11852 continue;
11853 }
11854 } elseif (count($values) == 5) { // case deprecated
11855 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11856
11857 if ($values[0] != $type) {
11858 continue;
11859 }
11860 if ($values[3]) {
11861 if ($filterorigmodule) { // If a filter of module origin has been requested
11862 if (strpos($values[3], '@')) { // This is an external module
11863 if ($filterorigmodule != 'external') {
11864 continue;
11865 }
11866 } else { // This looks a core module
11867 if ($filterorigmodule != 'core') {
11868 continue;
11869 }
11870 }
11871 }
11872 $langs->load($values[3]);
11873 }
11874 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11875 $substitutionarray = array();
11876 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11877 $label = make_substitutions($reg[1], $substitutionarray);
11878 } else {
11879 $label = $langs->trans($values[2]);
11880 }
11881
11882 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11883 $newtab[1] = $label;
11884 $newtab[2] = str_replace('+', '', $values[1]);
11885 $h++;
11886 }
11887 // set tab at its position
11888 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11889 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11890 if ($values[0] != $type) {
11891 continue;
11892 }
11893 $tabname = str_replace('-', '', $values[1]);
11894 foreach ($head as $key => $val) {
11895 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11896 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11897 if ($head[$key][2] == $tabname && $condition) {
11898 unset($head[$key]);
11899 break;
11900 }
11901 }
11902 }
11903 }
11904 }
11905
11906 // No need to make a return $head. Var is modified as a reference
11907 if (!empty($hookmanager)) {
11908 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11909 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
11910 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11911 if ($reshook > 0) { // Hook ask to replace completely the array
11912 $head = $hookmanager->resArray;
11913 } else { // Hook
11914 $head = array_merge($head, $hookmanager->resArray);
11915 }
11916 $h = count($head);
11917 }
11918}
11919
11931function printCommonFooter($zone = 'private')
11932{
11933 global $conf, $hookmanager, $user, $langs;
11934 global $debugbar;
11935 global $action;
11936 global $micro_start_time;
11937
11938 if ($zone == 'private') {
11939 print "\n".'<!-- Common footer for private page -->'."\n";
11940 } else {
11941 print "\n".'<!-- Common footer for public page -->'."\n";
11942 }
11943
11944 // A div to store page_y POST parameter so we can read it using javascript
11945 print "\n<!-- A div to store page_y POST parameter -->\n";
11946 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11947
11948 $parameters = array();
11949 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11950 if (empty($reshook)) {
11951 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11952 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11953 }
11954
11955 print "\n";
11956 if (!empty($conf->use_javascript_ajax)) {
11957 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11958 print '<script>'."\n";
11959 print 'jQuery(document).ready(function() {'."\n";
11960
11961 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11962 print "\n";
11963 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11964 print 'jQuery("li.menuhider").click(function(event) {';
11965 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11966 print ' console.log("We click on .menuhider");'."\n";
11967 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11968 print '});'."\n";
11969 }
11970
11971 // Management of focus and mandatory for fields
11972 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"])))) {
11973 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11974 $relativepathstring = $_SERVER["PHP_SELF"];
11975 // Clean $relativepathstring
11976 if (constant('DOL_URL_ROOT')) {
11977 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11978 }
11979 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11980 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11981 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11982
11983 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11984 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11985 $qualified = 0;
11986 if ($defkey != '_noquery_') {
11987 $tmpqueryarraytohave = explode('&', $defkey);
11988 $foundintru = 0;
11989 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11990 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11991 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11992 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11993 $foundintru = 1;
11994 }
11995 }
11996 if (!$foundintru) {
11997 $qualified = 1;
11998 }
11999 //var_dump($defkey.'-'.$qualified);
12000 } else {
12001 $qualified = 1;
12002 }
12003
12004 if ($qualified) {
12005 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
12006 foreach ($defval as $paramkey => $paramval) {
12007 // Set focus on field
12008 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
12009 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
12010 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
12011 }
12012 }
12013 }
12014 }
12015 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
12016 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
12017 $qualified = 0;
12018 if ($defkey != '_noquery_') {
12019 $tmpqueryarraytohave = explode('&', $defkey);
12020 $foundintru = 0;
12021 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
12022 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
12023 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
12024 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
12025 $foundintru = 1;
12026 }
12027 }
12028 if (!$foundintru) {
12029 $qualified = 1;
12030 }
12031 //var_dump($defkey.'-'.$qualified);
12032 } else {
12033 $qualified = 1;
12034 }
12035
12036 if ($qualified) {
12037 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
12038
12039 foreach ($defval as $paramkey => $paramval) {
12040 // Solution 1: Add handler on submit to check if mandatory fields are empty
12041 print 'var form = $(\'[name="'.dol_escape_js($paramkey).'"]\').closest("form");'."\n";
12042 print "form.on('submit', function(event) {
12043 var submitter = \$(this).find(':submit:focus').get(0);
12044 var buttonName = submitter ? \$(submitter).attr('name') : 'save';
12045
12046 if (buttonName == 'cancel') {
12047 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
12048 return true;
12049 }
12050
12051 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');
12052
12053 var tmpvalue = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').val();
12054 let tmptypefield = jQuery('[name=\"".dol_escape_js($paramkey)."\"]').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
12055
12056 if (tmptypefield == 'textarea') {
12057 // We must instead check the content of ckeditor
12058 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
12059 if (tmpeditor) {
12060 tmpvalue = tmpeditor.getData();
12061 console.log('For textarea tmpvalue is '+tmpvalue);
12062 }
12063 }
12064
12065 let tmpvalueisempty = false;
12066 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
12067 tmpvalueisempty = true;
12068 }
12069 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
12070 tmpvalueisempty = true;
12071 }
12072 if (tmpvalueisempty && buttonName !== 'cancel') {
12073 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
12074 event.preventDefault(); // Stop submission of form to allow custom code to decide.
12075 event.stopPropagation(); // Stop other handlers.
12076
12077 alert('".dol_escape_js($langs->transnoentitiesnoconv("ErrorFieldRequired", $paramkey).' ('.$langs->transnoentitiesnoconv("CustomMandatoryFieldRule").')')."');
12078
12079 return false;
12080 }
12081 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
12082 return true;
12083 });
12084 \n";
12085
12086 // Solution 2: Add property 'required' on input
12087 // so browser will check value and try to focus on it when submitting the form.
12088 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
12089 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12090 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
12091 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
12092 //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";
12093 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
12094 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
12095 // Add 'field required' class on closest td for all input elements : input, textarea and select
12096 //print '}, 500);'; // 500 milliseconds delay
12097
12098 // Now set the class "fieldrequired"
12099 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
12100 }
12101
12102 // If we submit using the cancel button, we remove the required attributes
12103 print 'jQuery("input[name=\'cancel\']").click(function() {
12104 console.log("We click on cancel button so removed all required attribute");
12105 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
12106 });'."\n";
12107 }
12108 }
12109 }
12110 }
12111
12112 print '});'."\n";
12113
12114 // End of tuning
12115 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
12116 print "\n";
12117 print "/* JS CODE TO ENABLE to add memory info */\n";
12118 print 'window.console && console.log("';
12119 if (getDolGlobalString('MEMCACHED_SERVER')) {
12120 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
12121 }
12122 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
12123 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
12124 $micro_end_time = microtime(true);
12125 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
12126 }
12127
12128 if (function_exists("memory_get_usage")) {
12129 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
12130 }
12131 if (function_exists("memory_get_peak_usage")) {
12132 print ' - Real mem peak: '.memory_get_peak_usage(true);
12133 }
12134 if (function_exists("zend_loader_file_encoded")) {
12135 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
12136 }
12137 print '");'."\n";
12138 }
12139
12140 print "\n".'</script>'."\n";
12141
12142 // Google Analytics
12143 // TODO Add a hook here
12144 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
12145 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
12146 foreach ($tmptagarray as $tmptag) {
12147 print "\n";
12148 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
12149 print '
12150 <!-- Global site tag (gtag.js) - Google Analytics -->
12151 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
12152 <script>
12153 window.dataLayer = window.dataLayer || [];
12154 function gtag(){dataLayer.push(arguments);}
12155 gtag(\'js\', new Date());
12156
12157 gtag(\'config\', \''.trim($tmptag).'\');
12158 </script>';
12159 print "\n";
12160 }
12161 }
12162 }
12163
12164 // Add Xdebug coverage of code
12165 if (defined('XDEBUGCOVERAGE')) {
12166 print_r(xdebug_get_code_coverage());
12167 }
12168
12169 // Add DebugBar data
12170 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
12171 if (isset($debugbar['time'])) {
12172 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
12173 $debugbar['time']->stopMeasure('pageaftermaster');
12174 }
12175 print '<!-- Output debugbar data -->'."\n";
12176 $renderer = $debugbar->getJavascriptRenderer();
12177 print $renderer->render();
12178 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
12179 print "\n";
12180 print "<!-- Start of log output\n";
12181 //print '<div class="hidden">'."\n";
12182 foreach ($conf->logbuffer as $logline) {
12183 print $logline."<br>\n";
12184 }
12185 //print '</div>'."\n";
12186 print "End of log output -->\n";
12187 }
12188 }
12189}
12190
12200function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
12201{
12202 if (is_null($string)) {
12203 return array();
12204 }
12205
12206 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
12207 // This is a regex string
12208 $newdelimiter = $delimiter;
12209 } else {
12210 // This is a simple string
12211 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
12212 $newdelimiter = preg_quote($delimiter, '/');
12213 }
12214
12215 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
12216 $ka = array();
12217 foreach ($a as $s) { // each part
12218 if ($s) {
12219 if ($pos = strpos($s, $kv)) { // key/value delimiter
12220 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
12221 } else { // key delimiter not found
12222 $ka[] = trim($s);
12223 }
12224 }
12225 }
12226 return $ka;
12227 }
12228
12229 return array();
12230}
12231
12239function dolExplodeKeepIfQuotes($input)
12240{
12241 // Use regexp to capture words and section in quotes
12242 $matches = array();
12243 preg_match_all('/"([^"]*)"|\'([^\']*)\'|(\S+)/', $input, $matches);
12244
12245 // Merge result and delete empty values
12246
12247 $result = array_map(
12254 static function ($a, $b, $c) {
12255 if ($a !== '') {
12256 return $a;
12257 }
12258 if ($b !== '') {
12259 return $b;
12260 }
12261 if ($c !== '') {
12262 return $c;
12263 }
12264 return '';
12265 },
12266 $matches[1],
12267 $matches[2],
12268 $matches[3]
12269 );
12270 return array_values(array_filter(
12271 $result,
12278 static function ($val) {
12279 return $val !== '';
12280 }
12281 ));
12282}
12283
12284
12291function dol_set_focus($selector)
12292{
12293 print "\n".'<!-- Set focus onto a specific field -->'."\n";
12294 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { console.log("Force focus by dol_set_focus"); jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
12295}
12296
12297
12305function dol_getmypid()
12306{
12307 if (!function_exists('getmypid')) {
12308 return mt_rand(99900000, 99965535);
12309 } else {
12310 return getmypid(); // May be a number on 64 bits (depending on OS)
12311 }
12312}
12313
12335function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
12336{
12337 global $db, $langs;
12338
12339 $value = trim($value);
12340
12341 if ($mode == 0) {
12342 $value = preg_replace('/\*/', '%', $value); // Replace * with %
12343 }
12344 if ($mode == 1) {
12345 $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
12346 }
12347
12348 $value = preg_replace('/\s*\|\s*/', '|', $value);
12349
12350 // Split criteria on ' ' but not if we are inside quotes.
12351 // For mode 3, the split is done later on the , only and not on the ' '.
12352 if ($mode != -3 && $mode != 3) {
12353 $crits = dolExplodeKeepIfQuotes($value);
12354 } else {
12355 $crits = array($value);
12356 }
12357
12358 $res = '';
12359 if (!is_array($fields)) {
12360 $fields = array($fields);
12361 }
12362 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
12363 foreach ($crits as $crit) { // Loop on each AND criteria
12364 $crit = trim($crit);
12365 $i2 = 0; // count the nb of valid criteria added for this this first criteria
12366 $newres = '';
12367
12368 foreach ($fields as $field) {
12369 if ($mode == 1) {
12370 $tmpcrits = explode('|', $crit);
12371 $i3 = 0; // count the nb of valid criteria added for this current field
12372 foreach ($tmpcrits as $tmpcrit) {
12373 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12374 continue;
12375 }
12376 $tmpcrit = trim($tmpcrit);
12377
12378 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12379
12380 $operator = '=';
12381 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
12382
12383 $reg = array();
12384 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
12385 if (!empty($reg[1])) {
12386 $operator = $reg[1];
12387 }
12388 if ($newcrit != '') {
12389 $numnewcrit = price2num($newcrit);
12390 if (is_numeric($numnewcrit)) {
12391 $newres .= $db->sanitize($field).' '.$operator.' '.((float) $numnewcrit); // should be a numeric
12392 } else {
12393 $newres .= '1 = 2'; // force false, we received a corrupted data
12394 }
12395 $i3++; // a criteria was added to string
12396 }
12397 }
12398 $i2++; // a criteria for 1 more field was added to string
12399 } elseif ($mode == 2 || $mode == -2) {
12400 $crit = preg_replace('/[^\-0-9,]/', '', $crit); // ID are always integer
12401 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -2 ? 'NOT ' : '');
12402 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
12403 if ($mode == -2) {
12404 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12405 }
12406 $i2++; // a criteria for 1 more field was added to string
12407 } elseif ($mode == 3 || $mode == -3) {
12408 $tmparray = explode(',', $crit);
12409 if (count($tmparray)) {
12410 $listofcodes = '';
12411 foreach ($tmparray as $val) {
12412 $val = trim($val);
12413 if ($val) {
12414 $listofcodes .= ($listofcodes ? ',' : '');
12415 $listofcodes .= "'".$db->escape($val)."'";
12416 }
12417 }
12418 $newres .= ($i2 > 0 ? ' OR ' : '').$db->sanitize($field)." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
12419 $i2++; // a criteria for 1 more field was added to string
12420 }
12421 if ($mode == -3) {
12422 $newres .= ' OR '.$db->sanitize($field).' IS NULL';
12423 }
12424 } elseif ($mode == 4) {
12425 $tmparray = explode(',', $crit);
12426 if (count($tmparray)) {
12427 $listofcodes = '';
12428 foreach ($tmparray as $val) {
12429 $val = trim($val);
12430 if ($val) {
12431 $newres .= ($i2 > 0 ? " OR (" : "(").$db->sanitize($field)." LIKE '".$db->escape($val).",%'";
12432 $newres .= ' OR '.$db->sanitize($field)." = '".$db->escape($val)."'";
12433 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val)."'";
12434 $newres .= ' OR '.$db->sanitize($field)." LIKE '%,".$db->escape($val).",%'";
12435 $newres .= ')';
12436 $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)
12437 }
12438 }
12439 }
12440 } else { // $mode=0
12441 $tmpcrits = explode('|', $crit);
12442 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
12443 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
12444 if ($tmpcrit !== '0' && empty($tmpcrit)) {
12445 continue;
12446 }
12447 $tmpcrit = trim($tmpcrit);
12448
12449 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
12450 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
12451 } else {
12452 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
12453 }
12454
12455 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
12456 $newres .= $db->sanitize($field)." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
12457 } else {
12458 $tmpcrit2 = $tmpcrit;
12459 $tmpbefore = '%';
12460 $tmpafter = '%';
12461 $tmps = '';
12462
12463 if (preg_match('/^!/', $tmpcrit)) {
12464 $tmps .= $db->sanitize($field)." NOT LIKE "; // ! as exclude character
12465 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
12466 } else {
12467 $tmps .= $db->sanitize($field)." LIKE ";
12468 }
12469 $tmps .= "'";
12470
12471 if (preg_match('/^[\^\$]/', $tmpcrit)) {
12472 $tmpbefore = '';
12473 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
12474 }
12475 if (preg_match('/[\^\$]$/', $tmpcrit)) {
12476 $tmpafter = '';
12477 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
12478 }
12479
12480 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12481 $tmps = "(".$tmps;
12482 }
12483 $newres .= $tmps;
12484 $newres .= $tmpbefore;
12485 $newres .= $db->escape($tmpcrit2);
12486 $newres .= $tmpafter;
12487 $newres .= "'";
12488 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
12489 $newres .= " OR ".$field." IS NULL)";
12490 }
12491 }
12492
12493 $i3++;
12494 }
12495
12496 $i2++; // a criteria for 1 more field was added to string
12497 }
12498 }
12499 if ($newres) {
12500 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
12501 }
12502 $i1++;
12503 }
12504 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
12505
12506 return $res;
12507}
12508
12515function showDirectDownloadLink($object)
12516{
12517 global $langs;
12518
12519 $out = '';
12520 $url = $object->getLastMainDocLink($object->element);
12521
12522 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
12523 if ($url) {
12524 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
12525 $out .= ajax_autoselect("directdownloadlink", '');
12526 } else {
12527 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
12528 }
12529
12530 return $out;
12531}
12532
12541function getImageFileNameForSize($file, $extName, $extImgTarget = '')
12542{
12543 $dirName = dirname($file);
12544 if ($dirName == '.') {
12545 $dirName = '';
12546 }
12547
12548 if (!in_array($extName, array('', '_small', '_mini'))) {
12549 return 'Bad parameter extName';
12550 }
12551
12552 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
12553 $fileName = basename($fileName);
12554
12555 if (empty($extImgTarget)) {
12556 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
12557 }
12558 if (empty($extImgTarget)) {
12559 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
12560 }
12561 if (empty($extImgTarget)) {
12562 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
12563 }
12564 if (empty($extImgTarget)) {
12565 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
12566 }
12567 if (empty($extImgTarget)) {
12568 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
12569 }
12570 if (empty($extImgTarget)) {
12571 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
12572 }
12573
12574 if (!$extImgTarget) {
12575 return $file;
12576 }
12577
12578 $subdir = '';
12579 if ($extName) {
12580 $subdir = 'thumbs/';
12581 }
12582
12583 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
12584}
12585
12586
12596function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
12597{
12598 global $conf, $langs;
12599
12600 if (empty($conf->use_javascript_ajax)) {
12601 return '';
12602 }
12603
12604 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
12605
12606 if ($alldata == 1) {
12607 if ($isAllowedForPreview) {
12608 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));
12609 } else {
12610 return array();
12611 }
12612 }
12613
12614 // old behavior, return a string
12615 if ($isAllowedForPreview) {
12616 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
12617 $title = $langs->transnoentities("Preview");
12618 //$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().
12619 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
12620
12621 // 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,
12622 // 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.
12623 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
12624 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
12625 } else {
12626 return '';
12627 }
12628}
12629
12636function getLabelSpecialCode($idcode)
12637{
12638 global $langs;
12639
12640 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
12641 if ($idcode > 10) {
12642 return 'Module ID '.$idcode;
12643 }
12644 if (!empty($arrayspecialines[$idcode])) {
12645 return $langs->trans($arrayspecialines[$idcode]);
12646 }
12647 return '';
12648}
12649
12658function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
12659{
12660 global $langs;
12661 $out = '<script nonce="'.getNonce().'">
12662 jQuery(document).ready(function () {
12663 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
12664 });
12665 </script>';
12666 if ($addlink) {
12667 if ($textonlink === 'image') {
12668 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
12669 } else {
12670 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
12671 }
12672 }
12673 return $out;
12674}
12675
12683function dolIsAllowedForPreview($file)
12684{
12685 // Check .noexe extension in filename
12686 if (preg_match('/\.noexe$/i', $file)) {
12687 return 0;
12688 }
12689
12690 // Check mime types
12691 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
12692 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
12693 $mime_preview[] = 'svg+xml';
12694 }
12695 //$mime_preview[]='vnd.oasis.opendocument.presentation';
12696 //$mime_preview[]='archive';
12697 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
12698 if ($num_mime !== false) {
12699 return 1;
12700 }
12701
12702 // By default, not allowed for preview
12703 return 0;
12704}
12705
12706
12716function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
12717{
12718 $mime = $default;
12719 $imgmime = 'other.png';
12720 $famime = 'file-o';
12721 $srclang = '';
12722
12723 $tmpfile = preg_replace('/\.noexe$/', '', $file);
12724
12725 // Plain text files
12726 if (preg_match('/\.txt$/i', $tmpfile)) {
12727 $mime = 'text/plain';
12728 $imgmime = 'text.png';
12729 $famime = 'file-alt';
12730 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
12731 $mime = 'text/richtext';
12732 $imgmime = 'text.png';
12733 $famime = 'file-alt';
12734 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
12735 $mime = 'text/csv';
12736 $imgmime = 'text.png';
12737 $famime = 'file-csv';
12738 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
12739 $mime = 'text/tab-separated-values';
12740 $imgmime = 'text.png';
12741 $famime = 'file-alt';
12742 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
12743 $mime = 'text/plain';
12744 $imgmime = 'text.png';
12745 $famime = 'file-alt';
12746 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
12747 $mime = 'text/plain';
12748 $imgmime = 'text.png';
12749 $srclang = 'ini';
12750 $famime = 'file-alt';
12751 } elseif (preg_match('/\.md$/i', $tmpfile)) {
12752 $mime = 'text/plain';
12753 $imgmime = 'text.png';
12754 $srclang = 'md';
12755 $famime = 'file-alt';
12756 } elseif (preg_match('/\.css$/i', $tmpfile)) {
12757 $mime = 'text/css';
12758 $imgmime = 'css.png';
12759 $srclang = 'css';
12760 $famime = 'file-alt';
12761 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
12762 $mime = 'text/plain';
12763 $imgmime = 'text.png';
12764 $srclang = 'lang';
12765 $famime = 'file-alt';
12766 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
12767 $mime = 'text/plain';
12768 $imgmime = 'text.png';
12769 $famime = 'file-alt';
12770 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
12771 $mime = 'text/html';
12772 $imgmime = 'html.png';
12773 $srclang = 'html';
12774 $famime = 'file-alt';
12775 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
12776 $mime = 'text/xml';
12777 $imgmime = 'other.png';
12778 $srclang = 'xml';
12779 $famime = 'file-alt';
12780 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
12781 $mime = 'text/xml';
12782 $imgmime = 'other.png';
12783 $srclang = 'xaml';
12784 $famime = 'file-alt';
12785 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
12786 $mime = 'text/plain';
12787 $imgmime = 'text.png';
12788 $srclang = 'bas';
12789 $famime = 'file-code';
12790 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
12791 $mime = 'text/plain';
12792 $imgmime = 'text.png';
12793 $srclang = 'c';
12794 $famime = 'file-code';
12795 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
12796 $mime = 'text/plain';
12797 $imgmime = 'text.png';
12798 $srclang = 'cpp';
12799 $famime = 'file-code';
12800 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
12801 $mime = 'text/plain';
12802 $imgmime = 'text.png';
12803 $srclang = 'cs';
12804 $famime = 'file-code';
12805 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
12806 $mime = 'text/plain';
12807 $imgmime = 'text.png';
12808 $srclang = 'h';
12809 $famime = 'file-code';
12810 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
12811 $mime = 'text/plain';
12812 $imgmime = 'text.png';
12813 $srclang = 'java';
12814 $famime = 'file-code';
12815 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
12816 $mime = 'text/plain';
12817 $imgmime = 'php.png';
12818 $srclang = 'php';
12819 $famime = 'file-code';
12820 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12821 $mime = 'text/plain';
12822 $imgmime = 'php.png';
12823 $srclang = 'php';
12824 $famime = 'file-code';
12825 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12826 $mime = 'text/plain';
12827 $imgmime = 'pl.png';
12828 $srclang = 'perl';
12829 $famime = 'file-code';
12830 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12831 $mime = 'text/plain';
12832 $imgmime = 'text.png';
12833 $srclang = 'sql';
12834 $famime = 'file-code';
12835 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12836 $mime = 'text/x-javascript';
12837 $imgmime = 'jscript.png';
12838 $srclang = 'js';
12839 $famime = 'file-code';
12840 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12841 $mime = 'application/vnd.oasis.opendocument.presentation';
12842 $imgmime = 'ooffice.png';
12843 $famime = 'file-powerpoint';
12844 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12845 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12846 $imgmime = 'ooffice.png';
12847 $famime = 'file-excel';
12848 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12849 $mime = 'application/vnd.oasis.opendocument.text';
12850 $imgmime = 'ooffice.png';
12851 $famime = 'file-word';
12852 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12853 $mime = 'application/msaccess';
12854 $imgmime = 'mdb.png';
12855 $famime = 'file';
12856 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12857 $mime = 'application/msword';
12858 $imgmime = 'doc.png';
12859 $famime = 'file-word';
12860 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12861 $mime = 'application/msword';
12862 $imgmime = 'doc.png';
12863 $famime = 'file-word';
12864 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12865 $mime = 'application/vnd.ms-excel';
12866 $imgmime = 'xls.png';
12867 $famime = 'file-excel';
12868 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12869 $mime = 'application/vnd.ms-excel';
12870 $imgmime = 'xls.png';
12871 $famime = 'file-excel';
12872 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12873 $mime = 'application/vnd.ms-excel';
12874 $imgmime = 'xls.png';
12875 $famime = 'file-excel';
12876 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12877 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12878 $imgmime = 'xls.png';
12879 $famime = 'file-excel';
12880 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12881 $mime = 'application/vnd.ms-powerpoint';
12882 $imgmime = 'ppt.png';
12883 $famime = 'file-powerpoint';
12884 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12885 $mime = 'application/x-mspowerpoint';
12886 $imgmime = 'ppt.png';
12887 $famime = 'file-powerpoint';
12888 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12889 $mime = 'application/pdf';
12890 $imgmime = 'pdf.png';
12891 $famime = 'file-pdf';
12892 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12893 $mime = 'text/x-bat';
12894 $imgmime = 'script.png';
12895 $srclang = 'dos';
12896 $famime = 'file-code';
12897 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12898 $mime = 'text/x-sh';
12899 $imgmime = 'script.png';
12900 $srclang = 'bash';
12901 $famime = 'file-code';
12902 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12903 $mime = 'text/x-ksh';
12904 $imgmime = 'script.png';
12905 $srclang = 'bash';
12906 $famime = 'file-code';
12907 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12908 $mime = 'text/x-bash';
12909 $imgmime = 'script.png';
12910 $srclang = 'bash';
12911 $famime = 'file-code';
12912 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12913 $mime = 'image/x-icon';
12914 $imgmime = 'image.png';
12915 $famime = 'file-image';
12916 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12917 $mime = 'image/jpeg';
12918 $imgmime = 'image.png';
12919 $famime = 'file-image';
12920 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12921 $mime = 'image/png';
12922 $imgmime = 'image.png';
12923 $famime = 'file-image';
12924 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12925 $mime = 'image/gif';
12926 $imgmime = 'image.png';
12927 $famime = 'file-image';
12928 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12929 $mime = 'image/bmp';
12930 $imgmime = 'image.png';
12931 $famime = 'file-image';
12932 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12933 $mime = 'image/tiff';
12934 $imgmime = 'image.png';
12935 $famime = 'file-image';
12936 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12937 $mime = 'image/svg+xml';
12938 $imgmime = 'image.png';
12939 $famime = 'file-image';
12940 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12941 $mime = 'image/webp';
12942 $imgmime = 'image.png';
12943 $famime = 'file-image';
12944 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12945 $mime = 'text/calendar';
12946 $imgmime = 'other.png';
12947 $famime = 'file-alt';
12948 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12949 $mime = 'text/calendar';
12950 $imgmime = 'other.png';
12951 $famime = 'file-alt';
12952 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12953 $mime = 'application/x-bittorrent';
12954 $imgmime = 'other.png';
12955 $famime = 'file-o';
12956 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12957 $mime = 'audio';
12958 $imgmime = 'audio.png';
12959 $famime = 'file-audio';
12960 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12961 $mime = 'video/mp4';
12962 $imgmime = 'video.png';
12963 $famime = 'file-video';
12964 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12965 $mime = 'video/ogg';
12966 $imgmime = 'video.png';
12967 $famime = 'file-video';
12968 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12969 $mime = 'video/webm';
12970 $imgmime = 'video.png';
12971 $famime = 'file-video';
12972 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12973 $mime = 'video/x-msvideo';
12974 $imgmime = 'video.png';
12975 $famime = 'file-video';
12976 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12977 $mime = 'video/divx';
12978 $imgmime = 'video.png';
12979 $famime = 'file-video';
12980 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12981 $mime = 'video/xvid';
12982 $imgmime = 'video.png';
12983 $famime = 'file-video';
12984 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12985 $mime = 'video';
12986 $imgmime = 'video.png';
12987 $famime = 'file-video';
12988 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12989 // application/xxx where zzz is zip, ...
12990 $mime = 'archive';
12991 $imgmime = 'archive.png';
12992 $famime = 'file-archive';
12993 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12994 $mime = 'application/octet-stream';
12995 $imgmime = 'other.png';
12996 $famime = 'file-o';
12997 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12998 $mime = 'library';
12999 $imgmime = 'library.png';
13000 $famime = 'file-o';
13001 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
13002 $mime = 'error';
13003 $imgmime = 'error.png';
13004 $famime = 'file-alt';
13005 }
13006
13007 if ($famime == 'file-o') {
13008 // file-o seems to not work in fontawesome 5
13009 $famime = 'file';
13010 }
13011
13012 // Return mimetype string
13013 switch ((int) $mode) {
13014 case 1:
13015 $tmp = explode('/', $mime);
13016 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
13017 case 2:
13018 return $imgmime;
13019 case 3:
13020 return $srclang;
13021 case 4:
13022 return $famime;
13023 }
13024 return $mime;
13025}
13026
13038function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
13039{
13040 global $conf, $db;
13041
13042 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
13043
13044 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
13045
13046 if (is_null($dictvalues)) {
13047 $dictvalues = array();
13048
13049 $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
13050 if ($checkentity) {
13051 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
13052 }
13053
13054 $resql = $db->query($sql);
13055 if ($resql) {
13056 while ($obj = $db->fetch_object($resql)) {
13057 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
13058 }
13059 } else {
13060 dol_print_error($db);
13061 }
13062
13063 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
13064 }
13065
13066 if (!empty($dictvalues[$id])) {
13067 // Found
13068 $tmp = $dictvalues[$id];
13069 return (property_exists($tmp, $field) ? $tmp->$field : '');
13070 } else {
13071 // Not found
13072 return '';
13073 }
13074}
13075
13082function colorIsLight($stringcolor)
13083{
13084 $stringcolor = str_replace('#', '', $stringcolor);
13085 $res = -1;
13086 if (!empty($stringcolor)) {
13087 $res = 0;
13088 $tmp = explode(',', $stringcolor);
13089 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
13090 $r = $tmp[0];
13091 $g = $tmp[1];
13092 $b = $tmp[2];
13093 } else {
13094 $hexr = $stringcolor[0].$stringcolor[1];
13095 $hexg = $stringcolor[2].$stringcolor[3];
13096 $hexb = $stringcolor[4].$stringcolor[5];
13097 $r = hexdec($hexr);
13098 $g = hexdec($hexg);
13099 $b = hexdec($hexb);
13100 }
13101 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
13102 if ($bright > 0.6) {
13103 $res = 1;
13104 }
13105 }
13106 return $res;
13107}
13108
13117function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
13118{
13119 global $conf;
13120
13121 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
13122 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
13123 if (empty($menuentry['enabled'])) {
13124 return 0; // Entry disabled by condition
13125 }
13126 if ($type_user && array_key_exists('module', $menuentry) && $menuentry['module']) {
13127 $tmploops = explode('|', $menuentry['module']);
13128 $found = 0;
13129 foreach ($tmploops as $tmploop) {
13130 if (in_array($tmploop, $listofmodulesforexternal)) {
13131 $found++;
13132 break;
13133 }
13134 }
13135 if (!$found) {
13136 return 0; // Entry is for menus all excluded to external users
13137 }
13138 }
13139 if (!$menuentry['perms'] && $type_user) {
13140 return 0; // No permissions and user is external
13141 }
13142 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
13143 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
13144 }
13145 if (!$menuentry['perms']) {
13146 return 2; // No permissions and user is external
13147 }
13148 return 1;
13149}
13150
13158function roundUpToNextMultiple($n, $x = 5)
13159{
13160 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
13161 return (int) $result;
13162}
13163
13175function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
13176{
13177 $csstouse = 'badge';
13178 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
13179 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
13180 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
13181
13182 $attr = array(
13183 'class' => $csstouse
13184 );
13185
13186 if (empty($html)) {
13187 $html = $label;
13188 }
13189
13190 if (!empty($url)) {
13191 $attr['href'] = $url;
13192 }
13193
13194 if ($mode === 'dot') {
13195 $attr['class'] .= ' classfortooltip';
13196 $attr['title'] = $html;
13197 $attr['aria-label'] = $label;
13198 $html = '';
13199 }
13200
13201 // Override attr
13202 if (!empty($params['attr']) && is_array($params['attr'])) {
13203 foreach ($params['attr'] as $key => $value) {
13204 if ($key == 'class') {
13205 $attr['class'] .= ' '.$value;
13206 } elseif ($key == 'classOverride') {
13207 $attr['class'] = $value;
13208 } else {
13209 $attr[$key] = $value;
13210 }
13211 }
13212 }
13213
13214 // TODO: add hook
13215
13216 // escape all attribute
13217 $attr = array_map('dolPrintHTMLForAttribute', $attr);
13218
13219 $TCompiledAttr = array();
13220 foreach ($attr as $key => $value) {
13221 $TCompiledAttr[] = $key.'="'.$value.'"';
13222 }
13223
13224 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
13225
13226 $tag = !empty($url) ? 'a' : 'span';
13227
13228 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
13229}
13230
13231
13244function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
13245{
13246 global $conf;
13247
13248 $return = '';
13249 $dolGetBadgeParams = array();
13250
13251 if (!empty($params['badgeParams'])) {
13252 $dolGetBadgeParams = $params['badgeParams'];
13253 }
13254
13255 // TODO : add a hook
13256 if ($displayMode == 0) {
13257 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
13258 } elseif ($displayMode == 1) {
13259 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13260 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
13261 // Use status with images (for backward compatibility)
13262 $return = '';
13263 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
13264 $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>' : '');
13265
13266 // For small screen, we always use the short label instead of long label.
13267 if (!empty($conf->dol_optimize_smallscreen)) {
13268 if ($displayMode == 0) {
13269 $displayMode = 1;
13270 } elseif ($displayMode == 4) {
13271 $displayMode = 2;
13272 } elseif ($displayMode == 6) {
13273 $displayMode = 5;
13274 }
13275 }
13276
13277 // For backward compatibility. Image's filename are still in French, so we use this array to convert
13278 $statusImg = array(
13279 'status0' => 'statut0',
13280 'status1' => 'statut1',
13281 'status2' => 'statut2',
13282 'status3' => 'statut3',
13283 'status4' => 'statut4',
13284 'status5' => 'statut5',
13285 'status6' => 'statut6',
13286 'status7' => 'statut7',
13287 'status8' => 'statut8',
13288 'status9' => 'statut9'
13289 );
13290
13291 if (!empty($statusImg[$statusType])) {
13292 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
13293 } else {
13294 $htmlImg = img_picto($statusLabel, $statusType);
13295 }
13296
13297 if ($displayMode === 2) {
13298 $return = $htmlImg.' '.$htmlLabelShort;
13299 } elseif ($displayMode === 3) {
13300 $return = $htmlImg;
13301 } elseif ($displayMode === 4) {
13302 $return = $htmlImg.' '.$htmlLabel;
13303 } elseif ($displayMode === 5) {
13304 $return = $htmlLabelShort.' '.$htmlImg;
13305 } else { // $displayMode >= 6
13306 $return = $htmlLabel.' '.$htmlImg;
13307 }
13308 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
13309 // Use new badge
13310 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
13311
13312 $dolGetBadgeParams['attr']['class'] = 'badge-status';
13313 if (empty($dolGetBadgeParams['attr']['title'])) {
13314 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
13315 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
13316 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
13317 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
13318 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr((string) $dolGetBadgeParams['attr']['title'], 1);
13319 }
13320
13321 if ($displayMode == 3) {
13322 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
13323 } elseif ($displayMode === 5) {
13324 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
13325 } else {
13326 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
13327 }
13328 }
13329
13330 return $return;
13331}
13332
13333
13369function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
13370{
13371 global $hookmanager, $action, $object, $langs;
13372
13373 // If $url is an array, we must build a dropdown button or recursively iterate over each value
13374 if (is_array($url)) {
13375 // Loop on $url array to remove entries of disabled modules
13376 foreach ($url as $key => $subbutton) {
13377 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
13378 unset($url[$key]);
13379 }
13380 }
13381
13382 $out = '';
13383
13384 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
13385 foreach ($url as $button) {
13386 if (!empty($button['lang'])) {
13387 $langs->load($button['lang']);
13388 }
13389 $label = $langs->trans($button['label']);
13390 $text = $button['text'] ?? '';
13391 $actionType = $button['actionType'] ?? '';
13392 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
13393 $id = $button['id'] ?? '';
13394 $userRight = $button['perm'] ?? 1;
13395 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
13396
13397 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
13398 }
13399 return $out;
13400 }
13401
13402 if (count($url) > 1) {
13403 $out .= '<div class="dropdown inline-block dropdown-holder">';
13404 $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>';
13405 $out .= '<div class="dropdown-content">';
13406 foreach ($url as $subbutton) {
13407 if (!empty($subbutton['lang'])) {
13408 $langs->load($subbutton['lang']);
13409 }
13410
13411 if (!empty($subbutton['urlraw'])) {
13412 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13413 } else {
13414 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13415 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13416 }
13417
13418 $subbuttonparam = array();
13419 if (!empty($subbutton['attr'])) {
13420 $subbuttonparam['attr'] = $subbutton['attr'];
13421 }
13422 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
13423
13424 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
13425 }
13426 $out .= "</div>";
13427 $out .= "</div>";
13428 } else {
13429 foreach ($url as $subbutton) { // Should loop on 1 record only
13430 if (!empty($subbutton['lang'])) {
13431 $langs->load($subbutton['lang']);
13432 }
13433
13434 if (!empty($subbutton['urlraw'])) {
13435 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
13436 } else {
13437 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
13438 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
13439 }
13440
13441 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
13442 }
13443 }
13444
13445 return $out;
13446 }
13447
13448 // Here, $url is a simple link
13449
13450 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
13451 $class = "dropdown-item";
13452 } else {
13453 $class = 'butAction';
13454 if ($actionType == 'danger' || $actionType == 'delete') {
13455 $class = 'butActionDelete';
13456 if (!empty($url) && strpos($url, 'token=') === false) {
13457 $url .= '&token='.newToken();
13458 }
13459 }
13460 }
13461 $attr = array(
13462 'class' => $class,
13463 'href' => empty($url) ? '' : $url,
13464 'title' => $label
13465 );
13466
13467 if (empty($text)) {
13468 $text = $label;
13469 $attr['title'] = ''; // if html not set, leave label on title is redundant
13470 } else {
13471 $attr['title'] = $label;
13472 $attr['aria-label'] = $label;
13473 }
13474
13475 if (empty($userRight) || $userRight < 0) {
13476 $attr['class'] = 'butActionRefused';
13477 $attr['href'] = '';
13478 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
13479 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '').(empty($userRight) ? $langs->trans('NotEnoughPermissions') : '');
13480 }
13481
13482 if (!empty($id)) {
13483 $attr['id'] = $id;
13484 }
13485
13486 // Override attr
13487 if (!empty($params['attr']) && is_array($params['attr'])) {
13488 foreach ($params['attr'] as $key => $value) {
13489 if ($key == 'class') {
13490 $attr['class'] .= ' '.$value;
13491 } elseif ($key == 'classOverride') {
13492 $attr['class'] = $value;
13493 } else {
13494 $attr[$key] = $value;
13495 }
13496 }
13497 }
13498
13499 // automatic add tooltip when title is detected
13500 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13501 $attr['class'] .= ' classfortooltip';
13502 }
13503
13504 // Js Confirm button
13505 if ($userRight && !empty($params['confirm'])) {
13506 if (!is_array($params['confirm'])) {
13507 $params['confirm'] = array();
13508 }
13509
13510 if (empty($params['confirm']['url'])) {
13511 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
13512 }
13513
13514 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
13515 $attr['data-confirm-url'] = $params['confirm']['url'];
13516 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
13517 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
13518 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
13519 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
13520 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
13521 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
13522
13523 $attr['class'] .= ' butActionConfirm';
13524 }
13525
13526 if (isset($attr['href']) && empty($attr['href'])) {
13527 unset($attr['href']);
13528 }
13529
13530 $TCompiledAttr = array();
13531 foreach ($attr as $key => $value) {
13532 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
13533 // Not recommended
13534 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13535 } elseif ($key == 'href') {
13536 $value = dolPrintHTMLForAttributeUrl($value);
13537 } else {
13538 $value = dolPrintHTMLForAttribute($value);
13539 }
13540
13541 $TCompiledAttr[] = $key.'="'.$value.'"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13542 }
13543
13544 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
13545
13546 $tag = !empty($attr['href']) ? 'a' : 'span';
13547
13548
13549 $parameters = array(
13550 'TCompiledAttr' => $TCompiledAttr, // array
13551 'compiledAttributes' => $compiledAttributes, // string
13552 'attr' => $attr,
13553 'tag' => $tag,
13554 'label' => $label,
13555 'html' => $text,
13556 'actionType' => $actionType,
13557 'url' => $url,
13558 'id' => $id,
13559 'userRight' => $userRight,
13560 'params' => $params
13561 );
13562
13563 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
13564 if ($reshook < 0) {
13565 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
13566 }
13567
13568 if (empty($reshook)) {
13569 if (dol_textishtml($text)) { // If content already HTML encoded
13570 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
13571 } else {
13572 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
13573 }
13574 } else {
13575 return $hookmanager->resPrint;
13576 }
13577}
13578
13579
13588function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
13589{
13590 if (empty($url)) {
13591 return '';
13592 }
13593
13594 $parsedUrl = parse_url($url);
13595 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
13596 return $url;
13597 }
13598
13599 if (!empty($parsedUrl['query'])) {
13600 // Use parse_str() function to parse the string passed via URL
13601 parse_str($parsedUrl['query'], $urlQuery);
13602 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
13603 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
13604 }
13605 }
13606
13607 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
13608 $url = DOL_URL_ROOT.$url;
13609 }
13610
13611 return $url;
13612}
13613
13614
13621function dolGetButtonTitleSeparator($moreClass = "")
13622{
13623 return '<span class="button-title-separator '.$moreClass.'" ></span>';
13624}
13625
13632function getFieldErrorIcon($fieldValidationErrorMsg)
13633{
13634 $out = '';
13635 if (!empty($fieldValidationErrorMsg)) {
13636 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
13637 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
13638 $out .= '</span>';
13639 }
13640
13641 return $out;
13642}
13643
13656function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
13657{
13658 global $langs, $user;
13659
13660 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
13661 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
13662 return '';
13663 }
13664 // Fix old picto fa-th-list to use fa-grid-vertical instead
13665 if ($iconClass == 'fa fa-th-list imgforviewmode') {
13666 $iconClass = ' fa fa-grip-horizontal imgforviewmode';
13667 }
13668
13669 $class = 'btnTitle';
13670 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
13671 $class .= ' btnTitlePlus';
13672 }
13673 $useclassfortooltip = 1;
13674
13675 if (!empty($params['morecss'])) {
13676 $class .= ' '.$params['morecss'];
13677 }
13678
13679 $attr = array(
13680 'class' => $class,
13681 'href' => empty($url) ? '' : $url
13682 );
13683
13684 if (!empty($helpText)) {
13685 $attr['title'] = $helpText;
13686 } elseif ($label) { // empty($attr['title']) &&
13687 $attr['title'] = $label;
13688 $useclassfortooltip = 0;
13689 }
13690
13691 if ($status == 2) {
13692 $attr['class'] .= ' btnTitleSelected';
13693 } elseif ($status <= 0) {
13694 $attr['class'] .= ' refused';
13695
13696 $attr['href'] = '';
13697
13698 if ($status == -1) { // disable
13699 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
13700 } elseif ($status == 0) { // Not enough permissions
13701 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
13702 }
13703 }
13704
13705 if (!empty($attr['title']) && $useclassfortooltip) {
13706 $attr['class'] .= ' classfortooltip';
13707 }
13708
13709 if (!empty($id)) {
13710 $attr['id'] = $id;
13711 }
13712
13713 // Override attr
13714 if (!empty($params['attr']) && is_array($params['attr'])) {
13715 foreach ($params['attr'] as $key => $value) {
13716 if ($key == 'class') {
13717 $attr['class'] .= ' '.$value;
13718 } elseif ($key == 'classOverride') {
13719 $attr['class'] = $value;
13720 } else {
13721 $attr[$key] = $value;
13722 }
13723 }
13724 }
13725
13726 if (isset($attr['href']) && empty($attr['href'])) {
13727 unset($attr['href']);
13728 }
13729
13730 // TODO : add a hook
13731
13732 // Generate attributes with escapement
13733 $TCompiledAttr = array();
13734 foreach ($attr as $key => $value) {
13735 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
13736 }
13737
13738 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
13739
13740 $tag = (empty($attr['href']) ? 'span' : 'a');
13741
13742 $button = '<'.$tag.' '.$compiledAttributes.'>';
13743 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
13744 if (!empty($params['forcenohideoftext'])) {
13745 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
13746 }
13747 $button .= '</'.$tag.'>';
13748
13749 return $button;
13750}
13751
13761function getElementProperties($elementType)
13762{
13763 global $conf, $db, $hookmanager;
13764
13765 $regs = array();
13766
13767 //$element_type='facture';
13768
13769 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
13770
13771 // Parse element/subelement
13772 $module = $elementType;
13773 $element = $elementType;
13774 $subelement = $elementType;
13775 $table_element = $elementType;
13776
13777 // If we ask a resource form external module (instead of default path)
13778 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
13779 $element = $subelement = $regs[1];
13780 $module = $regs[2];
13781 }
13782
13783 // If we ask a resource for a string with an element and a subelement
13784 // Example 'project_task'
13785 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
13786 $module = $element = $regs[1];
13787 $subelement = $regs[2];
13788 }
13789
13790 // Object lines will use parent classpath and module ref
13791 if (substr($elementType, -3) == 'det') {
13792 $module = preg_replace('/det$/', '', $element);
13793 $subelement = preg_replace('/det$/', '', $subelement);
13794 $classpath = $module.'/class';
13795 $classfile = $module;
13796 $classname = preg_replace('/det$/', 'Line', $element);
13797 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
13798 $classname = preg_replace('/det$/', 'Ligne', $element);
13799 }
13800 }
13801 // For compatibility and to work with non standard path
13802 if ($elementType == "action" || $elementType == "actioncomm") {
13803 $classpath = 'comm/action/class';
13804 $subelement = 'Actioncomm';
13805 $module = 'agenda';
13806 $table_element = 'actioncomm';
13807 } elseif ($elementType == 'cronjob') {
13808 $classpath = 'cron/class';
13809 $module = 'cron';
13810 $table_element = 'cron';
13811 } elseif ($elementType == 'adherent_type') {
13812 $classpath = 'adherents/class';
13813 $classfile = 'adherent_type';
13814 $module = 'adherent';
13815 $subelement = 'adherent_type';
13816 $classname = 'AdherentType';
13817 $table_element = 'adherent_type';
13818 } elseif ($elementType == 'bank_account') {
13819 $classpath = 'compta/bank/class';
13820 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13821 $classfile = 'account';
13822 $classname = 'Account';
13823 } elseif ($elementType == 'bank_line') {
13824 $classpath = 'compta/bank/class';
13825 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13826 $classfile = 'account';
13827 $classname = 'AccountLine';
13828 } elseif ($elementType == 'category') {
13829 $classpath = 'categories/class';
13830 $module = 'categorie';
13831 $subelement = 'categorie';
13832 $table_element = 'categorie';
13833 } elseif ($elementType == 'contact') {
13834 $classpath = 'contact/class';
13835 $classfile = 'contact';
13836 $module = 'societe';
13837 $subelement = 'contact';
13838 $table_element = 'socpeople';
13839 } elseif ($elementType == 'inventory') {
13840 $module = 'product';
13841 $classpath = 'product/inventory/class';
13842 } elseif ($elementType == 'inventoryline') {
13843 $module = 'product';
13844 $classpath = 'product/inventory/class';
13845 $table_element = 'inventorydet';
13846 $parent_element = 'inventory';
13847 } elseif ($elementType == 'stock' || $elementType == 'entrepot' || $elementType == 'warehouse') {
13848 $module = 'stock';
13849 $classpath = 'product/stock/class';
13850 $classfile = 'entrepot';
13851 $classname = 'Entrepot';
13852 $table_element = 'entrepot';
13853 } elseif ($elementType == 'project') {
13854 $classpath = 'projet/class';
13855 $module = 'projet';
13856 $table_element = 'projet';
13857 } elseif ($elementType == 'project_task') {
13858 $classpath = 'projet/class';
13859 $module = 'projet';
13860 $subelement = 'task';
13861 $table_element = 'projet_task';
13862 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13863 $classpath = 'compta/facture/class';
13864 $module = 'facture';
13865 $subelement = 'facture';
13866 $table_element = 'facture';
13867 } elseif ($elementType == 'facturedet') {
13868 $classpath = 'compta/facture/class';
13869 $classfile = 'facture';
13870 $classname = 'FactureLigne';
13871 $module = 'facture';
13872 $table_element = 'facturedet';
13873 $parent_element = 'facture';
13874 } elseif ($elementType == 'facturerec') {
13875 $classpath = 'compta/facture/class';
13876 $classfile = 'facture-rec';
13877 $module = 'facture';
13878 $classname = 'FactureRec';
13879 } elseif ($elementType == 'commande' || $elementType == 'order') {
13880 $classpath = 'commande/class';
13881 $module = 'commande';
13882 $subelement = 'commande';
13883 $table_element = 'commande';
13884 } elseif ($elementType == 'commandedet') {
13885 $classpath = 'commande/class';
13886 $classfile = 'commande';
13887 $classname = 'OrderLine';
13888 $module = 'commande';
13889 $table_element = 'commandedet';
13890 $parent_element = 'commande';
13891 } elseif ($elementType == 'propal') {
13892 $classpath = 'comm/propal/class';
13893 $table_element = 'propal';
13894 } elseif ($elementType == 'propaldet') {
13895 $classpath = 'comm/propal/class';
13896 $classfile = 'propal';
13897 $subelement = 'propaleligne';
13898 $module = 'propal';
13899 $table_element = 'propaldet';
13900 $parent_element = 'propal';
13901 } elseif ($elementType == 'shipping') {
13902 $classpath = 'expedition/class';
13903 $classfile = 'expedition';
13904 $classname = 'Expedition';
13905 $module = 'expedition';
13906 $table_element = 'expedition';
13907 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13908 $classpath = 'expedition/class';
13909 $classfile = 'expedition';
13910 $classname = 'ExpeditionLigne';
13911 $module = 'expedition';
13912 $table_element = 'expeditiondet';
13913 $parent_element = 'expedition';
13914 } elseif ($elementType == 'delivery_note') {
13915 $classpath = 'delivery/class';
13916 $subelement = 'delivery';
13917 $module = 'expedition';
13918 } elseif ($elementType == 'delivery') {
13919 $classpath = 'delivery/class';
13920 $subelement = 'delivery';
13921 $module = 'expedition';
13922 } elseif ($elementType == 'deliverydet') {
13923 // @todo
13924 } elseif ($elementType == 'supplier_proposal') {
13925 $classpath = 'supplier_proposal/class';
13926 $module = 'supplier_proposal';
13927 $element = 'supplierproposal';
13928 $classfile = 'supplier_proposal';
13929 $subelement = 'supplierproposal';
13930 } elseif ($elementType == 'supplier_proposaldet') {
13931 $classpath = 'supplier_proposal/class';
13932 $module = 'supplier_proposal';
13933 $classfile = 'supplier_proposal';
13934 $classname = 'SupplierProposalLine';
13935 $table_element = 'supplier_proposaldet';
13936 $parent_element = 'supplier_proposal';
13937 } elseif ($elementType == 'contract') {
13938 $classpath = 'contrat/class';
13939 $module = 'contrat';
13940 $subelement = 'contrat';
13941 $table_element = 'contract';
13942 } elseif ($elementType == 'contratdet') {
13943 $classpath = 'contrat/class';
13944 $module = 'contrat';
13945 $table_element = 'contratdet';
13946 $parent_element = 'contrat';
13947 } elseif ($elementType == 'mailing') {
13948 $classpath = 'comm/mailing/class';
13949 $module = 'mailing';
13950 $classfile = 'mailing';
13951 $classname = 'Mailing';
13952 $subelement = '';
13953 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13954 $classpath = 'adherents/class';
13955 $module = 'adherent';
13956 $subelement = 'adherent';
13957 $table_element = 'adherent';
13958 } elseif ($elementType == 'subscription') {
13959 $classpath = 'adherents/class';
13960 $classfile = 'subscription';
13961 $module = 'adherent';
13962 $subelement = 'subscription';
13963 $classname = 'Subscription';
13964 $table_element = 'subscription';
13965 } elseif ($elementType == 'usergroup') {
13966 $classpath = 'user/class';
13967 $module = 'user';
13968 } elseif ($elementType == 'mo') {
13969 $classpath = 'mrp/class';
13970 $classfile = 'mo';
13971 $classname = 'Mo';
13972 $module = 'mrp';
13973 $subelement = '';
13974 $table_element = 'mrp_mo';
13975 } elseif ($elementType == 'mrp_production') {
13976 $classpath = 'mrp/class';
13977 $classfile = 'mo';
13978 $classname = 'MoLine';
13979 $module = 'mrp';
13980 $subelement = '';
13981 $table_element = 'mrp_production';
13982 $parent_element = 'mo';
13983 } elseif ($elementType == 'cabinetmed_cons') {
13984 $classpath = 'cabinetmed/class';
13985 $module = 'cabinetmed';
13986 $subelement = 'cabinetmedcons';
13987 $table_element = 'cabinetmedcons';
13988 } elseif ($elementType == 'fichinter') {
13989 $classpath = 'fichinter/class';
13990 $module = 'ficheinter';
13991 $subelement = 'fichinter';
13992 $table_element = 'fichinter';
13993 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13994 $classpath = 'resource/class';
13995 $module = 'resource';
13996 $subelement = 'dolresource';
13997 $table_element = 'resource';
13998 } elseif ($elementType == 'opensurvey_sondage') {
13999 $classpath = 'opensurvey/class';
14000 $module = 'opensurvey';
14001 $subelement = 'opensurveysondage';
14002 } elseif ($elementType == 'order_supplier' || $elementType == 'supplier_order' ||$elementType == 'commande_fournisseur' || $elementType == 'commandefournisseur') {
14003 $classpath = 'fourn/class';
14004 $module = 'fournisseur';
14005 $classfile = 'fournisseur.commande';
14006 $element = 'order_supplier';
14007 $subelement = '';
14008 $classname = 'CommandeFournisseur';
14009 $table_element = 'commande_fournisseur';
14010 } elseif ($elementType == 'commande_fournisseurdet') {
14011 $classpath = 'fourn/class';
14012 $module = 'fournisseur';
14013 $classfile = 'fournisseur.commande';
14014 $element = 'commande_fournisseurdet';
14015 $subelement = '';
14016 $classname = 'CommandeFournisseurLigne';
14017 $table_element = 'commande_fournisseurdet';
14018 $parent_element = 'commande_fournisseur';
14019 } elseif ($elementType == 'invoice_supplier' || $elementType == 'supplier_invoice' || $elementType == 'facture_fourn') {
14020 $classpath = 'fourn/class';
14021 $module = 'fournisseur';
14022 $classfile = 'fournisseur.facture';
14023 $element = 'invoice_supplier';
14024 $subelement = '';
14025 $classname = 'FactureFournisseur';
14026 $table_element = 'facture_fourn';
14027 } elseif ($elementType == 'facture_fourn_det') {
14028 $classpath = 'fourn/class';
14029 $module = 'fournisseur';
14030 $classfile = 'fournisseur.facture';
14031 $element = 'facture_fourn_det';
14032 $subelement = '';
14033 $classname = 'SupplierInvoiceLine';
14034 $table_element = 'facture_fourn_det';
14035 $parent_element = 'invoice_supplier';
14036 } elseif ($elementType == "service") {
14037 $classpath = 'product/class';
14038 $subelement = 'product';
14039 $table_element = 'product';
14040 } elseif ($elementType == 'salary') {
14041 $classpath = 'salaries/class';
14042 $module = 'salaries';
14043 } elseif ($elementType == 'payment_salary') {
14044 $classpath = 'salaries/class';
14045 $classfile = 'paymentsalary';
14046 $classname = 'PaymentSalary';
14047 $module = 'salaries';
14048 } elseif ($elementType == 'productlot') {
14049 $module = 'productbatch';
14050 $classpath = 'product/stock/class';
14051 $classfile = 'productlot';
14052 $classname = 'Productlot';
14053 $element = 'productlot';
14054 $subelement = '';
14055 $table_element = 'product_lot';
14056 } elseif ($elementType == 'societeaccount') {
14057 $classpath = 'societe/class';
14058 $classfile = 'societeaccount';
14059 $classname = 'SocieteAccount';
14060 $module = 'societe';
14061 } elseif ($elementType == 'websitepage' || $elementType == 'website_page') {
14062 $classpath = 'website/class';
14063 $classfile = 'websitepage';
14064 $classname = 'Websitepage';
14065 $module = 'website';
14066 $subelement = 'websitepage';
14067 $table_element = 'website_page';
14068 } elseif ($elementType == 'fiscalyear') {
14069 $classpath = 'core/class';
14070 $module = 'accounting';
14071 $subelement = 'fiscalyear';
14072 } elseif ($elementType == 'chargesociales') {
14073 $classpath = 'compta/sociales/class';
14074 $module = 'tax';
14075 $table_element = 'chargesociales';
14076 } elseif ($elementType == 'tva') {
14077 $classpath = 'compta/tva/class';
14078 $module = 'tax';
14079 $subdir = '/vat';
14080 $table_element = 'tva';
14081 } elseif ($elementType == 'emailsenderprofile') {
14082 $module = '';
14083 $classpath = 'core/class';
14084 $classfile = 'emailsenderprofile';
14085 $classname = 'EmailSenderProfile';
14086 $table_element = 'c_email_senderprofile';
14087 $subelement = '';
14088 } elseif ($elementType == 'conferenceorboothattendee') {
14089 $classpath = 'eventorganization/class';
14090 $classfile = 'conferenceorboothattendee';
14091 $classname = 'ConferenceOrBoothAttendee';
14092 $module = 'eventorganization';
14093 } elseif ($elementType == 'conferenceorbooth') {
14094 $classpath = 'eventorganization/class';
14095 $classfile = 'conferenceorbooth';
14096 $classname = 'ConferenceOrBooth';
14097 $module = 'eventorganization';
14098 } elseif ($elementType == 'ccountry') {
14099 $module = '';
14100 $classpath = 'core/class';
14101 $classfile = 'ccountry';
14102 $classname = 'Ccountry';
14103 $table_element = 'c_country';
14104 $subelement = '';
14105 } elseif ($elementType == 'ecmfiles') {
14106 $module = 'ecm';
14107 $classpath = 'ecm/class';
14108 $classfile = 'ecmfiles';
14109 $classname = 'Ecmfiles';
14110 $table_element = 'ecmfiles';
14111 $subelement = '';
14112 } elseif ($elementType == 'knowledgerecord' || $elementType == 'knowledgemanagement') {
14113 $module = 'knowledgemanagement';
14114 $classpath = 'knowledgemanagement/class';
14115 $classfile = 'knowledgerecord';
14116 $classname = 'KnowledgeRecord';
14117 $table_element = 'knowledgemanagement_knowledgerecord';
14118 $subelement = '';
14119 } elseif ($elementType == 'customer') {
14120 $module = 'societe';
14121 $classpath = 'societe/class';
14122 $classfile = 'client';
14123 $classname = 'Client';
14124 $table_element = 'societe';
14125 $subelement = '';
14126 } elseif ($elementType == 'fournisseur' || $elementType == 'supplier') {
14127 $module = 'societe';
14128 $classpath = 'fourn/class';
14129 $classfile = 'fournisseur';
14130 $classname = 'Fournisseur';
14131 $table_element = 'societe';
14132 $subelement = '';
14133 }
14134
14135
14136 if (empty($classfile)) {
14137 $classfile = strtolower($subelement);
14138 }
14139 if (empty($classname)) {
14140 $classname = ucfirst($subelement);
14141 }
14142 if (empty($classpath)) {
14143 $classpath = $module.'/class';
14144 }
14145
14146 //print 'getElementProperties subdir='.$subdir;
14147
14148 // Set dir_output
14149 if ($module && isset($conf->$module)) { // The generic case
14150 if (!empty($conf->$module->multidir_output[$conf->entity])) {
14151 $dir_output = $conf->$module->multidir_output[$conf->entity];
14152 } elseif (!empty($conf->$module->output[$conf->entity])) {
14153 $dir_output = $conf->$module->output[$conf->entity];
14154 } elseif (!empty($conf->$module->dir_output)) {
14155 $dir_output = $conf->$module->dir_output;
14156 }
14157 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
14158 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
14159 } elseif (!empty($conf->$module->temp[$conf->entity])) {
14160 $dir_temp = $conf->$module->temp[$conf->entity];
14161 } elseif (!empty($conf->$module->dir_temp)) {
14162 $dir_temp = $conf->$module->dir_temp;
14163 }
14164 }
14165
14166 // Overwrite value for special cases
14167 if ($element == 'order_supplier' && isModEnabled('fournisseur')) {
14168 $dir_output = $conf->fournisseur->commande->dir_output;
14169 $dir_temp = $conf->fournisseur->commande->dir_temp;
14170 } elseif ($element == 'invoice_supplier' && isModEnabled('fournisseur')) {
14171 $dir_output = $conf->fournisseur->facture->dir_output;
14172 $dir_temp = $conf->fournisseur->facture->dir_temp;
14173 }
14174 $dir_output .= $subdir;
14175 $dir_temp .= $subdir;
14176
14177 $elementProperties = array(
14178 'module' => $module,
14179 'element' => $element,
14180 'table_element' => $table_element,
14181 'subelement' => $subelement,
14182 'classpath' => $classpath,
14183 'classfile' => $classfile,
14184 'classname' => $classname,
14185 'dir_output' => $dir_output,
14186 'dir_temp' => $dir_temp,
14187 'parent_element' => $parent_element,
14188 );
14189
14190
14191 // Add hook
14192 if (!is_object($hookmanager)) {
14193 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14194 $hookmanager = new HookManager($db);
14195 }
14196 $hookmanager->initHooks(array('elementproperties'));
14197
14198
14199 // Hook params
14200 $parameters = array(
14201 'elementType' => $elementType,
14202 'elementProperties' => $elementProperties
14203 );
14204
14205 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
14206
14207 if ($reshook) {
14208 $elementProperties = $hookmanager->resArray;
14209 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
14210 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
14211 }
14212
14213 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
14214 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
14215 unset($hookmanager->contextarray[$key]);
14216 }
14217
14218 return $elementProperties;
14219}
14220
14233function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
14234{
14235 global $db, $conf;
14236
14237 $ret = 0;
14238
14239 $element_prop = getElementProperties($element_type);
14240
14241 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
14242 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
14243 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
14244 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
14245 // of service and we will return properties of a product.
14246 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
14247 } elseif ($element_prop['module'] == 'societeaccount') {
14248 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
14249 } else {
14250 $ismodenabled = isModEnabled($element_prop['module']);
14251 }
14252 //var_dump('element_type='.$element_type);
14253 //var_dump($element_prop);
14254 //var_dump($element_prop['module'].' '.$ismodenabled);
14255 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
14256 if ($useCache === 1 && $element_id > 0
14257 && !empty($conf->cache['fetchObjectByElement'][$element_type])
14258 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14259 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
14260 ) {
14261 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
14262 }
14263
14264 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14265
14266 if (class_exists($element_prop['classname'])) {
14267 $className = $element_prop['classname'];
14268 $objecttmp = new $className($db);
14269 '@phan-var-force CommonObject $objecttmp';
14270
14271 if ($element_id > 0 || !empty($element_ref)) {
14272 $ret = $objecttmp->fetch($element_id, $element_ref);
14273 if ($ret >= 0) {
14274 if (empty($objecttmp->module)) {
14275 $objecttmp->module = $element_prop['module'];
14276 }
14277
14278 if ($useCache > 0) {
14279 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
14280 $conf->cache['fetchObjectByElement'][$element_type] = [];
14281 }
14282
14283 // Manage cache limit
14284 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
14285 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
14286 }
14287
14288 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
14289 }
14290
14291 return $objecttmp;
14292 }
14293 } else {
14294 return $objecttmp; // returned an object without fetch
14295 }
14296 } else {
14297 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
14298 return -1;
14299 }
14300 }
14301
14302 return $ret;
14303}
14304
14310function getExecutableContent()
14311{
14312 $arrayofregexextension = array(
14313 'htm', 'html', 'shtml', 'js', 'phar', 'php', 'php3', 'php4', 'php5', 'phtml', 'pht', 'pl', 'py', 'cgi', 'ksh', 'sh', 'shtml',
14314 'bash', 'bat', 'cmd', 'wpk', 'exe', 'dmg', 'appimage'
14315 );
14316
14317 return $arrayofregexextension;
14318}
14319
14326function isAFileWithExecutableContent($filename)
14327{
14328 $arrayofregexextension = getExecutableContent();
14329
14330 foreach ($arrayofregexextension as $fileextension) {
14331 if (preg_match('/\.'.preg_quote($fileextension, '/').'$/i', $filename)) {
14332 return true;
14333 }
14334 }
14335
14336 return false;
14337}
14338
14346function newToken()
14347{
14348 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
14349}
14350
14358function currentToken()
14359{
14360 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
14361}
14362
14368function getNonce()
14369{
14370 global $conf;
14371
14372 if (empty($conf->cache['nonce'])) {
14373 $conf->cache['nonce'] = dolGetRandomBytes(8);
14374 }
14375
14376 return $conf->cache['nonce'];
14377}
14378
14379
14393function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
14394{
14395 global $langs;
14396
14397 print '<div class="div-table-responsive-no-min">';
14398 print '<table class="noborder centpercent">';
14399 print '<tr class="liste_titre">';
14400
14401 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
14402
14403 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
14404
14405 if (!empty($link)) {
14406 if (!empty($arguments)) {
14407 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
14408 } else {
14409 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
14410 }
14411 }
14412
14413 if ($number > -1) {
14414 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
14415 } elseif (!empty($link)) {
14416 print '<span class="badge marginleftonlyshort">...</span>';
14417 }
14418
14419 if (!empty($link)) {
14420 print '</a>';
14421 }
14422
14423 print '</th>';
14424
14425 if ($number < 0 && !empty($link)) {
14426 print '<th class="right">';
14427 print '</th>';
14428 }
14429
14430 print '</tr>';
14431}
14432
14441function finishSimpleTable($addLineBreak = false)
14442{
14443 print '</table>';
14444 print '</div>';
14445
14446 if ($addLineBreak) {
14447 print '<br>';
14448 }
14449}
14450
14462function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
14463{
14464 global $langs;
14465
14466 if ($num === 0) {
14467 print '<tr class="oddeven">';
14468 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
14469 print '</tr>';
14470 return;
14471 }
14472
14473 if ($nbofloop === 0) {
14474 // don't show a summary line
14475 return;
14476 }
14477
14478 /* Case already handled above, commented to satisfy phpstan.
14479 if ($num === 0) {
14480 $colspan = $tableColumnCount;
14481 } else
14482 */
14483 if ($num > $nbofloop) {
14484 $colspan = $tableColumnCount;
14485 } else {
14486 $colspan = $tableColumnCount - 1;
14487 }
14488
14489 if ($extraRightColumn) {
14490 $colspan--;
14491 }
14492
14493 print '<tr class="liste_total">';
14494
14495 if ($nbofloop > 0 && $num > $nbofloop) {
14496 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
14497 } else {
14498 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
14499 print '<td class="right centpercent">'.price($total).'</td>';
14500 }
14501
14502 if ($extraRightColumn) {
14503 print '<td></td>';
14504 }
14505
14506 print '</tr>';
14507}
14508
14517function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
14518{
14519 if ($method == -1) {
14520 $method = 0;
14521 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
14522 $method = 1;
14523 }
14524 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
14525 $method = 2;
14526 }
14527 }
14528
14529 // Be sure we don't have output buffering enabled to have readfile working correctly
14530 while (ob_get_level()) {
14531 ob_end_flush();
14532 }
14533
14534 // Solution 0
14535 if ($method == 0) {
14536 readfile($fullpath_original_file_osencoded);
14537 } elseif ($method == 1) {
14538 // Solution 1
14539 $handle = fopen($fullpath_original_file_osencoded, "rb");
14540 while (!feof($handle)) {
14541 print fread($handle, 8192);
14542 }
14543 fclose($handle);
14544 } elseif ($method == 2) {
14545 // Solution 2
14546 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
14547 $handle2 = fopen("php://output", "wb");
14548 stream_copy_to_stream($handle1, $handle2);
14549 fclose($handle1);
14550 fclose($handle2);
14551 }
14552}
14553
14563function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
14564{
14565 global $langs;
14566
14567 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
14568
14569 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
14570 if ($texttoshow === 'none') {
14571 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14572 $result .= '<span class="clipboardCPValueToPrint"></span>';
14573 } elseif ($texttoshow) {
14574 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14575 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
14576 } else {
14577 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14578 }
14579 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
14580 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
14581 $result .= '<span class="clipboardCPText"></span>';
14582 $result .= '</span>';
14583
14584 return $result;
14585}
14586
14587
14594function jsonOrUnserialize($stringtodecode)
14595{
14596 $result = json_decode($stringtodecode);
14597 if ($result === null) {
14598 $result = unserialize($stringtodecode);
14599 }
14600
14601 return $result;
14602}
14603
14604
14621function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
14622{
14623 global $db, $user;
14624
14625 if (is_null($filter) || !is_string($filter) || $filter === '') {
14626 return '';
14627 }
14628 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
14629 $filter = '(' . $filter . ')';
14630 }
14631
14632 $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'
14633 $firstandlastparenthesis = 0;
14634
14635 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
14636 if ($noerror) {
14637 return '1 = 2';
14638 } else {
14639 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
14640 }
14641 }
14642
14643 // Test the filter syntax
14644 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
14645 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
14646 // If the string result contains something else than '()', the syntax was wrong
14647
14648 if (preg_match('/[^\‍(\‍)]/', $t)) {
14649 $tmperrorstr = 'Bad syntax of the search string';
14650 $errorstr = 'Bad syntax of the search string: '.$filter;
14651 if ($noerror) {
14652 return '1 = 2';
14653 } else {
14654 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
14655 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
14656 }
14657 }
14658
14659 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
14660
14661 if (is_object($db)) {
14662 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
14663 }
14664 if (is_object($user)) {
14665 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
14666 }
14667
14668 return $ret;
14669}
14670
14678function dolForgeExplodeAnd($sqlfilters)
14679{
14680 $arrayofandtags = array();
14681 $nbofchars = dol_strlen($sqlfilters);
14682
14683 $error = '';
14684 $parenthesislevel = 0;
14685 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
14686 if (!$result) {
14687 return array();
14688 }
14689 if ($parenthesislevel >= 1) {
14690 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
14691 }
14692
14693 $i = 0;
14694 $s = '';
14695 $countparenthesis = 0;
14696 while ($i < $nbofchars) {
14697 $char = dol_substr($sqlfilters, $i, 1);
14698
14699 if ($char == '(') {
14700 $countparenthesis++;
14701 } elseif ($char == ')') {
14702 $countparenthesis--;
14703 }
14704
14705 if ($countparenthesis == 0) {
14706 $char2 = dol_substr($sqlfilters, $i + 1, 1);
14707 $char3 = dol_substr($sqlfilters, $i + 2, 1);
14708 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
14709 // We found a AND
14710 $s = trim($s);
14711 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14712 $s = '('.$s.')';
14713 }
14714 $arrayofandtags[] = $s;
14715 $s = '';
14716 $i += 2;
14717 } else {
14718 $s .= $char;
14719 }
14720 } else {
14721 $s .= $char;
14722 }
14723 $i++;
14724 }
14725 if ($s) {
14726 $s = trim($s);
14727 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14728 $s = '('.$s.')';
14729 }
14730 $arrayofandtags[] = $s;
14731 }
14732
14733 return $arrayofandtags;
14734}
14735
14745function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
14746{
14747 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
14748 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
14749 $tmp = $sqlfilters;
14750
14751 $nb = dol_strlen($tmp);
14752 $counter = 0;
14753 $parenthesislevel = 0;
14754
14755 $error = '';
14756
14757 $i = 0;
14758 while ($i < $nb) {
14759 $char = dol_substr($tmp, $i, 1);
14760
14761 if ($char == '(') {
14762 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
14763 // We open a parenthesis and it is the first char
14764 $parenthesislevel++;
14765 }
14766 $counter++;
14767 } elseif ($char == ')') {
14768 $nbcharremaining = ($nb - $i - 1);
14769 if ($nbcharremaining >= $counter) {
14770 $parenthesislevel = min($parenthesislevel, $counter - 1);
14771 }
14772 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
14773 $parenthesislevel = $counter;
14774 }
14775 $counter--;
14776 }
14777
14778 if ($counter < 0) {
14779 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14780 $parenthesislevel = 0;
14781 dol_syslog($error, LOG_WARNING);
14782 return false;
14783 }
14784
14785 $i++;
14786 }
14787
14788 if ($counter > 0) {
14789 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14790 $parenthesislevel = 0;
14791 dol_syslog($error, LOG_WARNING);
14792 return false;
14793 }
14794
14795 return true;
14796}
14797
14805function dolForgeDummyCriteriaCallback($matches)
14806{
14807 //dol_syslog("Convert matches ".$matches[1]);
14808 if (empty($matches[1])) {
14809 return '';
14810 }
14811 $tmp = explode(':', $matches[1]);
14812 if (count($tmp) < 3) {
14813 return '';
14814 }
14815
14816 return '()'; // An empty criteria
14817}
14818
14827function dolForgeSQLCriteriaCallback($matches)
14828{
14829 global $db;
14830
14831 //dol_syslog("Convert matches ".$matches[1]);
14832 if (empty($matches[1])) {
14833 return '';
14834 }
14835 $tmp = explode(':', $matches[1], 3);
14836 if (count($tmp) < 3) {
14837 return '';
14838 }
14839
14840 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
14841
14842 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
14843
14844 $realOperator = [
14845 'NOTLIKE' => 'NOT LIKE',
14846 'ISNOT' => 'IS NOT',
14847 'NOTIN' => 'NOT IN',
14848 '!=' => '<>',
14849 ];
14850
14851 if (array_key_exists($operator, $realOperator)) {
14852 $operator = $realOperator[$operator];
14853 }
14854
14855 $tmpescaped = $tmp[2];
14856
14857 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
14858
14859 $regbis = array();
14860
14861 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)
14862 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
14863 $tmpescaped2 = '(';
14864 // Explode and sanitize each element in list
14865 $tmpelemarray = explode(',', $tmpescaped);
14866 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
14867 $reg = array();
14868 $tmpelem = trim($tmpelem);
14869 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
14870 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
14871 } elseif (ctype_digit((string) $tmpelem)) { // if only 0-9 chars, no .
14872 $tmpelemarray[$tmpkey] = (int) $tmpelem;
14873 } elseif (is_numeric((string) $tmpelem)) { // it can be a float with a .
14874 $tmpelemarray[$tmpkey] = (float) $tmpelem;
14875 } elseif (!getDolGlobalString("MAIN_DISALLOW_UNSECURED_SELECT_INTO_EXTRAFIELDS_FILTER")) {
14876 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_<>=!\s]/i', '', $tmpelem); // it can be a full subrequest
14877 } else {
14878 $tmpelemarray[$tmpkey] = preg_replace('/[^a-z0-9_]/i', '', $tmpelem); // it can be a name of field or a substitution variable like '__NOW__'
14879 }
14880 }
14881 $tmpescaped2 .= implode(',', $tmpelemarray);
14882 $tmpescaped2 .= ')';
14883
14884 $tmpescaped = $tmpescaped2;
14885 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14886 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14887 $tmpescaped = $regbis[1];
14888 }
14889 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14890 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14891 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14892 // TODO Retrieve type of field for $operand field name.
14893 // So we can complete format. For example we could complete a year with month and day.
14894 $tmpescaped = "'".$db->escape($regbis[1])."'";
14895 } else {
14896 if (strtoupper($tmpescaped) == 'NULL') {
14897 $tmpescaped = 'NULL';
14898 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
14899 $tmpescaped = (int) $tmpescaped;
14900 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
14901 $tmpescaped = (float) $tmpescaped;
14902 } else {
14903 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
14904 }
14905 }
14906
14907 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14908}
14909
14910
14920function getTimelineIcon($actionstatic, &$histo, $key)
14921{
14922 global $langs;
14923
14924 $out = '<!-- timeline icon -->'."\n";
14925 $iconClass = 'fa fa-comments';
14926 $img_picto = '';
14927 $colorClass = '';
14928 $pictoTitle = '';
14929
14930 if ($histo[$key]['percent'] == -1) {
14931 $colorClass = 'timeline-icon-not-applicble';
14932 $pictoTitle = $langs->trans('StatusNotApplicable');
14933 } elseif ($histo[$key]['percent'] == 0) {
14934 $colorClass = 'timeline-icon-todo';
14935 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14936 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14937 $colorClass = 'timeline-icon-in-progress';
14938 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14939 } elseif ($histo[$key]['percent'] >= 100) {
14940 $colorClass = 'timeline-icon-done';
14941 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14942 }
14943
14944 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14945 $iconClass = 'fa fa-ticket';
14946 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14947 $iconClass = 'fa fa-pencilxxx';
14948 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14949 $iconClass = 'fa fa-comments';
14950 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14951 $iconClass = 'fa fa-mask';
14952 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14953 if ($actionstatic->type_picto) {
14954 $img_picto = img_picto('', $actionstatic->type_picto);
14955 } else {
14956 if ($actionstatic->type_code == 'AC_RDV') {
14957 $iconClass = 'fa fa-handshake';
14958 } elseif ($actionstatic->type_code == 'AC_TEL') {
14959 $iconClass = 'fa fa-phone';
14960 } elseif ($actionstatic->type_code == 'AC_FAX') {
14961 $iconClass = 'fa fa-fax';
14962 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14963 $iconClass = 'fa fa-envelope';
14964 } elseif ($actionstatic->type_code == 'AC_INT') {
14965 $iconClass = 'fa fa-shipping-fast';
14966 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14967 $iconClass = 'fa fa-robot';
14968 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14969 $iconClass = 'fa fa-robot';
14970 }
14971 }
14972 }
14973
14974 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14975 return $out;
14976}
14977
14985{
14986 global $conf, $db;
14987
14988 $documents = array();
14989
14990 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename, ecm.agenda_id';
14991 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14992 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14993 //$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
14994 $sql.= ' OR ecm.agenda_id = '.(int) $object->id;
14995 $sql .= ' ORDER BY ecm.position ASC';
14996
14997 $resql = $db->query($sql);
14998 if ($resql) {
14999 if ($db->num_rows($resql)) {
15000 while ($obj = $db->fetch_object($resql)) {
15001 $documents[$obj->id] = $obj;
15002 }
15003 }
15004 }
15005
15006 return $documents;
15007}
15008
15009
15027function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
15028{
15029 global $user, $conf;
15030 global $form;
15031
15032 global $param, $massactionbutton;
15033
15034 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
15035
15036 // Check parameters
15037 if (!is_object($filterobj) && !is_object($objcon)) {
15038 dol_print_error(null, 'BadParameter');
15039 }
15040
15041 $histo = array();
15042 '@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';
15043
15044 $numaction = 0;
15045 $now = dol_now();
15046
15047 $sortfield_list = explode(',', $sortfield);
15048 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
15049 $sortfield_new_list = array();
15050 foreach ($sortfield_list as $sortfield_value) {
15051 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
15052 }
15053 $sortfield_new = implode(',', $sortfield_new_list);
15054
15055 $sql = null;
15056 $sql2 = null;
15057
15058 if (isModEnabled('agenda')) {
15059 // Search histo on actioncomm
15060 if (is_object($objcon) && $objcon->id > 0) {
15061 $sql = "SELECT DISTINCT a.id, a.label as label,";
15062 } else {
15063 $sql = "SELECT a.id, a.label as label,";
15064 }
15065 $sql .= " a.datep as dp,";
15066 $sql .= " a.note as message,";
15067 $sql .= " a.datep2 as dp2,";
15068 $sql .= " a.percent as percent, 'action' as type,";
15069 $sql .= " a.fk_element, a.elementtype,";
15070 $sql .= " a.fk_contact,";
15071 $sql .= " a.email_from as msg_from,";
15072 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
15073 $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";
15074 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15075 $sql .= ", sp.lastname, sp.firstname";
15076 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15077 $sql .= ", m.lastname, m.firstname";
15078 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
15079 $sql .= ", o.ref";
15080 }
15081 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
15082 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
15083 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
15084
15085 $force_filter_contact = $filterobj instanceof User;
15086
15087 if (is_object($objcon) && $objcon->id > 0) {
15088 $force_filter_contact = true;
15089 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
15090 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
15091 }
15092
15093 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
15094 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
15095 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
15096 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
15097 $sql .= " ON er.resource_type = 'dolresource'";
15098 $sql .= " AND er.element_id = a.id";
15099 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
15100 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15101 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
15102 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15103 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
15104 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15105 $sql .= ", ".MAIN_DB_PREFIX."product as o";
15106 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15107 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
15108 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15109 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
15110 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15111 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
15112 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15113 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
15114 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15115 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
15116 }
15117
15118 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
15119 if (!$force_filter_contact) {
15120 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
15121 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
15122 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
15123 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
15124 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15125 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
15126 if ($filterobj->id) {
15127 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15128 }
15129 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
15130 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
15131 if ($filterobj->id) {
15132 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15133 }
15134 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15135 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
15136 if ($filterobj->id) {
15137 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15138 }
15139 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15140 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
15141 if ($filterobj->id) {
15142 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15143 }
15144 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15145 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
15146 if ($filterobj->id) {
15147 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15148 }
15149 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
15150 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
15151 if ($filterobj->id) {
15152 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15153 }
15154 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
15155 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
15156 if ($filterobj->id) {
15157 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
15158 }
15159 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
15160 $sql .= " AND a.fk_contact = sp.rowid";
15161 if ($filterobj->id) {
15162 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
15163 }
15164 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
15165 $sql .= " AND a.fk_element = o.rowid";
15166 if ($filterobj->id) {
15167 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
15168 }
15169 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
15170 $sql .= " AND a.fk_element = o.rowid";
15171 if ($filterobj->id) {
15172 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
15173 }
15174 }
15175 } else {
15176 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
15177 }
15178
15179 // Condition on actioncode
15180 if (!empty($actioncode) && $actioncode != '-1') {
15181 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
15182 if ($actioncode == 'AC_NON_AUTO') {
15183 $sql .= " AND c.type != 'systemauto'";
15184 } elseif ($actioncode == 'AC_ALL_AUTO') {
15185 $sql .= " AND c.type = 'systemauto'";
15186 } else {
15187 if ($actioncode == 'AC_OTH') {
15188 $sql .= " AND c.type != 'systemauto'";
15189 } elseif ($actioncode == 'AC_OTH_AUTO') {
15190 $sql .= " AND c.type = 'systemauto'";
15191 }
15192 }
15193 } else {
15194 if ($actioncode == 'AC_NON_AUTO') {
15195 $sql .= " AND c.type != 'systemauto'";
15196 } elseif ($actioncode == 'AC_ALL_AUTO') {
15197 $sql .= " AND c.type = 'systemauto'";
15198 } else {
15199 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
15200 }
15201 }
15202 }
15203 if ($donetodo == 'todo') {
15204 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15205 } elseif ($donetodo == 'done') {
15206 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15207 }
15208 if (is_array($filters) && $filters['search_agenda_label']) {
15209 $sql .= natural_search('a.label', $filters['search_agenda_label']);
15210 }
15211 }
15212
15213 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
15214 if (isModEnabled('mailing') && !empty($objcon->email)
15215 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
15216 $langs->load("mails");
15217
15218 $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";
15219 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
15220 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
15221 $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
15222 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
15223 $sql2 .= ", '' as lastname, '' as firstname";
15224 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
15225 $sql2 .= ", '' as lastname, '' as firstname";
15226 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
15227 $sql2 .= ", '' as ref";
15228 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
15229 $sql2 .= ", '' as ref";
15230 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
15231 $sql2 .= ", '' as ref";
15232 }
15233 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
15234 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
15235 $sql2 .= " AND mc.statut = 1";
15236 $sql2 .= " AND u.rowid = m.fk_user_valid";
15237 $sql2 .= " AND mc.fk_mailing=m.rowid";
15238 }
15239
15240 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
15241 if (!empty($sql) && !empty($sql2)) {
15242 $sql = $sql." UNION ".$sql2;
15243 } elseif (empty($sql) && !empty($sql2)) {
15244 $sql = $sql2;
15245 }
15246
15247 //TODO Add navigation with this limits...
15248 $offset = 0;
15249 $limit = 1000;
15250
15251 // Complete request and execute it with limit
15252 $sql .= $db->order($sortfield_new, $sortorder);
15253 if ($limit) {
15254 $sql .= $db->plimit($limit + 1, $offset);
15255 }
15256
15257 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
15258 $resql = $db->query($sql);
15259 if ($resql) {
15260 $i = 0;
15261 $num = $db->num_rows($resql);
15262
15263 $imaxinloop = ($limit ? min($num, $limit) : $num);
15264 while ($i < $imaxinloop) {
15265 $obj = $db->fetch_object($resql);
15266
15267 if ($obj->type == 'action') {
15268 $contactaction = new ActionComm($db);
15269 $contactaction->id = $obj->id;
15270 $result = $contactaction->fetchResources();
15271 if ($result < 0) {
15272 dol_print_error($db);
15273 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
15274 }
15275
15276 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
15277 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
15278 $tododone = '';
15279 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
15280 $tododone = 'todo';
15281 }
15282
15283 $histo[$numaction] = array(
15284 'type' => $obj->type,
15285 'tododone' => $tododone,
15286 'id' => $obj->id,
15287 'datestart' => $db->jdate($obj->dp),
15288 'dateend' => $db->jdate($obj->dp2),
15289 'note' => $obj->label,
15290 'message' => dol_htmlentitiesbr($obj->message),
15291 'percent' => $obj->percent,
15292
15293 'userid' => $obj->user_id,
15294 'login' => $obj->user_login,
15295 'userfirstname' => $obj->user_firstname,
15296 'userlastname' => $obj->user_lastname,
15297 'userphoto' => $obj->user_photo,
15298 'msg_from' => $obj->msg_from,
15299
15300 'contact_id' => $obj->fk_contact,
15301 'socpeopleassigned' => $contactaction->socpeopleassigned,
15302 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
15303 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
15304 'fk_element' => $obj->fk_element,
15305 'elementtype' => $obj->elementtype,
15306 // Type of event
15307 'acode' => $obj->acode,
15308 'alabel' => $obj->alabel,
15309 'libelle' => $obj->alabel, // deprecated
15310 'apicto' => $obj->apicto
15311 );
15312 } else {
15313 $histo[$numaction] = array(
15314 'type' => $obj->type,
15315 'tododone' => 'done',
15316 'id' => $obj->id,
15317 'datestart' => $db->jdate($obj->dp),
15318 'dateend' => $db->jdate($obj->dp2),
15319 'note' => $obj->label,
15320 'message' => dol_htmlentitiesbr($obj->message),
15321 'percent' => $obj->percent,
15322 'acode' => $obj->acode,
15323
15324 'userid' => $obj->user_id,
15325 'login' => $obj->user_login,
15326 'userfirstname' => $obj->user_firstname,
15327 'userlastname' => $obj->user_lastname,
15328 'userphoto' => $obj->user_photo
15329 );
15330 }
15331
15332 $numaction++;
15333 $i++;
15334 }
15335 } else {
15336 dol_print_error($db);
15337 }
15338 }
15339
15340 // Set $out to show events
15341 $out = '';
15342
15343 if (!isModEnabled('agenda')) {
15344 $langs->loadLangs(array("admin", "errors"));
15345 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
15346 }
15347
15348 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
15349 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
15350
15351 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
15352 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
15353 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
15354 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
15355
15356 $formactions = new FormActions($db);
15357
15358 $actionstatic = new ActionComm($db);
15359 $userstatic = new User($db);
15360 $contactstatic = new Contact($db);
15361 $userGetNomUrlCache = array();
15362 $contactGetNomUrlCache = array();
15363
15364 $out .= '<div class="filters-container" >';
15365 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
15366 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
15367
15368 if ($objcon && get_class($objcon) == 'Contact' &&
15369 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
15370 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
15371 } else {
15372 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
15373 }
15374 if (($filterobj && get_class($filterobj) == 'Societe')) {
15375 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
15376 } else {
15377 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
15378 }
15379
15380 $out .= "\n";
15381
15382 $out .= '<div class="div-table-responsive-no-min">';
15383 $out .= '<table class="noborder borderbottom centpercent">';
15384
15385 $out .= '<tr class="liste_titre">';
15386
15387 // Action column
15388 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15389 $out .= '<th class="liste_titre width50 middle">';
15390 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15391 $out .= $searchpicto;
15392 $out .= '</th>';
15393 }
15394
15395 // Date
15396 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
15397
15398 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
15399 if ($donetodo) {
15400 $out .= '<th class="liste_titre"></th>';
15401 }
15402 // Type of event
15403 $out .= '<th class="liste_titre">';
15404 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
15405 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
15406 $out .= '</th>';
15407 // Label
15408 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
15409 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
15410 $out .= '</th>';
15411
15412 // Action column
15413 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
15414 $out .= '<th class="liste_titre width50 middle">';
15415 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
15416 $out .= $searchpicto;
15417 $out .= '</th>';
15418 }
15419
15420 $out .= '</tr>';
15421
15422 $out .= '</table>';
15423
15424 $out .= '</form>';
15425 $out .= '</div>';
15426
15427 $out .= "\n";
15428
15429 $out .= '<ul class="timeline">';
15430
15431 if ($donetodo) {
15432 $tmp = '';
15433 if ($filterobj instanceof Societe) {
15434 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15435 }
15436 if ($filterobj instanceof User) {
15437 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
15438 }
15439 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
15440 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
15441 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
15442 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
15443 if ($filterobj instanceof Societe) {
15444 $tmp .= '</a>';
15445 }
15446 if ($filterobj instanceof User) {
15447 $tmp .= '</a>';
15448 }
15449 $out .= getTitleFieldOfList($tmp);
15450 }
15451
15452 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
15453 $caction = new CActionComm($db);
15454 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
15455
15456 $actualCycleDate = false;
15457
15458 // Loop on each event to show it
15459 foreach ($histo as $key => $value) {
15460 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
15461
15462 $actionstatic->type_picto = $histo[$key]['apicto'];
15463 $actionstatic->type_code = $histo[$key]['acode'];
15464
15465 $labeltype = $actionstatic->type_code;
15466 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
15467 $labeltype = 'AC_OTH';
15468 }
15469 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15470 $labeltype = $langs->trans("Message");
15471 } else {
15472 if (!empty($arraylist[$labeltype])) {
15473 $labeltype = $arraylist[$labeltype];
15474 }
15475 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
15476 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
15477 }
15478 }
15479
15480 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
15481
15482 $tmpa = dol_getdate($histo[$key]['datestart'], false);
15483
15484 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
15485 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
15486 $out .= '<!-- timeline time label -->';
15487 $out .= '<li class="time-label">';
15488 $out .= '<span class="timeline-badge-date">';
15489 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
15490 $out .= '</span>';
15491 $out .= '</li>';
15492 $out .= '<!-- /.timeline-label -->';
15493 }
15494
15495
15496 $out .= '<!-- timeline item -->'."\n";
15497 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
15498
15499 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
15500 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
15501 //$out .= $timelineicon;
15502 //var_dump($timelineicon);
15503 $out .= $typeicon;
15504
15505 $out .= '<div class="timeline-item">'."\n";
15506
15507 $out .= '<span class="time timeline-header-action2">';
15508
15509 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
15510 $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").' ';
15511 $out .= $histo[$key]['id'];
15512 $out .= '</a> ';
15513 } else {
15514 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
15515 }
15516
15517 if ($user->hasRight('agenda', 'allactions', 'create') ||
15518 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
15519 $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).'">';
15520 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
15521 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
15522 $out .= '</a>';
15523 }
15524
15525 $out .= '</span>';
15526
15527 // Date
15528 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
15529 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
15530 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
15531 $tmpa = dol_getdate($histo[$key]['datestart'], true);
15532 $tmpb = dol_getdate($histo[$key]['dateend'], true);
15533 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
15534 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
15535 } else {
15536 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
15537 }
15538 }
15539 $late = 0;
15540 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15541 $late = 1;
15542 }
15543 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15544 $late = 1;
15545 }
15546 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
15547 $late = 1;
15548 }
15549 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
15550 $late = 1;
15551 }
15552 if ($late) {
15553 $out .= img_warning($langs->trans("Late")).' ';
15554 }
15555 $out .= "</span></span>\n";
15556
15557 $out .= '<span class="time">';
15558 $out .= $actionstatic->getLibStatut(2);
15559 $out .= '</span>';
15560
15561 // Ref
15562 $out .= '<h3 class="timeline-header">';
15563
15564 // Author of event
15565 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
15566 if ($histo[$key]['userid'] > 0) {
15567 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
15568 $userstatic->fetch($histo[$key]['userid']);
15569 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
15570 }
15571 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
15572 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
15573 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
15574 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
15575 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
15576 } else {
15577 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
15578 }
15579 }
15580 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
15581 }
15582 $out .= '</div>';
15583
15584 // Title
15585 $out .= ' <div class="messaging-title inline-block">';
15586 //$out .= $actionstatic->getTypePicto(); // The type of event is already into the timeline on left.
15587 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
15588 $out .= $labeltype.' - ';
15589 }
15590
15591 $libelle = '';
15592
15593 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
15594 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
15595 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15596 $out .= $langs->trans('TicketNewMessage');
15597 } elseif (isset($histo[$key]['type'])) {
15598 if ($histo[$key]['type'] == 'action') {
15599 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15600 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
15601 $libelle = $histo[$key]['note'];
15602 $actionstatic->id = $histo[$key]['id'];
15603 if ($libelle != $labeltype) {
15604 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15605 }
15606 } elseif ($histo[$key]['type'] == 'mailing') {
15607 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
15608 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15609 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
15610 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15611 } else {
15612 $libelle .= $histo[$key]['note'];
15613 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15614 }
15615 }
15616 $out = preg_replace('/ - $/', '', $out); // Remove ending ' - '
15617
15618 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
15619 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
15620 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
15621 } else {
15622 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
15623 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
15624 }
15625 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
15626 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
15627 }
15628 if ($link) {
15629 $out .= ' - '.$link;
15630 }
15631 }
15632
15633 $out .= '</div>';
15634
15635 $out .= '</h3>';
15636
15637 // Message
15638 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
15639 && $actionstatic->code != 'AC_TICKET_CREATE'
15640 && $actionstatic->code != 'AC_TICKET_MODIFY'
15641 ) {
15642 $out .= '<div class="timeline-body wordbreak small">';
15643 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
15644 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
15645 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
15646 $out .= '<div class="readmore-block --closed" >';
15647 $out .= ' <div class="readmore-block__excerpt">';
15648 $out .= dolPrintHTML($truncatedText);
15649 $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>';
15650 $out .= ' </div>';
15651 $out .= ' <div class="readmore-block__full-text" >';
15652 $out .= dolPrintHTML($histo[$key]['message']);
15653 $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>';
15654 $out .= ' </div>';
15655 $out .= '</div>';
15656 } else {
15657 $out .= dolPrintHTML($histo[$key]['message']);
15658 }
15659
15660 $out .= '</div>';
15661 }
15662
15663 // Timeline footer
15664 $footer = '';
15665
15666 // Contact for this action
15667 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
15668 $contactList = '';
15669 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
15670 if (empty($conf->cache['contact'][$cid])) {
15671 $contact = new Contact($db);
15672 $contact->fetch($cid);
15673 $conf->cache['contact'][$cid] = $contact;
15674 } else {
15675 $contact = $conf->cache['contact'][$cid];
15676 }
15677
15678 if ($contact) {
15679 $contactList .= !empty($contactList) ? ', ' : '';
15680 $contactList .= $contact->getNomUrl(1);
15681 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15682 if (!empty($contact->phone_pro)) {
15683 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
15684 }
15685 }
15686 }
15687 }
15688
15689 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
15690 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
15691 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
15692 $contact = new Contact($db);
15693 $result = $contact->fetch($histo[$key]['contact_id']);
15694 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
15695 } else {
15696 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
15697 $result = ($contact instanceof Contact) ? $contact->id : 0;
15698 }
15699
15700 if ($result > 0) {
15701 $footer .= $contact->getNomUrl(1);
15702 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15703 if (!empty($contact->phone_pro)) {
15704 $footer .= '('.dol_print_phone($contact->phone_pro).')';
15705 }
15706 }
15707 }
15708 }
15709
15710 $documents = getActionCommEcmList($actionstatic);
15711 if (!empty($documents)) {
15712 $footer .= '<div class="timeline-documents-container">';
15713 foreach ($documents as $doc) {
15714 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
15715 $footer .= ' data-id="'.$doc->id.'" ';
15716 $footer .= ' data-path="'.$doc->filepath.'"';
15717 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
15718 $footer .= '>';
15719
15720 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
15721 $mime = dol_mimetype($filePath);
15722 if (empty($doc->agenda_id)) {
15723 $dir_ref = $actionstatic->id;
15724 $modulepart = 'actions';
15725 } else {
15726 $split_dir = explode('/', $doc->filepath);
15727 $modulepart = array_shift($split_dir);
15728 $dir_ref = implode('/', $split_dir);
15729 }
15730
15731 $file = $dir_ref.'/'.$doc->filename;
15732 $thumb = $dir_ref.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
15733 $doclink = dol_buildpath('document.php', 1).'?modulepart='.$modulepart.'&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
15734 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart='.$modulepart.'&file='.urlencode($thumb).'&entity='.$conf->entity;
15735
15736
15737
15738 $mimeAttr = ' mime="'.$mime.'" ';
15739 $class = '';
15740 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
15741 $class .= ' documentpreview';
15742 }
15743
15744 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
15745 $footer .= img_mime($filePath).' '.$doc->filename;
15746 $footer .= '</a>';
15747
15748 $footer .= '</span>';
15749 }
15750 $footer .= '</div>';
15751 }
15752
15753 if (!empty($footer)) {
15754 $out .= '<div class="timeline-footer">'.$footer.'</div>';
15755 }
15756
15757 $out .= '</div>'."\n"; // end timeline-item
15758
15759 $out .= '</li>';
15760 $out .= '<!-- END timeline item -->';
15761 }
15762
15763 $out .= "</ul>\n";
15764
15765 // Code to manage the click on button data-read-more-action to show full description of an event
15766 $out .= '<script>
15767 jQuery(document).ready(function () {
15768 $(document).on("click", "[data-read-more-action]", function(e){
15769 console.log("We click on data-read-more-action");
15770 let readMoreBloc = $(this).closest(".readmore-block");
15771 if(readMoreBloc.length > 0){
15772 e.preventDefault();
15773 if($(this).attr("data-read-more-action") == "close"){
15774 readMoreBloc.addClass("--closed").removeClass("--open");
15775 $("html, body").animate({
15776 scrollTop: readMoreBloc.offset().top - 200
15777 }, 100);
15778 }else{
15779 readMoreBloc.addClass("--open").removeClass("--closed");
15780 }
15781 }
15782 });
15783 });
15784 </script>';
15785
15786
15787 if (empty($histo)) {
15788 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
15789 }
15790 }
15791
15792 if ($noprint) {
15793 return $out;
15794 } else {
15795 print $out;
15796 return null;
15797 }
15798}
15799
15811function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
15812{
15813 if ($timestamp === null) {
15814 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
15815 }
15816 $TParam = array(
15817 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
15818 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
15819 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
15820 );
15821 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
15822 $TParam = array_merge($TParam, array(
15823 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
15824 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
15825 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
15826 ));
15827 }
15828
15829 return '&' . http_build_query($TParam);
15830}
15831
15850function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
15851{
15852 global $conf, $db, $langs, $hookmanager;
15853 global $action, $object;
15854
15855 if (!is_object($langs)) {
15856 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
15857 $langs = new Translate('', $conf);
15858 $langs->setDefaultLang();
15859 }
15860
15861 $langs->load("errors");
15862
15863 if ($printheader) {
15864 if (function_exists("llxHeader")) {
15865 llxHeader('');
15866 } elseif (function_exists("llxHeaderVierge")) {
15867 llxHeaderVierge('');
15868 }
15869 }
15870
15871 print '<div class="error">';
15872 if (empty($message)) {
15873 print $langs->trans("ErrorRecordNotFound");
15874 } else {
15875 print $langs->trans($message);
15876 }
15877 print '</div>';
15878 print '<br>';
15879
15880 if (empty($showonlymessage)) {
15881 if (empty($hookmanager)) {
15882 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
15883 $hookmanager = new HookManager($db);
15884 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
15885 $hookmanager->initHooks(array('main'));
15886 }
15887
15888 $parameters = array('message' => $message, 'params' => $params);
15889 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
15890 print $hookmanager->resPrint;
15891 }
15892
15893 if ($printfooter && function_exists("llxFooter")) {
15894 llxFooter();
15895 if (is_object($db)) {
15896 $db->close();
15897 }
15898 }
15899 exit(0);
15900}
$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