dolibarr 21.0.0-beta
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-2024 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-2024 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 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
24 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.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 *
29 * This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 3 of the License, or
32 * (at your option) any later version.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program. If not, see <https://www.gnu.org/licenses/>.
41 * or see https://www.gnu.org/
42 */
43
50//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
51
52// Function for better PHP x compatibility
53if (!function_exists('utf8_encode')) {
61 function utf8_encode($elements)
62 {
63 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
64 }
65}
66
67if (!function_exists('utf8_decode')) {
75 function utf8_decode($elements)
76 {
77 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
78 }
79}
80if (!function_exists('str_starts_with')) {
89 function str_starts_with($haystack, $needle)
90 {
91 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
92 }
93}
94if (!function_exists('str_ends_with')) {
103 function str_ends_with($haystack, $needle)
104 {
105 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
106 }
107}
108if (!function_exists('str_contains')) {
117 function str_contains($haystack, $needle)
118 {
119 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
120 }
121}
122
123
135function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
136{
137 global $conf;
138
139 if (!is_object($object) && empty($module)) {
140 return null;
141 }
142 if (empty($module) && !empty($object->element)) {
143 $module = $object->element;
144 }
145
146 // Special case for backward compatibility
147 if ($module == 'fichinter') {
148 $module = 'ficheinter';
149 } elseif ($module == 'invoice_supplier') {
150 $module = 'supplier_invoice';
151 } elseif ($module == 'order_supplier') {
152 $module = 'supplier_order';
153 }
154
155 // Get the relative path of directory
156 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
157 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
158 $s = '';
159 if ($mode != 'outputrel') {
160 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
161 }
162 if ($forobject && $object->id > 0) {
163 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
164 }
165 return $s;
166 } else {
167 return 'error-diroutput-not-defined-for-this-object='.$module;
168 }
169 } elseif ($mode == 'temp') {
170 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
171 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
172 } else {
173 return 'error-dirtemp-not-defined-for-this-object='.$module;
174 }
175 } else {
176 return 'error-bad-value-for-mode';
177 }
178}
179
189function getMultidirTemp($object, $module = '', $forobject = 0)
190{
191 return getMultidirOutput($object, $module, $forobject, 'temp');
192}
193
203function getMultidirVersion($object, $module = '', $forobject = 0)
204{
205 return getMultidirOutput($object, $module, $forobject, 'version');
206}
207
208
217function getDolGlobalString($key, $default = '')
218{
219 global $conf;
220 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
221}
222
232function getDolGlobalInt($key, $default = 0)
233{
234 global $conf;
235 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
236}
237
247function getDolGlobalFloat($key, $default = 0)
248{
249 global $conf;
250 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
251}
252
261function getDolGlobalBool($key, $default = false)
262{
263 global $conf;
264 return (bool) ($conf->global->$key ?? $default);
265}
266
276function getDolUserString($key, $default = '', $tmpuser = null)
277{
278 if (empty($tmpuser)) {
279 global $user;
280 $tmpuser = $user;
281 }
282
283 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
284}
285
294function getDolUserInt($key, $default = 0, $tmpuser = null)
295{
296 if (empty($tmpuser)) {
297 global $user;
298 $tmpuser = $user;
299 }
300
301 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
302}
303
304
314define(
315 'MODULE_MAPPING',
316 array(
317 // Map deprecated names to new names
318 'adherent' => 'member', // Has new directory
319 'member_type' => 'adherent_type', // No directory, but file called adherent_type
320 'banque' => 'bank', // Has new directory
321 'contrat' => 'contract', // Has new directory
322 'entrepot' => 'stock', // Has new directory
323 'projet' => 'project', // Has new directory
324 'categorie' => 'category', // Has old directory
325 'commande' => 'order', // Has old directory
326 'expedition' => 'shipping', // Has old directory
327 'facture' => 'invoice', // Has old directory
328 'fichinter' => 'intervention', // Has old directory
329 'ficheinter' => 'intervention', // Backup for 'fichinter'
330 'propale' => 'propal', // Has old directory
331 'socpeople' => 'contact', // Has old directory
332 'fournisseur' => 'supplier', // Has old directory
333
334 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
335 'product_price' => 'productprice', // NO directory
336 'product_fournisseur_price' => 'productsupplierprice', // NO directory
337 )
338);
339
346function isModEnabled($module)
347{
348 global $conf;
349
350 // Fix old names (map to new names)
351 $arrayconv = MODULE_MAPPING;
352 $arrayconvbis = array_flip(MODULE_MAPPING);
353
354 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
355 // Special cases: both use the same module.
356 $arrayconv['supplier_order'] = 'fournisseur';
357 $arrayconv['supplier_invoice'] = 'fournisseur';
358 }
359 // Special case.
360 // @TODO Replace isModEnabled('delivery_note') with
361 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
362 if ($module == 'delivery_note') {
363 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
364 return false;
365 } else {
366 $module = 'shipping';
367 }
368 }
369
370 $module_alt = $module;
371 if (!empty($arrayconv[$module])) {
372 $module_alt = $arrayconv[$module];
373 }
374 $module_bis = $module;
375 if (!empty($arrayconvbis[$module])) {
376 $module_bis = $arrayconvbis[$module];
377 }
378
379 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
380 //return !empty($conf->$module->enabled);
381}
382
389function isDolTms($timestamp)
390{
391 if ($timestamp === '') {
392 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
393 return false;
394 }
395 if (is_null($timestamp) || !is_numeric($timestamp)) {
396 return false;
397 }
398
399 return true;
400}
401
413function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
414{
415 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
416
417 $class = 'DoliDB'.ucfirst($type);
418 $db = new $class($type, $host, $user, $pass, $name, $port);
419 return $db;
420}
421
439function getEntity($element, $shared = 1, $currentobject = null)
440{
441 global $conf, $mc, $hookmanager, $object, $action, $db;
442
443 if (!is_object($hookmanager)) {
444 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
445 $hookmanager = new HookManager($db);
446 }
447
448 // fix different element names (France to English)
449 switch ($element) {
450 case 'projet':
451 $element = 'project';
452 break;
453 case 'contrat':
454 $element = 'contract';
455 break; // "/contrat/class/contrat.class.php"
456 case 'order_supplier':
457 $element = 'supplier_order';
458 break; // "/fourn/class/fournisseur.commande.class.php"
459 case 'invoice_supplier':
460 $element = 'supplier_invoice';
461 break; // "/fourn/class/fournisseur.facture.class.php"
462 }
463
464 if (is_object($mc)) {
465 $out = $mc->getEntity($element, $shared, $currentobject);
466 } else {
467 $out = '';
468 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
469 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
470 $addzero[] = 'c_holiday_types';
471 }
472 if (in_array($element, $addzero)) {
473 $out .= '0,';
474 }
475 $out .= ((int) $conf->entity);
476 }
477
478 // Manipulate entities to query on the fly
479 $parameters = array(
480 'element' => $element,
481 'shared' => $shared,
482 'object' => $object,
483 'currentobject' => $currentobject,
484 'out' => $out
485 );
486 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
487
488 if (is_numeric($reshook)) {
489 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
490 $out .= ','.$hookmanager->resPrint; // add
491 } elseif ($reshook == 1) {
492 $out = $hookmanager->resPrint; // replace
493 }
494 }
495
496 return $out;
497}
498
505function setEntity($currentobject)
506{
507 global $conf, $mc;
508
509 if (is_object($mc) && method_exists($mc, 'setEntity')) {
510 return $mc->setEntity($currentobject);
511 } else {
512 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
513 }
514}
515
522function isASecretKey($keyname)
523{
524 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
525}
526
527
534function num2Alpha($n)
535{
536 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
537 $r = chr($n % 26 + 0x41) . $r;
538 }
539 return $r;
540}
541
542
559function getBrowserInfo($user_agent)
560{
561 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
562
563 $name = 'unknown';
564 $version = '';
565 $os = 'unknown';
566 $phone = '';
567
568 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
569
570 $detectmobile = new Mobile_Detect(null, $user_agent);
571 $tablet = $detectmobile->isTablet();
572
573 if ($detectmobile->isMobile()) {
574 $phone = 'unknown';
575
576 // If phone/smartphone, we set phone os name.
577 if ($detectmobile->is('AndroidOS')) {
578 $os = $phone = 'android';
579 } elseif ($detectmobile->is('BlackBerryOS')) {
580 $os = $phone = 'blackberry';
581 } elseif ($detectmobile->is('iOS')) {
582 $os = 'ios';
583 $phone = 'iphone';
584 } elseif ($detectmobile->is('PalmOS')) {
585 $os = $phone = 'palm';
586 } elseif ($detectmobile->is('SymbianOS')) {
587 $os = 'symbian';
588 } elseif ($detectmobile->is('webOS')) {
589 $os = 'webos';
590 } elseif ($detectmobile->is('MaemoOS')) {
591 $os = 'maemo';
592 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
593 $os = 'windows';
594 }
595 }
596
597 // OS
598 if (preg_match('/linux/i', $user_agent)) {
599 $os = 'linux';
600 } elseif (preg_match('/macintosh/i', $user_agent)) {
601 $os = 'macintosh';
602 } elseif (preg_match('/windows/i', $user_agent)) {
603 $os = 'windows';
604 }
605
606 // Name
607 $reg = array();
608 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
609 $name = 'firefox';
610 $version = empty($reg[2]) ? '' : $reg[2];
611 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
612 $name = 'edge';
613 $version = empty($reg[2]) ? '' : $reg[2];
614 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
615 $name = 'chrome';
616 $version = empty($reg[2]) ? '' : $reg[2];
617 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
618 // we can have 'chrome (Mozilla...) chrome x.y' in one string
619 $name = 'chrome';
620 } elseif (preg_match('/iceweasel/i', $user_agent)) {
621 $name = 'iceweasel';
622 } elseif (preg_match('/epiphany/i', $user_agent)) {
623 $name = 'epiphany';
624 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
625 $name = 'safari';
626 $version = empty($reg[2]) ? '' : $reg[2];
627 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
628 // Safari is often present in string for mobile but its not.
629 $name = 'opera';
630 $version = empty($reg[2]) ? '' : $reg[2];
631 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
632 $name = 'ie';
633 $version = end($reg);
634 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
635 // MS products at end
636 $name = 'ie';
637 $version = end($reg);
638 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
639 // MS products at end
640 $name = 'textbrowser';
641 $version = empty($reg[3]) ? '' : $reg[3];
642 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
643 // MS products at end
644 $name = 'textbrowser';
645 $version = empty($reg[1]) ? '' : $reg[1];
646 }
647
648 if ($tablet) {
649 $layout = 'tablet';
650 } elseif ($phone) {
651 $layout = 'phone';
652 } else {
653 $layout = 'classic';
654 }
655
656 return array(
657 'browsername' => $name,
658 'browserversion' => $version,
659 'browseros' => $os,
660 'browserua' => $user_agent,
661 'layout' => $layout, // tablet, phone, classic
662 'phone' => $phone, // deprecated
663 'tablet' => $tablet // deprecated
664 );
665}
666
672function dol_shutdown()
673{
674 global $db;
675 $disconnectdone = false;
676 $depth = 0;
677 if (is_object($db) && !empty($db->connected)) {
678 $depth = $db->transaction_opened;
679 $disconnectdone = $db->close();
680 }
681 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));
682}
683
693function GETPOSTISSET($paramname)
694{
695 $isset = false;
696
697 $relativepathstring = $_SERVER["PHP_SELF"];
698 // Clean $relativepathstring
699 if (constant('DOL_URL_ROOT')) {
700 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
701 }
702 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
703 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
704 //var_dump($relativepathstring);
705 //var_dump($user->default_values);
706
707 // Code for search criteria persistence.
708 // Retrieve values if restore_lastsearch_values
709 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
710 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
711 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
712 if (is_array($tmp)) {
713 foreach ($tmp as $key => $val) {
714 if ($key == $paramname) { // We are on the requested parameter
715 $isset = true;
716 break;
717 }
718 }
719 }
720 }
721 // If there is saved contextpage, limit, page or mode
722 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
723 $isset = true;
724 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
725 $isset = true;
726 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
727 $isset = true;
728 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
729 $isset = true;
730 }
731 } else {
732 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
733 }
734
735 return $isset;
736}
737
746function GETPOSTISARRAY($paramname, $method = 0)
747{
748 // for $method test need return the same $val as GETPOST
749 if (empty($method)) {
750 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
751 } elseif ($method == 1) {
752 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
753 } elseif ($method == 2) {
754 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
755 } elseif ($method == 3) {
756 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
757 } else {
758 $val = 'BadFirstParameterForGETPOST';
759 }
760
761 return is_array($val);
762}
763
794function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
795{
796 global $mysoc, $user, $conf;
797
798 if (empty($paramname)) { // Explicit test for null for phan.
799 return 'BadFirstParameterForGETPOST';
800 }
801 if (empty($check)) {
802 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);
803 // Enable this line to know who call the GETPOST with '' $check parameter.
804 //var_dump(debug_backtrace()[0]);
805 }
806
807 if (empty($method)) {
808 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
809 } elseif ($method == 1) {
810 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
811 } elseif ($method == 2) {
812 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
813 } elseif ($method == 3) {
814 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
815 } else {
816 return 'BadThirdParameterForGETPOST';
817 }
818
819 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
820
821 if (empty($method) || $method == 3 || $method == 4) {
822 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
823 // Clean $relativepathstring
824 if (constant('DOL_URL_ROOT')) {
825 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
826 }
827 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
828 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
829 //var_dump($relativepathstring);
830 //var_dump($user->default_values);
831
832 // Code for search criteria persistence.
833 // Retrieve saved values if restore_lastsearch_values is set
834 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
835 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
836 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
837 if (is_array($tmp)) {
838 foreach ($tmp as $key => $val) {
839 if ($key == $paramname) { // We are on the requested parameter
840 $out = $val;
841 break;
842 }
843 }
844 }
845 }
846 // If there is saved contextpage, page or limit
847 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
848 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
849 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
850 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
851 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
852 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
853 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
854 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
855 }
856 } elseif (!isset($_GET['sortfield'])) {
857 // Else, retrieve default values if we are not doing a sort
858 // 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
859 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
860 // Search default value from $object->field
861 global $object;
862 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
863 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
864 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
865 $out = $object->fields[$paramname]['default'];
866 }
867 }
868 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
869 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
870 // Now search in setup to overwrite default values
871 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
872 if (isset($user->default_values[$relativepathstring]['createform'])) {
873 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
874 $qualified = 0;
875 if ($defkey != '_noquery_') {
876 $tmpqueryarraytohave = explode('&', $defkey);
877 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
878 $foundintru = 0;
879 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
880 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
881 $foundintru = 1;
882 }
883 }
884 if (!$foundintru) {
885 $qualified = 1;
886 }
887 //var_dump($defkey.'-'.$qualified);
888 } else {
889 $qualified = 1;
890 }
891
892 if ($qualified) {
893 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
894 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
895 break;
896 }
897 }
898 }
899 }
900 }
901 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
902 // Management of default search_filters and sort order
903 if (!empty($user->default_values)) {
904 // $user->default_values defined from menu 'Setup - Default values'
905 //var_dump($user->default_values[$relativepathstring]);
906 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
907 // Sorted on which fields ? ASC or DESC ?
908 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
909 // Even if paramname is sortfield, data are stored into ['sortorder...']
910 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
911 $qualified = 0;
912 if ($defkey != '_noquery_') {
913 $tmpqueryarraytohave = explode('&', $defkey);
914 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
915 $foundintru = 0;
916 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
917 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
918 $foundintru = 1;
919 }
920 }
921 if (!$foundintru) {
922 $qualified = 1;
923 }
924 //var_dump($defkey.'-'.$qualified);
925 } else {
926 $qualified = 1;
927 }
928
929 if ($qualified) {
930 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
931 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
932 if ($out) {
933 $out .= ', ';
934 }
935 if ($paramname == 'sortfield') {
936 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
937 }
938 if ($paramname == 'sortorder') {
939 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
940 }
941 }
942 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
943 }
944 }
945 }
946 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
947 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
948 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
949 continue;
950 }
951 $qualified = 0;
952 if ($defkey != '_noquery_') {
953 $tmpqueryarraytohave = explode('&', $defkey);
954 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
955 $foundintru = 0;
956 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
957 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
958 $foundintru = 1;
959 }
960 }
961 if (!$foundintru) {
962 $qualified = 1;
963 }
964 //var_dump($defkey.'-'.$qualified);
965 } else {
966 $qualified = 1;
967 }
968
969 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
970 // We must keep $_POST and $_GET here
971 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
972 // We made a search from quick search menu, do we still use default filter ?
973 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
974 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
975 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
976 }
977 } else {
978 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
979 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
980 }
981 break;
982 }
983 }
984 }
985 }
986 }
987 }
988 }
989 }
990
991 // 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)
992 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
993 // 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.
994 '@phan-var-force string $paramname';
995 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
996 $reg = array();
997 $maxloop = 20;
998 $loopnb = 0; // Protection against infinite loop
999
1000 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.
1001 $loopnb++;
1002 $newout = '';
1003
1004 if ($reg[1] == 'DAY') {
1005 $tmp = dol_getdate(dol_now(), true);
1006 $newout = $tmp['mday'];
1007 } elseif ($reg[1] == 'MONTH') {
1008 $tmp = dol_getdate(dol_now(), true);
1009 $newout = $tmp['mon'];
1010 } elseif ($reg[1] == 'YEAR') {
1011 $tmp = dol_getdate(dol_now(), true);
1012 $newout = $tmp['year'];
1013 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1014 $tmp = dol_getdate(dol_now(), true);
1015 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1016 $newout = $tmp2['day'];
1017 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1018 $tmp = dol_getdate(dol_now(), true);
1019 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1020 $newout = $tmp2['month'];
1021 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1022 $tmp = dol_getdate(dol_now(), true);
1023 $newout = ($tmp['year'] - 1);
1024 } elseif ($reg[1] == 'NEXT_DAY') {
1025 $tmp = dol_getdate(dol_now(), true);
1026 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1027 $newout = $tmp2['day'];
1028 } elseif ($reg[1] == 'NEXT_MONTH') {
1029 $tmp = dol_getdate(dol_now(), true);
1030 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1031 $newout = $tmp2['month'];
1032 } elseif ($reg[1] == 'NEXT_YEAR') {
1033 $tmp = dol_getdate(dol_now(), true);
1034 $newout = ($tmp['year'] + 1);
1035 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1036 $newout = $mysoc->country_id;
1037 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1038 $newout = $user->id;
1039 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1040 $newout = $user->fk_user;
1041 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1042 $newout = $conf->entity;
1043 } elseif ($reg[1] == 'ID') {
1044 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1045 } else {
1046 $newout = ''; // Key not found, we replace with empty string
1047 }
1048 //var_dump('__'.$reg[1].'__ -> '.$newout);
1049 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1050 }
1051 }
1052
1053 // Check type of variable and make sanitization according to this
1054 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1055 if (!is_array($out) || empty($out)) {
1056 $out = array();
1057 } else {
1058 $tmparray = explode(':', $check);
1059 if (!empty($tmparray[1])) {
1060 $tmpcheck = $tmparray[1];
1061 } else {
1062 $tmpcheck = 'alphanohtml';
1063 }
1064 foreach ($out as $outkey => $outval) {
1065 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1066 }
1067 }
1068 } else {
1069 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1070 // 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
1071 if (strpos($paramname, 'search_') === 0) {
1072 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1073 }
1074
1075 // @phan-suppress-next-line UnknownSanitizeType
1076 $out = sanitizeVal($out, $check, $filter, $options);
1077 }
1078
1079 // Sanitizing for special parameters.
1080 // 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.
1081 // @TODO Merge backtopage with backtourl
1082 // @TODO Rename backtolist into backtopagelist
1083 if (preg_match('/^backto/i', $paramname)) {
1084 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1085 $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.
1086 do {
1087 $oldstringtoclean = $out;
1088 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1089 $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'
1090 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1091 } while ($oldstringtoclean != $out);
1092 }
1093
1094 // Code for search criteria persistence.
1095 // Save data into session if key start with 'search_'
1096 if (empty($method) || $method == 3 || $method == 4) {
1097 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1098 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1099
1100 // We save search key only if $out not empty that means:
1101 // - posted value not empty, or
1102 // - 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).
1103
1104 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1105 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1106 }
1107 }
1108 }
1109
1110 return $out;
1111}
1112
1122function GETPOSTINT($paramname, $method = 0)
1123{
1124 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1125}
1126
1127
1136function GETPOSTFLOAT($paramname, $rounding = '')
1137{
1138 // 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.)
1139 return (float) price2num(GETPOST($paramname), $rounding, 2);
1140}
1141
1142
1154function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
1155{
1156 $m = array();
1157 if ($hourTime === 'getpost') {
1158 $hour = GETPOSTINT($prefix . 'hour');
1159 $minute = GETPOSTINT($prefix . 'minute');
1160 $second = GETPOSTINT($prefix . 'second');
1161 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1162 $hour = intval($m[1]);
1163 $minute = intval($m[2]);
1164 $second = intval($m[3]);
1165 } else {
1166 $hour = $minute = $second = 0;
1167 }
1168 // normalize out of range values
1169 $hour = (int) min($hour, 23);
1170 $minute = (int) min($minute, 59);
1171 $second = (int) min($second, 59);
1172
1173 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
1174}
1175
1176
1187function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1188{
1189 return sanitizeVal($out, $check, $filter, $options);
1190}
1191
1201function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1202{
1203 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1204 // Check is done after replacement
1205 switch ($check) {
1206 case 'none':
1207 case 'password':
1208 break;
1209 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1210 if (!is_numeric($out)) {
1211 $out = '';
1212 }
1213 break;
1214 case 'intcomma':
1215 if (is_array($out)) {
1216 $out = implode(',', $out);
1217 }
1218 if (preg_match('/[^0-9,-]+/i', $out)) {
1219 $out = '';
1220 }
1221 break;
1222 case 'san_alpha':
1223 $out = filter_var($out, FILTER_SANITIZE_STRING);
1224 break;
1225 case 'email':
1226 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1227 break;
1228 case 'aZ':
1229 if (!is_array($out)) {
1230 $out = trim($out);
1231 if (preg_match('/[^a-z]+/i', $out)) {
1232 $out = '';
1233 }
1234 }
1235 break;
1236 case 'aZ09':
1237 if (!is_array($out)) {
1238 $out = trim($out);
1239 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1240 $out = '';
1241 }
1242 }
1243 break;
1244 case 'aZ09arobase': // great to sanitize $objecttype parameter
1245 if (!is_array($out)) {
1246 $out = trim($out);
1247 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1248 $out = '';
1249 }
1250 }
1251 break;
1252 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1253 if (!is_array($out)) {
1254 $out = trim($out);
1255 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1256 $out = '';
1257 }
1258 }
1259 break;
1260 case 'alpha': // No html and no ../ and "
1261 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1262 if (!is_array($out)) {
1263 $out = trim($out);
1264 do {
1265 $oldstringtoclean = $out;
1266 // Remove html tags
1267 $out = dol_string_nohtmltag($out, 0);
1268 // 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).
1269 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1270 // Remove also other dangerous string sequences
1271 // '../' or '..\' is dangerous because it allows dir transversals
1272 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1273 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1274 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1275 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1276 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1277 } while ($oldstringtoclean != $out);
1278 // keep lines feed
1279 }
1280 break;
1281 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'
1282 if (!is_array($out)) {
1283 $out = trim($out);
1284 do {
1285 $oldstringtoclean = $out;
1286 // Decode html entities
1287 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1288 // 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).
1289 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1290 // Remove also other dangerous string sequences
1291 // '../' or '..\' is dangerous because it allows dir transversals
1292 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1293 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1294 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1295 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1296 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1297 } while ($oldstringtoclean != $out);
1298 }
1299 break;
1300 case 'nohtml': // No html
1301 $out = dol_string_nohtmltag($out, 0);
1302 break;
1303 case 'restricthtmlnolink':
1304 case 'restricthtml': // Recommended for most html textarea
1305 case 'restricthtmlallowclass':
1306 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1307 case 'restricthtmlallowunvalid':
1308 $out = dol_htmlwithnojs($out, 1, $check);
1309 break;
1310 case 'custom':
1311 if (!empty($out)) {
1312 if (empty($filter)) {
1313 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1314 }
1315 if (is_null($options)) {
1316 $options = 0;
1317 }
1318 $out = filter_var($out, $filter, $options);
1319 }
1320 break;
1321 default:
1322 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1323 $out = GETPOST($out, 'alphanohtml');
1324 break;
1325 }
1326
1327 return $out;
1328}
1329
1330
1331if (!function_exists('dol_getprefix')) {
1342 function dol_getprefix($mode = '')
1343 {
1344 // If prefix is for email (we need to have $conf already loaded for this case)
1345 if ($mode == 'email') {
1346 global $conf;
1347
1348 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1349 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1350 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1351 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1352 return $_SERVER["SERVER_NAME"];
1353 }
1354 }
1355
1356 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1357 if (!empty($conf->file->instance_unique_id)) {
1358 return sha1('dolibarr'.$conf->file->instance_unique_id);
1359 }
1360
1361 // For backward compatibility when instance_unique_id is not set
1362 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1363 }
1364
1365 // If prefix is for session (no need to have $conf loaded)
1366 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1367 $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
1368
1369 // The recommended value (may be not defined for old versions)
1370 if (!empty($tmp_instance_unique_id)) {
1371 return sha1('dolibarr'.$tmp_instance_unique_id);
1372 }
1373
1374 // For backward compatibility when instance_unique_id is not set
1375 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1376 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1377 } else {
1378 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1379 }
1380 }
1381}
1382
1393function dol_include_once($relpath, $classname = '')
1394{
1395 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']
1396
1397 $fullpath = dol_buildpath($relpath);
1398
1399 if (!file_exists($fullpath)) {
1400 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1401 return false;
1402 }
1403
1404 if (!empty($classname) && !class_exists($classname)) {
1405 return include $fullpath;
1406 } else {
1407 return include_once $fullpath;
1408 }
1409}
1410
1411
1425function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1426{
1427 global $conf;
1428
1429 $path = preg_replace('/^\//', '', $path);
1430
1431 if (empty($type)) { // For a filesystem path
1432 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1433 if (is_array($conf->file->dol_document_root)) {
1434 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1435 if ($key == 'main') {
1436 continue;
1437 }
1438 // if (@file_exists($dirroot.'/'.$path)) {
1439 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1440 $res = $dirroot.'/'.$path;
1441 return $res;
1442 }
1443 }
1444 }
1445 if ($returnemptyifnotfound) {
1446 // Not found into alternate dir
1447 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1448 return '';
1449 }
1450 }
1451 } else {
1452 // For an url path
1453 // We try to get local path of file on filesystem from url
1454 // Note that trying to know if a file on disk exist by forging path on disk from url
1455 // works only for some web server and some setup. This is bugged when
1456 // using proxy, rewriting, virtual path, etc...
1457 $res = '';
1458 if ($type == 1) {
1459 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1460 }
1461 if ($type == 2) {
1462 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1463 }
1464 if ($type == 3) {
1465 $res = DOL_URL_ROOT.'/'.$path;
1466 }
1467
1468 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1469 if ($key == 'main') {
1470 if ($type == 3) {
1471 /*global $dolibarr_main_url_root;*/
1472
1473 // Define $urlwithroot
1474 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1475 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1476 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1477
1478 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1479 }
1480 continue;
1481 }
1482 $regs = array();
1483 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1484 if (!empty($regs[1])) {
1485 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1486 //if (file_exists($dirroot.'/'.$regs[1])) {
1487 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1488 if ($type == 1) {
1489 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1490 } elseif ($type == 2) {
1491 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1492 } elseif ($type == 3) {
1493 /*global $dolibarr_main_url_root;*/
1494
1495 // Define $urlwithroot
1496 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1497 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1498 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1499
1500 $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
1501 }
1502 break;
1503 }
1504 }
1505 }
1506 }
1507
1508 return $res;
1509}
1510
1521function dol_get_object_properties($obj, $properties = [])
1522{
1523 // Get real properties using get_object_vars() if $properties is empty
1524 if (empty($properties)) {
1525 return get_object_vars($obj);
1526 }
1527
1528 $existingProperties = [];
1529 $realProperties = get_object_vars($obj);
1530
1531 // Get the real or magic property values
1532 foreach ($properties as $property) {
1533 if (array_key_exists($property, $realProperties)) {
1534 // Real property, add the value
1535 $existingProperties[$property] = $obj->{$property};
1536 } elseif (property_exists($obj, $property)) {
1537 // Magic property
1538 $existingProperties[$property] = $obj->{$property};
1539 }
1540 }
1541
1542 return $existingProperties;
1543}
1544
1545
1560function dol_clone($object, $native = 2)
1561{
1562 if ($native == 0) {
1563 // deprecated method, use the method with native = 2 instead
1564 $tmpsavdb = null;
1565 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1566 $tmpsavdb = $object->db;
1567 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1568 }
1569
1570 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1571
1572 if (!empty($tmpsavdb)) {
1573 $object->db = $tmpsavdb;
1574 }
1575 } elseif ($native == 2) {
1576 // recommended method to have a full isolated cloned object
1577 $myclone = new stdClass();
1578 $tmparray = get_object_vars($object); // return only public properties
1579
1580 if (is_array($tmparray)) {
1581 foreach ($tmparray as $propertykey => $propertyval) {
1582 if (is_scalar($propertyval) || is_array($propertyval)) {
1583 $myclone->$propertykey = $propertyval;
1584 }
1585 }
1586 }
1587 } else {
1588 $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)
1589 }
1590
1591 return $myclone;
1592}
1593
1603function dol_size($size, $type = '')
1604{
1605 global $conf;
1606 if (empty($conf->dol_optimize_smallscreen)) {
1607 return $size;
1608 }
1609 if ($type == 'width' && $size > 250) {
1610 return 250;
1611 } else {
1612 return 10;
1613 }
1614}
1615
1616
1628function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1629{
1630 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1631 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1632 // Char '/' and '\' are file delimiters.
1633 // 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
1634 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1635 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1636 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1637 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1638 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1639 $tmp = str_replace('..', '', $tmp);
1640 return $tmp;
1641}
1642
1643
1655function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1656{
1657 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1658 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1659 // 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
1660 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1661 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1662 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1663 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1664 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1665 $tmp = str_replace('..', '', $tmp);
1666 return $tmp;
1667}
1668
1676function dol_sanitizeUrl($stringtoclean, $type = 1)
1677{
1678 // 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)
1679 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1680 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1681 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1682 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1683
1684 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1685 if ($type == 1) {
1686 // removing : should disable links to external url like http:aaa)
1687 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1688 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1689 }
1690
1691 do {
1692 $oldstringtoclean = $stringtoclean;
1693 // removing '&colon' should disable links to external url like http:aaa)
1694 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1695 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1696 } while ($oldstringtoclean != $stringtoclean);
1697
1698 if ($type == 1) {
1699 // removing '//' should disable links to external url like //aaa or http//)
1700 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1701 }
1702
1703 return $stringtoclean;
1704}
1705
1712function dol_sanitizeEmail($stringtoclean)
1713{
1714 do {
1715 $oldstringtoclean = $stringtoclean;
1716 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1717 } while ($oldstringtoclean != $stringtoclean);
1718
1719 return $stringtoclean;
1720}
1721
1730function dol_sanitizeKeyCode($str)
1731{
1732 return preg_replace('/[^\w]+/', '', $str);
1733}
1734
1735
1744function dol_string_unaccent($str)
1745{
1746 if (is_null($str)) {
1747 return '';
1748 }
1749
1750 if (utf8_check($str)) {
1751 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1752 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1753 return $transliterator->transliterate($str);
1754 }
1755 // See http://www.utf8-chartable.de/
1756 $string = rawurlencode($str);
1757 $replacements = array(
1758 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1759 '%C3%87' => 'C',
1760 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1761 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1762 '%C3%91' => 'N',
1763 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1764 '%C5%A0' => 'S',
1765 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1766 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1767 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1768 '%C3%A7' => 'c',
1769 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1770 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1771 '%C3%B1' => 'n',
1772 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1773 '%C5%A1' => 's',
1774 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1775 '%C3%BD' => 'y', '%C3%BF' => 'y'
1776 );
1777 $string = strtr($string, $replacements);
1778 return rawurldecode($string);
1779 } else {
1780 // See http://www.ascii-code.com/
1781 $string = strtr(
1782 $str,
1783 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1784 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1785 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1786 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1787 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1788 \xF9\xFA\xFB\xFC\xFD\xFF",
1789 "AAAAAAC
1790 EEEEIIIIDN
1791 OOOOOUUUY
1792 aaaaaaceeee
1793 iiiidnooooo
1794 uuuuyy"
1795 );
1796 $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"));
1797 return $string;
1798 }
1799}
1800
1814function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1815{
1816 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1817 if (empty($keepspaces)) {
1818 $forbidden_chars_to_replace[] = " ";
1819 }
1820 $forbidden_chars_to_remove = array();
1821 //$forbidden_chars_to_remove=array("(",")");
1822
1823 if (is_array($badcharstoreplace)) {
1824 $forbidden_chars_to_replace = $badcharstoreplace;
1825 }
1826 if (is_array($badcharstoremove)) {
1827 $forbidden_chars_to_remove = $badcharstoremove;
1828 }
1829
1830 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1831 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1832}
1833
1834
1848function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1849{
1850 if ($removetabcrlf) {
1851 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1852 } else {
1853 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
1854 }
1855}
1856
1865function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1866{
1867 if (is_null($stringtoescape)) {
1868 return '';
1869 }
1870
1871 // escape quotes and backslashes, newlines, etc.
1872 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1873 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1874 if (empty($noescapebackslashn)) {
1875 $substitjs["\n"] = '\\n';
1876 $substitjs['\\'] = '\\\\';
1877 }
1878 if (empty($mode)) {
1879 $substitjs["'"] = "\\'";
1880 $substitjs['"'] = "\\'";
1881 } elseif ($mode == 1) {
1882 $substitjs["'"] = "\\'";
1883 } elseif ($mode == 2) {
1884 $substitjs['"'] = '\\"';
1885 } elseif ($mode == 3) {
1886 $substitjs["'"] = "\\'";
1887 $substitjs['"'] = "\\\"";
1888 }
1889 return strtr($stringtoescape, $substitjs);
1890}
1891
1898function dol_escape_json($stringtoescape)
1899{
1900 return str_replace('"', '\"', $stringtoescape);
1901}
1902
1910function dol_escape_php($stringtoescape, $stringforquotes = 2)
1911{
1912 if (is_null($stringtoescape)) {
1913 return '';
1914 }
1915
1916 if ($stringforquotes == 2) {
1917 return str_replace('"', "'", $stringtoescape);
1918 } elseif ($stringforquotes == 1) {
1919 // We remove the \ char.
1920 // If we allow the \ char, we can have $stringtoescape =
1921 // abc\';phpcodedanger; so the escapement will become
1922 // abc\\';phpcodedanger; and injecting this into
1923 // $a='...' will give $ac='abc\\';phpcodedanger;
1924 $stringtoescape = str_replace('\\', '', $stringtoescape);
1925 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1926 }
1927
1928 return 'Bad parameter for stringforquotes in dol_escape_php';
1929}
1930
1937function dol_escape_all($stringtoescape)
1938{
1939 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1940}
1941
1948function dol_escape_xml($stringtoescape)
1949{
1950 return $stringtoescape;
1951}
1952
1960function dolPrintLabel($s)
1961{
1962 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1963}
1964
1972function dolPrintText($s)
1973{
1974 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
1975}
1976
1986function dolPrintHTML($s, $allowiframe = 0)
1987{
1988 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1989}
1990
1998function dolPrintHTMLForAttribute($s)
1999{
2000 // The dol_htmlentitiesbr will convert simple text into html
2001 // The dol_escape_htmltag will escape html chars.
2002 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
2003}
2004
2014function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2015{
2016 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2017}
2018
2025function dolPrintPassword($s)
2026{
2027 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
2028}
2029
2030
2047function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2048{
2049 if ($noescapetags == 'common') {
2050 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
2051 // Add also html5 tags
2052 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2053 }
2054 if ($cleanalsojavascript) {
2055 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2056 }
2057
2058 // escape quotes and backslashes, newlines, etc.
2059 if ($escapeonlyhtmltags) {
2060 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2061 } else {
2062 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
2063 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
2064 }
2065 if (!$keepb) {
2066 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2067 }
2068 if (!$keepn) {
2069 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2070 } elseif ($keepn == -1) {
2071 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2072 }
2073
2074 if ($escapeonlyhtmltags) {
2075 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2076 } else {
2077 // Escape tags to keep
2078 $tmparrayoftags = array();
2079 if ($noescapetags) {
2080 $tmparrayoftags = explode(',', $noescapetags);
2081 }
2082 if (count($tmparrayoftags)) {
2083 $reg = array();
2084 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2085
2086 foreach ($tmparrayoftags as $tagtoreplace) {
2087 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2088 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2089 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2090
2091 // For case of tag with attribute
2092 do {
2093 $tmpold = $tmp;
2094
2095 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', $tmp, $reg)) {
2096 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must never have [ ] inside the attribute string
2097 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes);
2098 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2099 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2100 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2101 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2102 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2103 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2104 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2105 }
2106 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)\s+\/>/', $tmp, $reg)) {
2107 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
2108 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2109 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2110 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2111 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2112 }
2113
2114 $diff = strcmp($tmpold, $tmp);
2115 } while ($diff);
2116 }
2117 }
2118
2119 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2120
2121 //print $result;
2122
2123 if (count($tmparrayoftags)) {
2124 foreach ($tmparrayoftags as $tagtoreplace) {
2125 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2126 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2127 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2128 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2129 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2130 }
2131
2132 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2133 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2134 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2135 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2136 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2137 }
2138
2139 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2140
2141 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2142
2143 return $result;
2144 }
2145}
2146
2154function dol_strtolower($string, $encoding = "UTF-8")
2155{
2156 if (function_exists('mb_strtolower')) {
2157 return mb_strtolower($string, $encoding);
2158 } else {
2159 return strtolower($string);
2160 }
2161}
2162
2171function dol_strtoupper($string, $encoding = "UTF-8")
2172{
2173 if (function_exists('mb_strtoupper')) {
2174 return mb_strtoupper($string, $encoding);
2175 } else {
2176 return strtoupper($string);
2177 }
2178}
2179
2188function dol_ucfirst($string, $encoding = "UTF-8")
2189{
2190 if (function_exists('mb_substr')) {
2191 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2192 } else {
2193 return ucfirst($string);
2194 }
2195}
2196
2205function dol_ucwords($string, $encoding = "UTF-8")
2206{
2207 if (function_exists('mb_convert_case')) {
2208 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2209 } else {
2210 return ucwords($string);
2211 }
2212}
2213
2214
2220function getCallerInfoString()
2221{
2222 $backtrace = debug_backtrace();
2223 $msg = "";
2224 if (count($backtrace) >= 2) {
2225 $trace = $backtrace[1];
2226 if (isset($trace['file'], $trace['line'])) {
2227 $msg = " From {$trace['file']}:{$trace['line']}.";
2228 }
2229 }
2230 return $msg;
2231}
2232
2255function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2256{
2257 global $conf, $user, $debugbar;
2258
2259 // If syslog module enabled
2260 if (!isModEnabled('syslog')) {
2261 return;
2262 }
2263
2264 // Check if we are into execution of code of a website
2265 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2266 global $website, $websitekey;
2267 if (is_object($website) && !empty($website->ref)) {
2268 $suffixinfilename .= '_website_'.$website->ref;
2269 } elseif (!empty($websitekey)) {
2270 $suffixinfilename .= '_website_'.$websitekey;
2271 }
2272 }
2273
2274 // Check if we have a forced suffix
2275 if (defined('USESUFFIXINLOG')) {
2276 $suffixinfilename .= constant('USESUFFIXINLOG');
2277 }
2278
2279 if ($ident < 0) {
2280 foreach ($conf->loghandlers as $loghandlerinstance) {
2281 $loghandlerinstance->setIdent($ident);
2282 }
2283 }
2284
2285 if (!empty($message)) {
2286 // Test log level @phan-suppress-next-line PhanPluginDuplicateArrayKey
2287 $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');
2288 if (!array_key_exists($level, $logLevels)) {
2289 throw new Exception('Incorrect log level');
2290 }
2291 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2292 return;
2293 }
2294
2295 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2296 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2297 }
2298
2299 // If adding log inside HTML page is required
2300 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2301 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2302 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2303 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2304
2305 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2306 }
2307
2308 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2309 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2310 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2311 print "\n\n<!-- Log start\n";
2312 print dol_escape_htmltag($message)."\n";
2313 print "Log end -->\n";
2314 }
2315
2316 $data = array(
2317 'message' => $message,
2318 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2319 'level' => $level,
2320 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2321 'ip' => false,
2322 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2323 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2324 );
2325
2326 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2327 if (!empty($remoteip)) {
2328 $data['ip'] = $remoteip;
2329 // This is when server run behind a reverse proxy
2330 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2331 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2332 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2333 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2334 }
2335 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2336 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2337 $data['ip'] = $_SERVER['SERVER_ADDR'];
2338 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2339 // 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).
2340 $data['ip'] = $_SERVER['COMPUTERNAME'];
2341 } else {
2342 $data['ip'] = '???';
2343 }
2344
2345 if (!empty($_SERVER['USERNAME'])) {
2346 // 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).
2347 $data['osuser'] = $_SERVER['USERNAME'];
2348 } elseif (!empty($_SERVER['LOGNAME'])) {
2349 // 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).
2350 $data['osuser'] = $_SERVER['LOGNAME'];
2351 }
2352
2353 // Loop on each log handler and send output
2354 foreach ($conf->loghandlers as $loghandlerinstance) {
2355 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2356 continue;
2357 }
2358 $loghandlerinstance->export($data, $suffixinfilename);
2359 }
2360 unset($data);
2361 }
2362
2363 if ($ident > 0) {
2364 foreach ($conf->loghandlers as $loghandlerinstance) {
2365 $loghandlerinstance->setIdent($ident);
2366 }
2367 }
2368}
2369
2381function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2382{
2383 global $langs, $db;
2384
2385 $form = new Form($db);
2386
2387 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2388 if (empty($templatenameforexport)) {
2389 $templatenameforexport = 'website_'.$website->ref;
2390 }
2391
2392 $out = '';
2393 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2394
2395 // for generate popup
2396 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2397 $out .= 'jQuery(document).ready(function () {';
2398 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2399 $out .= ' var dialogHtml = \'';
2400
2401 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2402 $dialogcontent .= ' <div style="margin-top: 20px;">';
2403 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2404 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2405 $dialogcontent .= ' </div>';
2406 $dialogcontent .= ' <br>';
2407 $dialogcontent .= ' <div style="margin-top: 20px;">';
2408 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2409 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2410 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2411 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2412 $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>';
2413 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2414 $dialogcontent .= ' </form>';
2415 $dialogcontent .= ' </div>';
2416 $dialogcontent .= ' </div>';
2417
2418 $out .= dol_escape_js($dialogcontent);
2419
2420 $out .= '\';';
2421
2422
2423 // Add the content of the dialog to the body of the page
2424 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2425 $out .= ' if ($dialog.length > 0) {
2426 $dialog.remove();
2427 }
2428 jQuery("body").append(dialogHtml);';
2429
2430 // Configuration of popup
2431 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2432 $out .= ' autoOpen: false,';
2433 $out .= ' modal: true,';
2434 $out .= ' height: 290,';
2435 $out .= ' width: "40%",';
2436 $out .= ' title: "' . dol_escape_js($label) . '",';
2437 $out .= ' });';
2438
2439 // Simulate a click on the original "submit" input to export the site.
2440 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2441 $out .= ' console.log("Clic on exportsite.");';
2442 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2443 $out .= ' console.log("element founded:", target.length > 0);';
2444 $out .= ' if (target.length > 0) { target.click(); }';
2445 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2446 $out .= ' });';
2447
2448 // open popup
2449 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2450 $out .= ' return false;';
2451 $out .= ' });';
2452 $out .= '});';
2453 $out .= '</script>';
2454
2455 return $out;
2456}
2457
2458
2475function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2476{
2477 global $conf;
2478
2479 if (strpos($url, '?') > 0) {
2480 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2481 } else {
2482 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2483 }
2484
2485 $out = '';
2486
2487 $backtopagejsfieldsid = '';
2488 $backtopagejsfieldslabel = '';
2489 if ($backtopagejsfields) {
2490 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2491 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2492 $backtopagejsfields = $name.":".$backtopagejsfields;
2493 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2494 } else {
2495 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2496 }
2497 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2498 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2499 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2500 }
2501
2502 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2503 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2504 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2505 if (empty($conf->use_javascript_ajax)) {
2506 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2507 } elseif ($jsonopen) {
2508 $out .= ' href="#" onclick="'.$jsonopen.'"';
2509 } else {
2510 $out .= ' href="#"';
2511 }
2512 $out .= '>'.$buttonstring.'</a>';
2513
2514 if (!empty($conf->use_javascript_ajax)) {
2515 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2516 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2517 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2518 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2519 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2520
2521 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2522 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2523 jQuery(document).ready(function () {
2524 jQuery(".button_'.$name.'").click(function () {
2525 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2526 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2527 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2528 $tmpdialog.dialog({
2529 autoOpen: false,
2530 modal: true,
2531 height: (window.innerHeight - 150),
2532 width: \'80%\',
2533 title: \''.dol_escape_js($label).'\',
2534 open: function (event, ui) {
2535 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2536 },
2537 close: function (event, ui) {
2538 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2539 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2540 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2541 if (returnedid != "" && returnedid != "div for returned id") {
2542 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2543 }
2544 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2545 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2546 }
2547 }
2548 });
2549
2550 $tmpdialog.dialog(\'open\');
2551 return false;
2552 });
2553 });
2554 </script>';
2555 }
2556 return $out;
2557}
2558
2575function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2576{
2577 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2578}
2579
2596function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2597{
2598 global $conf, $langs, $hookmanager;
2599
2600 // Show title
2601 $showtitle = 1;
2602 if (!empty($conf->dol_optimize_smallscreen)) {
2603 $showtitle = 0;
2604 }
2605
2606 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2607
2608 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2609 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2610 }
2611
2612 // Show right part
2613 if ($morehtmlright) {
2614 $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.
2615 }
2616
2617 // Show title
2618 /*
2619 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2620 $limittitle = 30;
2621 $out .= '<a class="tabTitle">';
2622 if ($picto) {
2623 $noprefix = $pictoisfullpath;
2624 if (strpos($picto, 'fontawesome_') !== false) {
2625 $noprefix = 1;
2626 }
2627 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2628 }
2629 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2630 $out .= '</a>';
2631 }
2632 */
2633
2634 // Show tabs
2635
2636 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2637 $maxkey = -1;
2638 if (is_array($links) && !empty($links)) {
2639 $keys = array_keys($links);
2640 if (count($keys)) {
2641 $maxkey = max($keys);
2642 }
2643 }
2644
2645 // Show tabs
2646 // if =0 we don't use the feature
2647 if (empty($limittoshow)) {
2648 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2649 }
2650 if (!empty($conf->dol_optimize_smallscreen)) {
2651 $limittoshow = 2;
2652 }
2653
2654 $displaytab = 0;
2655 $nbintab = 0;
2656 $popuptab = 0;
2657 $outmore = '';
2658 for ($i = 0; $i <= $maxkey; $i++) {
2659 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2660 // If active tab is already present
2661 if ($i >= $limittoshow) {
2662 $limittoshow--;
2663 }
2664 }
2665 }
2666
2667 for ($i = 0; $i <= $maxkey; $i++) {
2668 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2669 $isactive = true;
2670 } else {
2671 $isactive = false;
2672 }
2673
2674 if ($i < $limittoshow || $isactive) {
2675 // Output entry with a visible tab
2676 $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])).' -->';
2677
2678 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2679 if (!empty($links[$i][0])) {
2680 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2681 } else {
2682 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2683 }
2684 } elseif (!empty($links[$i][1])) {
2685 //print "x $i $active ".$links[$i][2]." z";
2686 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2687
2688 if (!empty($links[$i][0])) {
2689 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2690 $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).'">';
2691 }
2692
2693 if ($displaytab == 0 && $picto) {
2694 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2695 }
2696
2697 $out .= $links[$i][1];
2698 if (!empty($links[$i][0])) {
2699 $out .= '</a>'."\n";
2700 }
2701 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2702 $out .= '</div>';
2703 }
2704
2705 $out .= '</div>';
2706 } else {
2707 // Add entry into the combo popup with the other tabs
2708 if (!$popuptab) {
2709 $popuptab = 1;
2710 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2711 }
2712 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2713 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2714 if (!empty($links[$i][0])) {
2715 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2716 } else {
2717 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2718 }
2719 } elseif (!empty($links[$i][1])) {
2720 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2721 $outmore .= 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.
2722 $outmore .= '</a>'."\n";
2723 }
2724 $outmore .= '</div>';
2725
2726 $nbintab++;
2727 }
2728
2729 $displaytab = $i + 1;
2730 }
2731 if ($popuptab) {
2732 $outmore .= '</div>';
2733 }
2734
2735 if ($popuptab) { // If there is some tabs not shown
2736 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2737 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2738 $widthofpopup = 200;
2739
2740 $tabsname = $moretabssuffix;
2741 if (empty($tabsname)) {
2742 $tabsname = str_replace("@", "", $picto);
2743 }
2744 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2745 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2746 $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".
2747 }
2748 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2749 $out .= $outmore;
2750 $out .= '</div>';
2751 $out .= '<div></div>';
2752 $out .= "</div>\n";
2753
2754 $out .= '<script nonce="'.getNonce().'">';
2755 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2756 var x = this.offsetLeft, y = this.offsetTop;
2757 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2758 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2759 $('#moretabsList".$tabsname."').css('".$right."','8px');
2760 }
2761 $('#moretabsList".$tabsname."').css('".$left."','auto');
2762 });
2763 ";
2764 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2765 $out .= "</script>";
2766 }
2767
2768 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2769 $out .= "</div>\n";
2770 }
2771
2772 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
2773 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
2774 $out .= '">'."\n";
2775 }
2776 if (!empty($dragdropfile)) {
2777 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2778 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2779 }
2780 $parameters = array('tabname' => $active, 'out' => $out);
2781 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2782 if ($reshook > 0) {
2783 $out = $hookmanager->resPrint;
2784 }
2785
2786 return $out;
2787}
2788
2796function dol_fiche_end($notab = 0)
2797{
2798 print dol_get_fiche_end($notab);
2799}
2800
2807function dol_get_fiche_end($notab = 0)
2808{
2809 if (!$notab || $notab == -1) {
2810 return "\n</div>\n";
2811 } else {
2812 return '';
2813 }
2814}
2815
2835function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2836{
2837 global $conf, $form, $user, $langs, $hookmanager, $action;
2838
2839 $error = 0;
2840
2841 $maxvisiblephotos = 1;
2842 $showimage = 1;
2843 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2844 // @phan-suppress-next-line PhanUndeclaredMethod
2845 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2846 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2847 $showbarcode = 0;
2848 }
2849 $modulepart = 'unknown';
2850
2851 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2852 $modulepart = $object->element;
2853 } elseif ($object->element == 'member') {
2854 $modulepart = 'memberphoto';
2855 } elseif ($object->element == 'user') {
2856 $modulepart = 'userphoto';
2857 }
2858
2859 if (class_exists("Imagick")) {
2860 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2861 $modulepart = $object->element;
2862 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2863 $modulepart = 'ficheinter';
2864 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2865 $modulepart = 'contract';
2866 } elseif ($object->element == 'order_supplier') {
2867 $modulepart = 'supplier_order';
2868 } elseif ($object->element == 'invoice_supplier') {
2869 $modulepart = 'supplier_invoice';
2870 }
2871 }
2872
2873 if ($object->element == 'product') {
2875 '@phan-var-force Product $object';
2876 $width = 80;
2877 $cssclass = 'photowithmargin photoref';
2878 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2879 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2880 if ($conf->browser->layout == 'phone') {
2881 $maxvisiblephotos = 1;
2882 }
2883 if ($showimage) {
2884 $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>';
2885 } else {
2886 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2887 $nophoto = '';
2888 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2889 } else { // Show no photo link
2890 $nophoto = '/public/theme/common/nophoto.png';
2891 $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>';
2892 }
2893 }
2894 } elseif ($object->element == 'category') {
2896 '@phan-var-force Categorie $object';
2897 $width = 80;
2898 $cssclass = 'photowithmargin photoref';
2899 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2900 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2901 if ($conf->browser->layout == 'phone') {
2902 $maxvisiblephotos = 1;
2903 }
2904 if ($showimage) {
2905 $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>';
2906 } else {
2907 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2908 $nophoto = '';
2909 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2910 } else { // Show no photo link
2911 $nophoto = '/public/theme/common/nophoto.png';
2912 $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>';
2913 }
2914 }
2915 } elseif ($object->element == 'bom') {
2917 '@phan-var-force Bom $object';
2918 $width = 80;
2919 $cssclass = 'photowithmargin photoref';
2920 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2921 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2922 if ($conf->browser->layout == 'phone') {
2923 $maxvisiblephotos = 1;
2924 }
2925 if ($showimage) {
2926 $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>';
2927 } else {
2928 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2929 $nophoto = '';
2930 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2931 } else { // Show no photo link
2932 $nophoto = '/public/theme/common/nophoto.png';
2933 $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>';
2934 }
2935 }
2936 } elseif ($object->element == 'ticket') {
2937 $width = 80;
2938 $cssclass = 'photoref';
2940 '@phan-var-force Ticket $object';
2941 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2942 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2943 if ($conf->browser->layout == 'phone') {
2944 $maxvisiblephotos = 1;
2945 }
2946
2947 if ($showimage) {
2948 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2949 if ($object->nbphoto > 0) {
2950 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2951 } else {
2952 $showimage = 0;
2953 }
2954 }
2955 if (!$showimage) {
2956 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2957 $nophoto = '';
2958 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2959 } else { // Show no photo link
2960 $nophoto = img_picto('No photo', 'object_ticket');
2961 $morehtmlleft .= '<!-- No photo to show -->';
2962 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2963 $morehtmlleft .= $nophoto;
2964 $morehtmlleft .= '</div></div>';
2965 }
2966 }
2967 } else {
2968 if ($showimage) {
2969 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2970 $phototoshow = '';
2971 // Check if a preview file is available
2972 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2973 $objectref = dol_sanitizeFileName($object->ref);
2974 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2975 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2976 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2977 $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
2978 } else {
2979 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2980 }
2981 if (empty($subdir)) {
2982 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2983 }
2984
2985 $filepath = $dir_output.$subdir."/";
2986
2987 $filepdf = $filepath.$objectref.".pdf";
2988 $relativepath = $subdir.'/'.$objectref.'.pdf';
2989
2990 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2991 $fileimage = $filepdf.'_preview.png';
2992 $relativepathimage = $relativepath.'_preview.png';
2993
2994 $pdfexists = file_exists($filepdf);
2995
2996 // If PDF file exists
2997 if ($pdfexists) {
2998 // Conversion du PDF en image png si fichier png non existent
2999 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3000 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3001 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3002 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3003 if ($ret < 0) {
3004 $error++;
3005 }
3006 }
3007 }
3008 }
3009
3010 if ($pdfexists && !$error) {
3011 $heightforphotref = 80;
3012 if (!empty($conf->dol_optimize_smallscreen)) {
3013 $heightforphotref = 60;
3014 }
3015 // If the preview file is found
3016 if (file_exists($fileimage)) {
3017 $phototoshow = '<div class="photoref">';
3018 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3019 $phototoshow .= '</div>';
3020 }
3021 }
3022 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3023 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
3024 }
3025
3026 if ($phototoshow) {
3027 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3028 $morehtmlleft .= $phototoshow;
3029 $morehtmlleft .= '</div>';
3030 }
3031 }
3032
3033 if (empty($phototoshow)) { // Show No photo link (picto of object)
3034 if ($object->element == 'action') {
3035 $width = 80;
3036 $cssclass = 'photorefcenter';
3037 $nophoto = img_picto('No photo', 'title_agenda');
3038 } else {
3039 $width = 14;
3040 $cssclass = 'photorefcenter';
3041 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3042 $prefix = 'object_';
3043 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3044 $picto = 'project'; // instead of projectpub
3045 }
3046 if (strpos($picto, 'fontawesome_') !== false) {
3047 $prefix = '';
3048 }
3049 $nophoto = img_picto('No photo', $prefix.$picto);
3050 }
3051 $morehtmlleft .= '<!-- No photo to show -->';
3052 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3053 $morehtmlleft .= $nophoto;
3054 $morehtmlleft .= '</div></div>';
3055 }
3056 }
3057 }
3058
3059 // Show barcode
3060 if ($showbarcode) {
3061 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3062 }
3063
3064 if ($object->element == 'societe') {
3065 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3066 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3067 } else {
3068 $morehtmlstatus .= $object->getLibStatut(6);
3069 }
3070 } elseif ($object->element == 'product') {
3071 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3072 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3073 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3074 } else {
3075 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3076 }
3077 $morehtmlstatus .= ' &nbsp; ';
3078 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3079 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3080 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3081 } else {
3082 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3083 }
3084 } elseif (in_array($object->element, array('salary'))) {
3085 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3086 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3087 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3088 }
3089 $morehtmlstatus .= $tmptxt;
3090 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3091 $totalallpayments = $object->getSommePaiement(0);
3092 $totalallpayments += $object->getSumCreditNotesUsed(0);
3093 $totalallpayments += $object->getSumDepositsUsed(0);
3094 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3095 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3096 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3097 }
3098 $morehtmlstatus .= $tmptxt;
3099 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3100 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3101 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3102 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3103 }
3104 $morehtmlstatus .= $tmptxt;
3105 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3106 if ($object->statut == 0) {
3107 $morehtmlstatus .= $object->getLibStatut(5);
3108 } else {
3109 $morehtmlstatus .= $object->getLibStatut(4);
3110 }
3111 } elseif ($object->element == 'facturerec') {
3112 '@phan-var-force FactureRec $object';
3113 if ($object->frequency == 0) {
3114 $morehtmlstatus .= $object->getLibStatut(2);
3115 } else {
3116 $morehtmlstatus .= $object->getLibStatut(5);
3117 }
3118 } elseif ($object->element == 'project_task') {
3119 $object->fk_statut = 1;
3120 $object->status = 1;
3121 if ($object->progress > 0) {
3122 $object->fk_statut = 2;
3123 $object->status = 2;
3124 }
3125 if ($object->progress >= 100) {
3126 $object->fk_statut = 3;
3127 $object->status = 3;
3128 }
3129 $tmptxt = $object->getLibStatut(5);
3130 $morehtmlstatus .= $tmptxt; // No status on task
3131 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3132 $tmptxt = $object->getLibStatut(6);
3133 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3134 $tmptxt = $object->getLibStatut(5);
3135 }
3136 $morehtmlstatus .= $tmptxt;
3137 }
3138
3139 // Add if object was dispatched "into accountancy"
3140 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3141 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3142 if (method_exists($object, 'getVentilExportCompta')) {
3143 $accounted = $object->getVentilExportCompta();
3144 $langs->load("accountancy");
3145 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3146 }
3147 }
3148
3149 // Add alias for thirdparty
3150 if (!empty($object->name_alias)) {
3151 '@phan-var-force Societe $object';
3152 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3153 }
3154
3155 // Add label
3156 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3157 if (!empty($object->label)) {
3158 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3159 }
3160 }
3161 // Show address and email
3162 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3163 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3164 if ($moreaddress) {
3165 $morehtmlref .= '<div class="refidno refaddress">';
3166 $morehtmlref .= $moreaddress;
3167 $morehtmlref .= '</div>';
3168 }
3169 }
3170 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)) {
3171 $morehtmlref .= '<div style="clear: both;"></div>';
3172 $morehtmlref .= '<div class="refidno opacitymedium">';
3173 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3174 $morehtmlref .= '</div>';
3175 }
3176
3177 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3178 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3179 if ($reshook < 0) {
3180 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3181 } elseif (empty($reshook)) {
3182 $morehtmlref .= $hookmanager->resPrint;
3183 } elseif ($reshook > 0) {
3184 $morehtmlref = $hookmanager->resPrint;
3185 }
3186
3187 // $morehtml is the right part (link "Back to list")
3188 // $morehtmlleft is the picto or photo of banner
3189 // $morehtmlstatus is part under the status
3190 // $morehtmlright is part of htmlright
3191
3192 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3193 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3194 print '</div>';
3195 print '<div class="underrefbanner clearboth"></div>';
3196}
3197
3207function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3208{
3209 global $langs;
3210 $ret = '';
3211 if ($fieldrequired) {
3212 $ret .= '<span class="fieldrequired">';
3213 }
3214 $ret .= '<label for="'.$fieldkey.'">';
3215 $ret .= $langs->trans($langkey);
3216 $ret .= '</label>';
3217 if ($fieldrequired) {
3218 $ret .= '</span>';
3219 }
3220 return $ret;
3221}
3222
3236function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3237{
3238 global $langs, $hookmanager;
3239
3240 $ret = '';
3241 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3242
3243 // See format of addresses on https://en.wikipedia.org/wiki/Address
3244 // Address
3245 if (empty($mode)) {
3246 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3247 }
3248 // Zip/Town/State
3249 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3250 // US: title firstname name \n address lines \n town, state, zip \n country
3251 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3252 $ret .= (($ret && $town) ? $sep : '').$town;
3253
3254 if (!empty($object->state)) {
3255 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3256 }
3257 if (!empty($object->zip)) {
3258 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3259 }
3260 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3261 // UK: title firstname name \n address lines \n town state \n zip \n country
3262 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3263 $ret .= ($ret ? $sep : '').$town;
3264 if (!empty($object->state)) {
3265 $ret .= ($ret ? ", " : '').$object->state;
3266 }
3267 if (!empty($object->zip)) {
3268 $ret .= ($ret ? $sep : '').$object->zip;
3269 }
3270 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3271 // ES: title firstname name \n address lines \n zip town \n state \n country
3272 $ret .= ($ret ? $sep : '').$object->zip;
3273 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3274 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3275 if (!empty($object->state)) {
3276 $ret .= $sep.$object->state;
3277 }
3278 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3279 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3280 // See https://www.sljfaq.org/afaq/addresses.html
3281 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3282 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3283 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3284 // IT: title firstname name\n address lines \n zip town state_code \n country
3285 $ret .= ($ret ? $sep : '').$object->zip;
3286 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3287 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3288 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3289 } else {
3290 // Other: title firstname name \n address lines \n zip town[, state] \n country
3291 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3292 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3293 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3294 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3295 $ret .= ($ret ? ", " : '').$object->state;
3296 }
3297 }
3298
3299 if (!is_object($outputlangs)) {
3300 $outputlangs = $langs;
3301 }
3302 if ($withcountry) {
3303 $langs->load("dict");
3304 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3305 }
3306 if ($hookmanager) {
3307 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3308 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3309 if ($reshook > 0) {
3310 $ret = '';
3311 }
3312 $ret .= $hookmanager->resPrint;
3313 }
3314
3315 return $ret;
3316}
3317
3318
3319
3329function dol_strftime($fmt, $ts = false, $is_gmt = false)
3330{
3331 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3332 return dol_print_date($ts, $fmt, $is_gmt);
3333 } else {
3334 return 'Error date outside supported range';
3335 }
3336}
3337
3359function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3360{
3361 global $conf, $langs;
3362
3363 // If date undefined or "", we return ""
3364 if (dol_strlen($time) == 0) {
3365 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3366 }
3367
3368 if ($tzoutput === 'auto') {
3369 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3370 }
3371
3372 // Clean parameters
3373 $to_gmt = false;
3374 $offsettz = $offsetdst = 0;
3375 if ($tzoutput) {
3376 $to_gmt = true; // For backward compatibility
3377 if (is_string($tzoutput)) {
3378 if ($tzoutput == 'tzserver') {
3379 $to_gmt = false;
3380 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3381 // @phan-suppress-next-line PhanPluginRedundantAssignment
3382 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3383 // @phan-suppress-next-line PhanPluginRedundantAssignment
3384 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3385 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3386 $to_gmt = true;
3387 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3388
3389 if (class_exists('DateTimeZone')) {
3390 $user_date_tz = new DateTimeZone($offsettzstring);
3391 $user_dt = new DateTime();
3392 $user_dt->setTimezone($user_date_tz);
3393 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3394 $offsettz = $user_dt->getOffset(); // should include dst ?
3395 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3396 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3397 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3398 }
3399 }
3400 }
3401 }
3402 if (!is_object($outputlangs)) {
3403 $outputlangs = $langs;
3404 }
3405 if (!$format) {
3406 $format = 'daytextshort';
3407 }
3408
3409 // Do we have to reduce the length of date (year on 2 chars) to save space.
3410 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3411 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3412 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3413 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3414 if ($formatwithoutreduce != $format) {
3415 $format = $formatwithoutreduce;
3416 $reduceformat = 1;
3417 } // so format 'dayreduceformat' is processed like day
3418
3419 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3420 // TODO Add format daysmallyear and dayhoursmallyear
3421 if ($format == 'day') {
3422 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3423 } elseif ($format == 'hour') {
3424 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3425 } elseif ($format == 'hourduration') {
3426 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3427 } elseif ($format == 'daytext') {
3428 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3429 } elseif ($format == 'daytextshort') {
3430 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3431 } elseif ($format == 'dayhour') {
3432 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3433 } elseif ($format == 'dayhoursec') {
3434 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3435 } elseif ($format == 'dayhourtext') {
3436 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3437 } elseif ($format == 'dayhourtextshort') {
3438 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3439 } elseif ($format == 'dayhourlog') {
3440 // Format not sensitive to language
3441 $format = '%Y%m%d%H%M%S';
3442 } elseif ($format == 'dayhourlogsmall') {
3443 // Format not sensitive to language
3444 $format = '%y%m%d%H%M';
3445 } elseif ($format == 'dayhourldap') {
3446 $format = '%Y%m%d%H%M%SZ';
3447 } elseif ($format == 'dayhourxcard') {
3448 $format = '%Y%m%dT%H%M%SZ';
3449 } elseif ($format == 'dayxcard') {
3450 $format = '%Y%m%d';
3451 } elseif ($format == 'dayrfc') {
3452 $format = '%Y-%m-%d'; // DATE_RFC3339
3453 } elseif ($format == 'dayhourrfc') {
3454 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3455 } elseif ($format == 'standard') {
3456 $format = '%Y-%m-%d %H:%M:%S';
3457 }
3458
3459 if ($reduceformat) {
3460 $format = str_replace('%Y', '%y', $format);
3461 $format = str_replace('yyyy', 'yy', $format);
3462 }
3463
3464 // Clean format
3465 if (preg_match('/%b/i', $format)) { // There is some text to translate
3466 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3467 $format = str_replace('%b', '__b__', $format);
3468 $format = str_replace('%B', '__B__', $format);
3469 }
3470 if (preg_match('/%a/i', $format)) { // There is some text to translate
3471 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3472 $format = str_replace('%a', '__a__', $format);
3473 $format = str_replace('%A', '__A__', $format);
3474 }
3475
3476 // Analyze date
3477 $reg = array();
3478 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
3479 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"]));
3480 return '';
3481 } 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
3482 // This part of code should not be used anymore.
3483 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);
3484 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3485 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3486 $syear = (!empty($reg[1]) ? $reg[1] : '');
3487 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3488 $sday = (!empty($reg[3]) ? $reg[3] : '');
3489 $shour = (!empty($reg[4]) ? $reg[4] : '');
3490 $smin = (!empty($reg[5]) ? $reg[5] : '');
3491 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3492
3493 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3494
3495 if ($to_gmt) {
3496 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3497 } else {
3498 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3499 }
3500 $dtts = new DateTime();
3501 $dtts->setTimestamp($time);
3502 $dtts->setTimezone($tzo);
3503 $newformat = str_replace(
3504 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3505 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3506 $format
3507 );
3508 $ret = $dtts->format($newformat);
3509 $ret = str_replace(
3510 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3511 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3512 $ret
3513 );
3514 } else {
3515 // Date is a timestamps
3516 if ($time < 100000000000) { // Protection against bad date values
3517 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3518
3519 if ($to_gmt) {
3520 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3521 } else {
3522 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3523 }
3524 $dtts = new DateTime();
3525 $dtts->setTimestamp($timetouse);
3526 $dtts->setTimezone($tzo);
3527 $newformat = str_replace(
3528 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3529 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3530 $format
3531 );
3532 $ret = $dtts->format($newformat);
3533 $ret = str_replace(
3534 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3535 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3536 $ret
3537 );
3538 //var_dump($ret);exit;
3539 } else {
3540 $ret = 'Bad value '.$time.' for date';
3541 }
3542 }
3543
3544 if (preg_match('/__b__/i', $format)) {
3545 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3546
3547 if ($to_gmt) {
3548 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3549 } else {
3550 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3551 }
3552 $dtts = new DateTime();
3553 $dtts->setTimestamp($timetouse);
3554 $dtts->setTimezone($tzo);
3555 $month = (int) $dtts->format("m");
3556 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3557 if ($encodetooutput) {
3558 $monthtext = $outputlangs->transnoentities('Month'.$month);
3559 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3560 } else {
3561 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3562 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3563 }
3564 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3565 $ret = str_replace('__b__', $monthtextshort, $ret);
3566 $ret = str_replace('__B__', $monthtext, $ret);
3567 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3568 //return $ret;
3569 }
3570 if (preg_match('/__a__/i', $format)) {
3571 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3572 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3573
3574 if ($to_gmt) {
3575 $tzo = new DateTimeZone('UTC');
3576 } else {
3577 $tzo = new DateTimeZone(date_default_timezone_get());
3578 }
3579 $dtts = new DateTime();
3580 $dtts->setTimestamp($timetouse);
3581 $dtts->setTimezone($tzo);
3582 $w = $dtts->format("w");
3583 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3584
3585 $ret = str_replace('__A__', $dayweek, $ret);
3586 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3587 }
3588
3589 return $ret;
3590}
3591
3592
3613function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3614{
3615 if ($timestamp === '') {
3616 return array();
3617 }
3618
3619 $datetimeobj = new DateTime();
3620 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3621 if ($forcetimezone) {
3622 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3623 }
3624 $arrayinfo = array(
3625 'year' => ((int) date_format($datetimeobj, 'Y')),
3626 'mon' => ((int) date_format($datetimeobj, 'm')),
3627 'mday' => ((int) date_format($datetimeobj, 'd')),
3628 'wday' => ((int) date_format($datetimeobj, 'w')),
3629 'yday' => ((int) date_format($datetimeobj, 'z')),
3630 'hours' => ((int) date_format($datetimeobj, 'H')),
3631 'minutes' => ((int) date_format($datetimeobj, 'i')),
3632 'seconds' => ((int) date_format($datetimeobj, 's')),
3633 '0' => $timestamp
3634 );
3635
3636 return $arrayinfo;
3637}
3638
3660function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3661{
3662 global $conf;
3663 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3664
3665 if ($gm === 'auto') {
3666 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3667 }
3668 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3669
3670 // Clean parameters
3671 if ($hour == -1 || empty($hour)) {
3672 $hour = 0;
3673 }
3674 if ($minute == -1 || empty($minute)) {
3675 $minute = 0;
3676 }
3677 if ($second == -1 || empty($second)) {
3678 $second = 0;
3679 }
3680
3681 // Check parameters
3682 if ($check) {
3683 if (!$month || !$day) {
3684 return '';
3685 }
3686 if ($day > 31) {
3687 return '';
3688 }
3689 if ($month > 12) {
3690 return '';
3691 }
3692 if ($hour < 0 || $hour > 24) {
3693 return '';
3694 }
3695 if ($minute < 0 || $minute > 60) {
3696 return '';
3697 }
3698 if ($second < 0 || $second > 60) {
3699 return '';
3700 }
3701 }
3702
3703 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3704 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3705 $localtz = new DateTimeZone($default_timezone);
3706 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3707 // We use dol_tz_string first because it is more reliable.
3708 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3709 try {
3710 $localtz = new DateTimeZone($default_timezone);
3711 } catch (Exception $e) {
3712 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3713 $default_timezone = @date_default_timezone_get();
3714 }
3715 } elseif (strrpos($gm, "tz,") !== false) {
3716 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3717 try {
3718 $localtz = new DateTimeZone($timezone);
3719 } catch (Exception $e) {
3720 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3721 }
3722 }
3723
3724 if (empty($localtz)) {
3725 $localtz = new DateTimeZone('UTC');
3726 }
3727 //var_dump($localtz);
3728 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3729 $dt = new DateTime('now', $localtz);
3730 $dt->setDate((int) $year, (int) $month, (int) $day);
3731 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3732 $date = $dt->getTimestamp(); // should include daylight saving time
3733 //var_dump($date);
3734 return $date;
3735}
3736
3737
3748function dol_now($mode = 'auto')
3749{
3750 $ret = 0;
3751
3752 if ($mode === 'auto') {
3753 $mode = 'gmt';
3754 }
3755
3756 if ($mode == 'gmt') {
3757 $ret = time(); // Time for now at greenwich.
3758 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3759 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3760 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3761 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3762 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3763 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3764 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3765 // $ret=dol_now('gmt')+($tzsecond*3600);
3766 //}
3767 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3768 // Time for now with user timezone added
3769 //print 'time: '.time();
3770 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3771 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3772 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3773 }
3774
3775 return $ret;
3776}
3777
3778
3787function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3788{
3789 global $conf, $langs;
3790 $level = 1024;
3791
3792 if (!empty($conf->dol_optimize_smallscreen)) {
3793 $shortunit = 1;
3794 }
3795
3796 // Set value text
3797 if (empty($shortvalue) || $size < ($level * 10)) {
3798 $ret = $size;
3799 $textunitshort = $langs->trans("b");
3800 $textunitlong = $langs->trans("Bytes");
3801 } else {
3802 $ret = round($size / $level, 0);
3803 $textunitshort = $langs->trans("Kb");
3804 $textunitlong = $langs->trans("KiloBytes");
3805 }
3806 // Use long or short text unit
3807 if (empty($shortunit)) {
3808 $ret .= ' '.$textunitlong;
3809 } else {
3810 $ret .= ' '.$textunitshort;
3811 }
3812
3813 return $ret;
3814}
3815
3826function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3827{
3828 global $langs;
3829
3830 if (empty($url)) {
3831 return '';
3832 }
3833
3834 $linkstart = '<a href="';
3835 if (!preg_match('/^http/i', $url)) {
3836 $linkstart .= 'http://';
3837 }
3838 $linkstart .= $url;
3839 $linkstart .= '"';
3840 if ($target) {
3841 $linkstart .= ' target="'.$target.'"';
3842 }
3843 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3844 $linkstart .= '>';
3845
3846 $link = '';
3847 if (!preg_match('/^http/i', $url)) {
3848 $link .= 'http://';
3849 }
3850 $link .= dol_trunc($url, $max);
3851
3852 $linkend = '</a>';
3853
3854 if ($morecss == 'float') { // deprecated
3855 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3856 } else {
3857 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3858 }
3859}
3860
3874function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
3875{
3876 global $user, $langs, $hookmanager;
3877
3878 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3879 //$showinvalid = 1; $email = 'rrrrr';
3880
3881 $newemail = dol_escape_htmltag($email);
3882
3883 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3884 $withpicto = 0;
3885 }
3886
3887 if (empty($email)) {
3888 return '&nbsp;';
3889 }
3890
3891 if ($addlink == 1) {
3892 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
3893 if (!preg_match('/^mailto:/i', $email)) {
3894 $newemail .= 'mailto:';
3895 }
3896 $newemail .= $email;
3897 $newemail .= '" target="_blank">';
3898
3899 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3900
3901 if ($max > 0) {
3902 $newemail .= dol_trunc($email, $max);
3903 } else {
3904 $newemail .= $email;
3905 }
3906 $newemail .= '</a>';
3907 if ($showinvalid && !isValidEmail($email)) {
3908 $langs->load("errors");
3909 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3910 }
3911
3912 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3913 $type = 'AC_EMAIL';
3914 $linktoaddaction = '';
3915 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3916 $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>';
3917 }
3918 if ($linktoaddaction) {
3919 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3920 }
3921 }
3922 } elseif ($addlink === 'thirdparty') {
3923 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
3924 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3925 if ($withpicto == 1) {
3926 $tmpnewemail .= $newemail;
3927 }
3928 $tmpnewemail .= '</a>';
3929
3930 $newemail = $tmpnewemail;
3931 } else {
3932 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3933
3934 if ($showinvalid && !isValidEmail($email)) {
3935 $langs->load("errors");
3936 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3937 }
3938 }
3939
3940 //$rep = '<div class="nospan" style="margin-right: 10px">';
3941 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3942 //$rep .= '</div>';
3943 $rep = $newemail;
3944
3945 if ($hookmanager) {
3946 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3947
3948 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3949 if ($reshook > 0) {
3950 $rep = '';
3951 }
3952 $rep .= $hookmanager->resPrint;
3953 }
3954
3955 return $rep;
3956}
3957
3963function getArrayOfSocialNetworks()
3964{
3965 global $conf, $db;
3966
3967 $socialnetworks = array();
3968 // Enable caching of array
3969 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3970 $cachekey = 'socialnetworks_' . $conf->entity;
3971 $dataretrieved = dol_getcache($cachekey);
3972 if (!is_null($dataretrieved)) {
3973 $socialnetworks = $dataretrieved;
3974 } else {
3975 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3976 $sql .= " WHERE entity=".$conf->entity;
3977 $resql = $db->query($sql);
3978 if ($resql) {
3979 while ($obj = $db->fetch_object($resql)) {
3980 $socialnetworks[$obj->code] = array(
3981 'rowid' => $obj->rowid,
3982 'label' => $obj->label,
3983 'url' => $obj->url,
3984 'icon' => $obj->icon,
3985 'active' => $obj->active,
3986 );
3987 }
3988 }
3989 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3990 }
3991 return $socialnetworks;
3992}
3993
4004function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
4005{
4006 global $hookmanager, $langs, $user;
4007
4008 $htmllink = $value;
4009
4010 if (empty($value)) {
4011 return '&nbsp;';
4012 }
4013
4014 if (!empty($type)) {
4015 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4016 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4017 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
4018 if ($type == 'skype') {
4019 $htmllink .= dol_escape_htmltag($value);
4020 $htmllink .= '&nbsp; <a href="skype:';
4021 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4022 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
4023 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
4024 $htmllink .= '</a><a href="skype:';
4025 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4026 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
4027 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
4028 $htmllink .= '</a>';
4029 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4030 $addlink = 'AC_SKYPE';
4031 $link = '';
4032 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4033 $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$addlink.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
4034 }
4035 $htmllink .= ($link ? ' '.$link : '');
4036 }
4037 } else {
4038 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
4039 if (getDolGlobalString($networkconstname)) {
4040 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
4041 if (preg_match('/^https?:\/\//i', $link)) {
4042 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4043 } elseif ($link) {
4044 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4045 }
4046 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
4047 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4048 if ($tmpvirginurl) {
4049 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4050 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4051
4052 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4053 if ($tmpvirginurl3) {
4054 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4055 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4056 }
4057
4058 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4059 if ($tmpvirginurl2) {
4060 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4061 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4062 }
4063 }
4064 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4065 if (preg_match('/^https?:\/\//i', $link)) {
4066 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4067 } else {
4068 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4069 }
4070 } else {
4071 $htmllink .= dol_escape_htmltag($value);
4072 }
4073 }
4074 $htmllink .= '</div>';
4075 } else {
4076 $langs->load("errors");
4077 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4078 }
4079
4080 if ($hookmanager) {
4081 $parameters = array(
4082 'value' => $value,
4083 'cid' => $cid,
4084 'socid' => $socid,
4085 'type' => $type,
4086 'dictsocialnetworks' => $dictsocialnetworks,
4087 );
4088
4089 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4090 if ($reshook > 0) {
4091 $htmllink = '';
4092 }
4093 $htmllink .= $hookmanager->resPrint;
4094 }
4095
4096 return $htmllink;
4097}
4098
4108function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4109{
4110 global $mysoc;
4111
4112 if (empty($profID) || empty($profIDtype)) {
4113 return '';
4114 }
4115 if (empty($countrycode)) {
4116 $countrycode = $mysoc->country_code;
4117 }
4118 $newProfID = $profID;
4119 $id = substr($profIDtype, -1);
4120 $ret = '';
4121 if (strtoupper($countrycode) == 'FR') {
4122 // France
4123 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4124
4125 if ($id == 1 && dol_strlen($newProfID) == 9) {
4126 // SIREN (ex: 123 123 123)
4127 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4128 }
4129 if ($id == 2 && dol_strlen($newProfID) == 14) {
4130 // SIRET (ex: 123 123 123 12345)
4131 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4132 }
4133 if ($id == 3 && dol_strlen($newProfID) == 5) {
4134 // NAF/APE (ex: 69.20Z)
4135 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4136 }
4137 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4138 // TVA intracommunautaire (ex: FR12 123 123 123)
4139 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4140 }
4141 }
4142 if (!empty($addcpButton)) {
4143 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4144 } else {
4145 $ret = $newProfID;
4146 }
4147 return $ret;
4148}
4149
4165function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4166{
4167 global $conf, $user, $langs, $mysoc, $hookmanager;
4168
4169 // Clean phone parameter
4170 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4171 if (empty($phone)) {
4172 return '';
4173 }
4174 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4175 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4176 }
4177 if (empty($countrycode) && is_object($mysoc)) {
4178 $countrycode = $mysoc->country_code;
4179 }
4180
4181 // Short format for small screens
4182 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4183 $separ = '';
4184 }
4185
4186 $newphone = $phone;
4187 $newphonewa = $phone;
4188 if (strtoupper($countrycode) == "FR") {
4189 // France
4190 if (dol_strlen($phone) == 10) {
4191 $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);
4192 } elseif (dol_strlen($phone) == 7) {
4193 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4194 } elseif (dol_strlen($phone) == 9) {
4195 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4196 } elseif (dol_strlen($phone) == 11) {
4197 $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);
4198 } elseif (dol_strlen($phone) == 12) {
4199 $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);
4200 } elseif (dol_strlen($phone) == 13) {
4201 $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);
4202 }
4203 } elseif (strtoupper($countrycode) == "CA") {
4204 if (dol_strlen($phone) == 10) {
4205 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4206 }
4207 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4208 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4209 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4210 }
4211 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4212 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4213 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4214 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4215 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4216 }
4217 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4218 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4219 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4220 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4221 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4222 }
4223 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4224 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4225 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4226 }
4227 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4228 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4229 $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);
4230 }
4231 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4232 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4233 $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);
4234 }
4235 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4236 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4237 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4238 }
4239 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4240 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4241 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4242 }
4243 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4244 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4245 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4246 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4247 $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);
4248 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4249 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4250 }
4251 } elseif (strtoupper($countrycode) == "ML") {//Mali
4252 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4253 $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);
4254 }
4255 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4256 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4257 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4258 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4259 $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);
4260 }
4261 } elseif (strtoupper($countrycode) == "MU") {
4262 //Maurice
4263 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4264 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4265 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4266 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4267 }
4268 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4269 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4270 $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);
4271 }
4272 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4273 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4274 $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);
4275 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4276 $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);
4277 }
4278 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4279 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4280 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4281 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4282 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4283 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4284 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4285 }
4286 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4287 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4288 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4289 }
4290 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4291 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4292 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4293 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4294 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4295 }
4296 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4297 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4298 $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);
4299 }
4300 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4301 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4302 $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);
4303 }
4304 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4305 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4306 $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);
4307 }
4308 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4309 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4310 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4311 }
4312 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4313 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4314 $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);
4315 }
4316 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4317 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4318 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4319 }
4320 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4321 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4322 $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);
4323 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4324 $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);
4325 }
4326 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4327 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4328 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4329 }
4330 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4331 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4332 $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);
4333 }
4334 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4335 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4336 $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);
4337 }
4338 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4339 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4340 $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);
4341 }
4342 } elseif (strtoupper($countrycode) == "IT") {//Italie
4343 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4344 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4345 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4346 $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);
4347 }
4348 } elseif (strtoupper($countrycode) == "AU") {
4349 //Australie
4350 if (dol_strlen($phone) == 12) {
4351 //ex: +61_A_BCDE_FGHI
4352 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4353 }
4354 } elseif (strtoupper($countrycode) == "LU") {
4355 // Luxembourg
4356 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4357 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4358 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4359 $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);
4360 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4361 $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);
4362 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4363 $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);
4364 }
4365 } elseif (strtoupper($countrycode) == "PE") {
4366 // Peru
4367 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4368 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4369 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4370 $newphonewa = '+51'.$newphone;
4371 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4372 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4373 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4374 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4375 $newphonewa = $newphone;
4376 $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);
4377 }
4378 }
4379
4380 $newphoneastart = $newphoneaend = '';
4381 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4382 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
4383 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4384 $newphoneaend .= '</a>';
4385 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4386 if (empty($user->clicktodial_loaded)) {
4387 $user->fetch_clicktodial();
4388 }
4389
4390 // Define urlmask
4391 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4392 if (!empty($user->clicktodial_url)) {
4393 $urlmask = $user->clicktodial_url;
4394 }
4395
4396 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4397 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4398 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4399 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4400 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4401 // Those lines are for substitution
4402 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4403 '__PHONETO__' => urlencode($phone),
4404 '__LOGIN__' => $clicktodial_login,
4405 '__PASS__' => $clicktodial_password);
4406 $url = make_substitutions($url, $substitarray);
4407 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4408 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4409 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4410 $newphoneaend = '</a>';
4411 } else {
4412 // Old method
4413 $newphoneastart = '<a href="'.$url.'"';
4414 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4415 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4416 }
4417 $newphoneastart .= '>';
4418 $newphoneaend .= '</a>';
4419 }
4420 }
4421
4422 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4423 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4424 $type = 'AC_TEL';
4425 $addlinktoagenda = '';
4426 if ($addlink == 'AC_FAX') {
4427 $type = 'AC_FAX';
4428 }
4429 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4430 $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>';
4431 }
4432 if ($addlinktoagenda) {
4433 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4434 }
4435 }
4436 }
4437
4438 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4439 // Link to Whatsapp
4440 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4441 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4442 }
4443
4444 if (empty($titlealt)) {
4445 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4446 }
4447 $rep = '';
4448
4449 if ($hookmanager) {
4450 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4451 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4452 $rep .= $hookmanager->resPrint;
4453 }
4454 if (empty($reshook)) {
4455 $picto = '';
4456 if ($withpicto) {
4457 if ($withpicto == 'fax') {
4458 $picto = 'phoning_fax';
4459 } elseif ($withpicto == 'phone') {
4460 $picto = 'phoning';
4461 } elseif ($withpicto == 'mobile') {
4462 $picto = 'phoning_mobile';
4463 } else {
4464 $picto = '';
4465 }
4466 }
4467 if ($adddivfloat == 1) {
4468 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4469 } elseif (empty($adddivfloat)) {
4470 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4471 }
4472
4473 $rep .= $newphoneastart;
4474 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4475 if ($separ != 'hidenum') {
4476 $rep .= ($withpicto ? ' ' : '').$newphone;
4477 }
4478 $rep .= $newphoneaend;
4479
4480 if ($adddivfloat == 1) {
4481 $rep .= '</div>';
4482 } elseif (empty($adddivfloat)) {
4483 $rep .= '</span>';
4484 }
4485 }
4486
4487 return $rep;
4488}
4489
4497function dol_print_ip($ip, $mode = 0)
4498{
4499 global $langs;
4500
4501 $ret = '';
4502
4503 if (empty($mode)) {
4504 $ret .= $ip;
4505 }
4506
4507 if ($mode != 2) {
4508 $countrycode = dolGetCountryCodeFromIp($ip);
4509 if ($countrycode) { // If success, countrycode is us, fr, ...
4510 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4511 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4512 } else {
4513 $ret .= ' ('.$countrycode.')';
4514 }
4515 } else {
4516 // Nothing
4517 }
4518 }
4519
4520 return $ret;
4521}
4522
4531function getUserRemoteIP()
4532{
4533 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4534 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4535 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4536 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4537 } else {
4538 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4539 }
4540 } else {
4541 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4542 }
4543 } else {
4544 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4545 }
4546 return $ip;
4547}
4548
4557function isHTTPS()
4558{
4559 $isSecure = false;
4560 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4561 $isSecure = true;
4562 } 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') {
4563 $isSecure = true;
4564 }
4565 return $isSecure;
4566}
4567
4574function dolGetCountryCodeFromIp($ip)
4575{
4576 global $conf;
4577
4578 $countrycode = '';
4579
4580 if (isModEnabled('geoipmaxmind')) {
4581 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4582 //$ip='24.24.24.24';
4583 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4584 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4585 $geoip = new DolGeoIP('country', $datafile);
4586 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4587 $countrycode = $geoip->getCountryCodeFromIP($ip);
4588 }
4589
4590 return $countrycode;
4591}
4592
4593
4600function dol_user_country()
4601{
4602 global $conf, $langs, $user;
4603
4604 //$ret=$user->xxx;
4605 $ret = '';
4606 if (isModEnabled('geoipmaxmind')) {
4607 $ip = getUserRemoteIP();
4608 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4609 //$ip='24.24.24.24';
4610 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4611 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4612 $geoip = new DolGeoIP('country', $datafile);
4613 $countrycode = $geoip->getCountryCodeFromIP($ip);
4614 $ret = $countrycode;
4615 }
4616 return $ret;
4617}
4618
4631function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4632{
4633 global $conf, $user, $langs, $hookmanager;
4634
4635 $out = '';
4636
4637 if ($address) {
4638 if ($hookmanager) {
4639 $parameters = array('element' => $element, 'id' => $id);
4640 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4641 $out .= $hookmanager->resPrint;
4642 }
4643 if (empty($reshook)) {
4644 if (empty($charfornl)) {
4645 $out .= nl2br($address);
4646 } else {
4647 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4648 }
4649
4650 // TODO Remove this block, we can add this using the hook now
4651 $showgmap = $showomap = 0;
4652 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4653 $showgmap = 1;
4654 }
4655 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4656 $showgmap = 1;
4657 }
4658 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4659 $showgmap = 1;
4660 }
4661 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4662 $showgmap = 1;
4663 }
4664 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4665 $showomap = 1;
4666 }
4667 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4668 $showomap = 1;
4669 }
4670 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4671 $showomap = 1;
4672 }
4673 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4674 $showomap = 1;
4675 }
4676 if ($showgmap) {
4677 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4678 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4679 }
4680 if ($showomap) {
4681 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4682 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4683 }
4684 }
4685 }
4686 if ($noprint) {
4687 return $out;
4688 } else {
4689 print $out;
4690 return null;
4691 }
4692}
4693
4694
4704function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4705{
4706 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4707 return true;
4708 }
4709 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4710 return true;
4711 }
4712 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4713 return true;
4714 }
4715
4716 return false;
4717}
4718
4728function isValidMXRecord($domain)
4729{
4730 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4731 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4732 return 0;
4733 }
4734 if (function_exists('getmxrr')) {
4735 $mxhosts = array();
4736 $weight = array();
4737 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4738 if (count($mxhosts) > 1) {
4739 return 1;
4740 }
4741 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4742 return 1;
4743 }
4744
4745 return 0;
4746 }
4747 }
4748
4749 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4750 return -1;
4751}
4752
4760function isValidPhone($phone)
4761{
4762 return true;
4763}
4764
4765
4775function dolGetFirstLetters($s, $nbofchar = 1)
4776{
4777 $ret = '';
4778 $tmparray = explode(' ', $s);
4779 foreach ($tmparray as $tmps) {
4780 $ret .= dol_substr($tmps, 0, $nbofchar);
4781 }
4782
4783 return $ret;
4784}
4785
4786
4794function dol_strlen($string, $stringencoding = 'UTF-8')
4795{
4796 if (is_null($string)) {
4797 return 0;
4798 }
4799
4800 if (function_exists('mb_strlen')) {
4801 return mb_strlen($string, $stringencoding);
4802 } else {
4803 return strlen($string);
4804 }
4805}
4806
4817function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4818{
4819 global $langs;
4820
4821 if (empty($stringencoding)) {
4822 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4823 }
4824
4825 $ret = '';
4826 if (empty($trunconbytes)) {
4827 if (function_exists('mb_substr')) {
4828 $ret = mb_substr($string, $start, $length, $stringencoding);
4829 } else {
4830 $ret = substr($string, $start, $length);
4831 }
4832 } else {
4833 if (function_exists('mb_strcut')) {
4834 $ret = mb_strcut($string, $start, $length, $stringencoding);
4835 } else {
4836 $ret = substr($string, $start, $length);
4837 }
4838 }
4839 return $ret;
4840}
4841
4842
4856function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4857{
4858 global $conf;
4859
4860 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4861 return $string;
4862 }
4863
4864 if (empty($stringencoding)) {
4865 $stringencoding = 'UTF-8';
4866 }
4867 // reduce for small screen
4868 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4869 $size = round($size / 3);
4870 }
4871
4872 // We go always here
4873 if ($trunc == 'right') {
4874 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4875 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4876 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4877 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4878 } else {
4879 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4880 return $string;
4881 }
4882 } elseif ($trunc == 'middle') {
4883 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4884 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4885 $size1 = (int) round($size / 2);
4886 $size2 = (int) round($size / 2);
4887 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4888 } else {
4889 return $string;
4890 }
4891 } elseif ($trunc == 'left') {
4892 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4893 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4894 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4895 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4896 } else {
4897 return $string;
4898 }
4899 } elseif ($trunc == 'wrap') {
4900 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4901 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4902 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4903 } else {
4904 return $string;
4905 }
4906 } else {
4907 return 'BadParam3CallingDolTrunc';
4908 }
4909}
4910
4918function getPictoForType($key, $morecss = '')
4919{
4920 // Set array with type -> picto
4921 $type2picto = array(
4922 'varchar' => 'font',
4923 'text' => 'font',
4924 'html' => 'code',
4925 'int' => 'sort-numeric-down',
4926 'double' => 'sort-numeric-down',
4927 'price' => 'currency',
4928 'pricecy' => 'multicurrency',
4929 'password' => 'key',
4930 'boolean' => 'check-square',
4931 'date' => 'calendar',
4932 'datetime' => 'calendar',
4933 'duration' => 'hourglass',
4934 'phone' => 'phone',
4935 'mail' => 'email',
4936 'url' => 'url',
4937 'ip' => 'country',
4938 'select' => 'list',
4939 'sellist' => 'list',
4940 'stars' => 'fontawesome_star_fas',
4941 'radio' => 'check-circle',
4942 'checkbox' => 'list',
4943 'chkbxlst' => 'list',
4944 'link' => 'link',
4945 'icon' => "question",
4946 'point' => "country",
4947 'multipts' => 'country',
4948 'linestrg' => "country",
4949 'polygon' => "country",
4950 'separate' => 'minus'
4951 );
4952
4953 if (!empty($type2picto[$key])) {
4954 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4955 }
4956
4957 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4958}
4959
4960
4982function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4983{
4984 global $conf;
4985
4986 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4987 $url = DOL_URL_ROOT;
4988 $theme = isset($conf->theme) ? $conf->theme : null;
4989 $path = 'theme/'.$theme;
4990 if (empty($picto)) {
4991 $picto = 'generic';
4992 }
4993
4994 // Define fullpathpicto to use into src
4995 if ($pictoisfullpath) {
4996 // Clean parameters
4997 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4998 $picto .= '.png';
4999 }
5000 $fullpathpicto = $picto;
5001 $reg = array();
5002 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5003 $morecss .= ($morecss ? ' ' : '').$reg[1];
5004 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5005 }
5006 } else {
5007 // $picto can not be null since replaced with 'generic' in that case
5008 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5009 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5010 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5011 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5012
5013 // Fix some values of $pictowithouttext
5014 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5015 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5016 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5017 }
5018
5019 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5020 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5021 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5022 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5023
5024 // Compatibility with old fontawesome versions
5025 if ($pictowithouttext == 'file-o') {
5026 $pictowithouttext = 'file';
5027 }
5028
5029 $pictowithouttextarray = explode('_', $pictowithouttext);
5030 $marginleftonlyshort = 0;
5031
5032 if (!empty($pictowithouttextarray[1])) {
5033 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5034 $fakey = 'fa-'.$pictowithouttextarray[0];
5035 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5036 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5037 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5038 } else {
5039 $fakey = 'fa-'.$pictowithouttext;
5040 $faprefix = 'fas';
5041 $facolor = '';
5042 $fasize = '';
5043 }
5044
5045 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5046 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5047 $morestyle = '';
5048 $reg = array();
5049 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5050 $morecss .= ($morecss ? ' ' : '').$reg[1];
5051 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5052 }
5053 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5054 $morestyle = $reg[1];
5055 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5056 }
5057 $moreatt = trim($moreatt);
5058
5059 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5060 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5061 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5062 $enabledisablehtml .= $titlealt;
5063 }*/
5064 $enabledisablehtml .= '</span>';
5065
5066 return $enabledisablehtml;
5067 }
5068
5069 if (empty($srconly) && in_array($pictowithouttext, array(
5070 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5071 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5072 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5073 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5074 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5075 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5076 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
5077 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5078 'commercial', 'companies',
5079 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5080 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5081 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5082 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5083 'hands-helping', 'help', 'holiday',
5084 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5085 'key', 'knowledgemanagement',
5086 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5087 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5088 'off', 'on', 'order',
5089 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5090 'stock', 'resize', 'service', 'stats',
5091 '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',
5092 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
5093 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5094 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5095 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5096 'technic', 'ticket',
5097 'error', 'warning',
5098 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
5099 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5100 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5101 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5102 'conferenceorbooth', 'eventorganization',
5103 'stamp', 'signature',
5104 'webportal'
5105 ))) {
5106 $fakey = $pictowithouttext;
5107 $facolor = '';
5108 $fasize = '';
5109 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5110 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'))) {
5111 $fa = 'far';
5112 }
5113 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
5114 $fa = 'fab';
5115 }
5116
5117 $arrayconvpictotofa = array(
5118 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5119 'asset' => 'money-check-alt', 'autofill' => 'fill',
5120 'back' => 'arrow-left', 'bank_account' => 'university',
5121 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5122 'bookcal' => 'calendar-check',
5123 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5124 'bom' => 'shapes',
5125 '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',
5126 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5127 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5128 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5129 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5130 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5131 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5132 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5133 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5134 'generic' => 'file', 'holiday' => 'umbrella-beach',
5135 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5136 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5137 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5138 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5139 'sign-out' => 'sign-out-alt',
5140 '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',
5141 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5142 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5143 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5144 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5145 'other' => 'square',
5146 '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',
5147 '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',
5148 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5149 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5150 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5151 'service' => 'concierge-bell',
5152 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5153 'status' => 'stop-circle',
5154 'stripe' => 'stripe-s', 'supplier' => 'building',
5155 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5156 'title_agenda' => 'calendar-alt',
5157 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5158 'jabber' => 'comment-o',
5159 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5160 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5161 'webportal' => 'door-open'
5162 );
5163 if ($conf->currency == 'EUR') {
5164 $arrayconvpictotofa['currency'] = 'euro-sign';
5165 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5166 } else {
5167 $arrayconvpictotofa['currency'] = 'dollar-sign';
5168 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5169 }
5170 if ($pictowithouttext == 'off') {
5171 $fakey = 'fa-square';
5172 $fasize = '1.3em';
5173 } elseif ($pictowithouttext == 'on') {
5174 $fakey = 'fa-check-square';
5175 $fasize = '1.3em';
5176 } elseif ($pictowithouttext == 'listlight') {
5177 $fakey = 'fa-download';
5178 $marginleftonlyshort = 1;
5179 } elseif ($pictowithouttext == 'printer') {
5180 $fakey = 'fa-print';
5181 $fasize = '1.2em';
5182 } elseif ($pictowithouttext == 'note') {
5183 $fakey = 'fa-sticky-note';
5184 $marginleftonlyshort = 1;
5185 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5186 $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');
5187 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5188 if (preg_match('/selected/', $pictowithouttext)) {
5189 $facolor = '#888';
5190 }
5191 $marginleftonlyshort = 1;
5192 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5193 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5194 } else {
5195 $fakey = 'fa-'.$pictowithouttext;
5196 }
5197
5198 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5199 $morecss .= ' em092';
5200 }
5201 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5202 $morecss .= ' em088';
5203 }
5204 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5205 $morecss .= ' em080';
5206 }
5207
5208 // Define $marginleftonlyshort
5209 $arrayconvpictotomarginleftonly = array(
5210 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5211 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5212 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5213 );
5214 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5215 $marginleftonlyshort = 0;
5216 }
5217
5218 // Add CSS
5219 $arrayconvpictotomorcess = array(
5220 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5221 'bank_account' => 'infobox-bank_account',
5222 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5223 'bookcal' => 'infobox-action',
5224 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5225 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5226 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5227 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5228 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5229 'incoterm' => 'infobox-supplier_proposal',
5230 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5231 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5232 'order' => 'infobox-commande',
5233 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5234 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5235 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5236 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5237 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5238 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5239 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5240 'resource' => 'infobox-action',
5241 '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',
5242 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5243 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5244 'vat' => 'infobox-bank_account',
5245 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5246 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5247 );
5248 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5249 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5250 }
5251
5252 // Define $color
5253 $arrayconvpictotocolor = array(
5254 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5255 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5256 'dynamicprice' => '#a69944',
5257 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5258 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5259 'lock' => '#ddd', 'lot' => '#a69944',
5260 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5261 'other' => '#ddd', 'world' => '#986c6a',
5262 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5263 //'shipment'=>'#a69944',
5264 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5265 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5266 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5267 'website' => '#304', 'workstation' => '#a69944'
5268 );
5269 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5270 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5271 }
5272
5273 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5274 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5275 $morestyle = '';
5276 $reg = array();
5277 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5278 $morecss .= ($morecss ? ' ' : '').$reg[1];
5279 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5280 }
5281 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5282 $morestyle = $reg[1];
5283 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5284 }
5285 $moreatt = trim($moreatt);
5286
5287 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5288 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5289 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5290 $enabledisablehtml .= $titlealt;
5291 }*/
5292 $enabledisablehtml .= '</span>';
5293
5294 return $enabledisablehtml;
5295 }
5296
5297 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5298 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5299 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5300 $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
5301 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5302 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5303 }
5304
5305 // If we ask an image into $url/$mymodule/img (instead of default path)
5306 $regs = array();
5307 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5308 $picto = $regs[1];
5309 $path = $regs[2]; // $path is $mymodule
5310 }
5311
5312 // Clean parameters
5313 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5314 $picto .= '.png';
5315 }
5316 // If alt path are defined, define url where img file is, according to physical path
5317 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5318 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5319 if ($type == 'main') {
5320 continue;
5321 }
5322 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5323 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5324 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5325 break;
5326 }
5327 }
5328
5329 // $url is '' or '/custom', $path is current theme or
5330 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5331 }
5332
5333 if ($srconly) {
5334 return $fullpathpicto;
5335 }
5336
5337 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5338 return '<img src="'.$fullpathpicto.'"'.($notitle ? '' : ' alt="'.dol_escape_htmltag($alt).'"').(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt.($morecss ? ' class="'.$morecss.'"' : '') : ' class="inline-block'.($morecss ? ' '.$morecss : '').'"').'>'; // Alt is used for accessibility, title for popup
5339}
5340
5354function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5355{
5356 if (strpos($picto, '^') === 0) {
5357 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5358 } else {
5359 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5360 }
5361}
5362
5374function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5375{
5376 global $conf;
5377
5378 if (is_numeric($picto)) {
5379 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5380 //$picto = $leveltopicto[$picto];
5381 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5382 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5383 $picto .= '.png';
5384 }
5385
5386 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5387
5388 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5389}
5390
5402function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5403{
5404 global $conf;
5405
5406 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5407 $picto .= '.png';
5408 }
5409
5410 if ($pictoisfullpath) {
5411 $path = $picto;
5412 } else {
5413 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5414
5415 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5416 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5417
5418 if (file_exists($themepath)) {
5419 $path = $themepath;
5420 }
5421 }
5422 }
5423
5424 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5425}
5426
5440function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5441{
5442 global $langs;
5443
5444 if (empty($titlealt) || $titlealt == 'default') {
5445 if ($numaction == '-1' || $numaction == 'ST_NO') {
5446 $numaction = -1;
5447 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5448 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5449 $numaction = 0;
5450 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5451 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5452 $numaction = 1;
5453 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5454 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5455 $numaction = 2;
5456 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5457 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5458 $numaction = 3;
5459 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5460 } else {
5461 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5462 $numaction = 0;
5463 }
5464 }
5465 if (!is_numeric($numaction)) {
5466 $numaction = 0;
5467 }
5468
5469 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5470}
5471
5479function img_pdf($titlealt = 'default', $size = 3)
5480{
5481 global $langs;
5482
5483 if ($titlealt == 'default') {
5484 $titlealt = $langs->trans('Show');
5485 }
5486
5487 return img_picto($titlealt, 'pdf'.$size.'.png');
5488}
5489
5497function img_edit_add($titlealt = 'default', $other = '')
5498{
5499 global $langs;
5500
5501 if ($titlealt == 'default') {
5502 $titlealt = $langs->trans('Add');
5503 }
5504
5505 return img_picto($titlealt, 'edit_add.png', $other);
5506}
5514function img_edit_remove($titlealt = 'default', $other = '')
5515{
5516 global $langs;
5517
5518 if ($titlealt == 'default') {
5519 $titlealt = $langs->trans('Remove');
5520 }
5521
5522 return img_picto($titlealt, 'edit_remove.png', $other);
5523}
5524
5533function img_edit($titlealt = 'default', $float = 0, $other = '')
5534{
5535 global $langs;
5536
5537 if ($titlealt == 'default') {
5538 $titlealt = $langs->trans('Modify');
5539 }
5540
5541 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5542}
5543
5552function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5553{
5554 global $langs;
5555
5556 if ($titlealt == 'default') {
5557 $titlealt = $langs->trans('View');
5558 }
5559
5560 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5561
5562 return img_picto($titlealt, 'eye', $moreatt);
5563}
5564
5573function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5574{
5575 global $langs;
5576
5577 if ($titlealt == 'default') {
5578 $titlealt = $langs->trans('Delete');
5579 }
5580
5581 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5582}
5583
5591function img_printer($titlealt = "default", $other = '')
5592{
5593 global $langs;
5594 if ($titlealt == "default") {
5595 $titlealt = $langs->trans("Print");
5596 }
5597 return img_picto($titlealt, 'printer.png', $other);
5598}
5599
5607function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5608{
5609 global $langs;
5610
5611 if ($titlealt == 'default') {
5612 $titlealt = $langs->trans('Split');
5613 }
5614
5615 return img_picto($titlealt, 'split.png', $other);
5616}
5617
5625function img_help($usehelpcursor = 1, $usealttitle = 1)
5626{
5627 global $langs;
5628
5629 if ($usealttitle) {
5630 if (is_string($usealttitle)) {
5631 $usealttitle = dol_escape_htmltag($usealttitle);
5632 } else {
5633 $usealttitle = $langs->trans('Info');
5634 }
5635 }
5636
5637 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5638}
5639
5646function img_info($titlealt = 'default')
5647{
5648 global $langs;
5649
5650 if ($titlealt == 'default') {
5651 $titlealt = $langs->trans('Informations');
5652 }
5653
5654 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5655}
5656
5665function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5666{
5667 global $langs;
5668
5669 if ($titlealt == 'default') {
5670 $titlealt = $langs->trans('Warning');
5671 }
5672
5673 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5674 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5675}
5676
5683function img_error($titlealt = 'default')
5684{
5685 global $langs;
5686
5687 if ($titlealt == 'default') {
5688 $titlealt = $langs->trans('Error');
5689 }
5690
5691 return img_picto($titlealt, 'error.png');
5692}
5693
5701function img_next($titlealt = 'default', $moreatt = '')
5702{
5703 global $langs;
5704
5705 if ($titlealt == 'default') {
5706 $titlealt = $langs->trans('Next');
5707 }
5708
5709 //return img_picto($titlealt, 'next.png', $moreatt);
5710 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5711}
5712
5720function img_previous($titlealt = 'default', $moreatt = '')
5721{
5722 global $langs;
5723
5724 if ($titlealt == 'default') {
5725 $titlealt = $langs->trans('Previous');
5726 }
5727
5728 //return img_picto($titlealt, 'previous.png', $moreatt);
5729 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5730}
5731
5740function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5741{
5742 global $langs;
5743
5744 if ($titlealt == 'default') {
5745 $titlealt = $langs->trans('Down');
5746 }
5747
5748 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5749}
5750
5759function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5760{
5761 global $langs;
5762
5763 if ($titlealt == 'default') {
5764 $titlealt = $langs->trans('Up');
5765 }
5766
5767 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5768}
5769
5778function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5779{
5780 global $langs;
5781
5782 if ($titlealt == 'default') {
5783 $titlealt = $langs->trans('Left');
5784 }
5785
5786 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5787}
5788
5797function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5798{
5799 global $langs;
5800
5801 if ($titlealt == 'default') {
5802 $titlealt = $langs->trans('Right');
5803 }
5804
5805 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5806}
5807
5815function img_allow($allow, $titlealt = 'default')
5816{
5817 global $langs;
5818
5819 if ($titlealt == 'default') {
5820 $titlealt = $langs->trans('Active');
5821 }
5822
5823 if ($allow == 1) {
5824 return img_picto($titlealt, 'tick.png');
5825 }
5826
5827 return '-';
5828}
5829
5837function img_credit_card($brand, $morecss = null)
5838{
5839 if (is_null($morecss)) {
5840 $morecss = 'fa-2x';
5841 }
5842
5843 if ($brand == 'visa' || $brand == 'Visa') {
5844 $brand = 'cc-visa';
5845 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5846 $brand = 'cc-mastercard';
5847 } elseif ($brand == 'amex' || $brand == 'American Express') {
5848 $brand = 'cc-amex';
5849 } elseif ($brand == 'discover' || $brand == 'Discover') {
5850 $brand = 'cc-discover';
5851 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5852 $brand = 'cc-jcb';
5853 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5854 $brand = 'cc-diners-club';
5855 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5856 $brand = 'credit-card';
5857 }
5858
5859 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5860}
5861
5870function img_mime($file, $titlealt = '', $morecss = '')
5871{
5872 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5873
5874 $mimetype = dol_mimetype($file, '', 1);
5875 $mimeimg = dol_mimetype($file, '', 2);
5876 $mimefa = dol_mimetype($file, '', 4);
5877
5878 if (empty($titlealt)) {
5879 $titlealt = 'Mime type: '.$mimetype;
5880 }
5881
5882 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5883 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5884}
5885
5886
5894function img_search($titlealt = 'default', $other = '')
5895{
5896 global $langs;
5897
5898 if ($titlealt == 'default') {
5899 $titlealt = $langs->trans('Search');
5900 }
5901
5902 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5903
5904 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5905 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5906
5907 return $input;
5908}
5909
5917function img_searchclear($titlealt = 'default', $other = '')
5918{
5919 global $langs;
5920
5921 if ($titlealt == 'default') {
5922 $titlealt = $langs->trans('Search');
5923 }
5924
5925 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5926
5927 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5928 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5929
5930 return $input;
5931}
5932
5945function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5946{
5947 global $conf, $langs;
5948
5949 if ($infoonimgalt) {
5950 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5951 } else {
5952 if (empty($conf->use_javascript_ajax)) {
5953 $textfordropdown = '';
5954 }
5955
5956 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5957 $fa = 'info-circle';
5958 if ($picto == 'warning') {
5959 $fa = 'exclamation-triangle';
5960 }
5961 $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> ';
5962 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5963 $result .= ($nodiv ? '' : '</div>');
5964
5965 if ($textfordropdown) {
5966 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5967 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5968 jQuery(document).ready(function() {
5969 jQuery(".'.$class.'text").click(function() {
5970 console.log("toggle text");
5971 jQuery(".'.$class.'").toggle();
5972 });
5973 });
5974 </script>';
5975
5976 $result = $tmpresult.$result;
5977 }
5978 }
5979
5980 return $result;
5981}
5982
5983
5995function dol_print_error($db = null, $error = '', $errors = null)
5996{
5997 global $conf, $langs, $user, $argv;
5998 global $dolibarr_main_prod;
5999
6000 $out = '';
6001 $syslog = '';
6002
6003 // If error occurs before the $lang object was loaded
6004 if (!$langs) {
6005 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6006 $langs = new Translate('', $conf);
6007 $langs->load("main");
6008 }
6009
6010 // Load translation files required by the error messages
6011 $langs->loadLangs(array('main', 'errors'));
6012
6013 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6014 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6015 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6016 $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";
6017 }
6018 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6019
6020 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6021 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6022 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6023 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6024 }
6025 if ($user instanceof User) {
6026 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6027 }
6028 if (function_exists("phpversion")) {
6029 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6030 }
6031 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6032 if (function_exists("php_uname")) {
6033 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6034 }
6035 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6036 $out .= "<br>\n";
6037 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
6038 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
6039 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
6040 $out .= "<br>\n";
6041 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
6042 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
6043 } else { // Mode CLI
6044 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6045 $syslog .= "pid=".dol_getmypid();
6046 }
6047
6048 if (!empty($conf->modules)) {
6049 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6050 }
6051
6052 if (is_object($db)) {
6053 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6054 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6055 $lastqueryerror = $db->lastqueryerror();
6056 if (!utf8_check($lastqueryerror)) {
6057 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6058 }
6059 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6060 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6061 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6062 $out .= "<br>\n";
6063 } else { // Mode CLI
6064 // No dol_escape_htmltag for output, we are in CLI mode
6065 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6066 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6067 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6068 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6069 }
6070 $syslog .= ", sql=".$db->lastquery();
6071 $syslog .= ", db_error=".$db->lasterror();
6072 }
6073
6074 if ($error || $errors) {
6075 // Merge all into $errors array
6076 if (is_array($error) && is_array($errors)) {
6077 $errors = array_merge($error, $errors);
6078 } elseif (is_array($error)) { // deprecated, use second parameters
6079 $errors = $error;
6080 } elseif (is_array($errors) && !empty($error)) {
6081 $errors = array_merge(array($error), $errors);
6082 } elseif (!empty($error)) {
6083 $errors = array_merge(array($error), array($errors));
6084 }
6085
6086 $langs->load("errors");
6087
6088 foreach ($errors as $msg) {
6089 if (empty($msg)) {
6090 continue;
6091 }
6092 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6093 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6094 } else { // Mode CLI
6095 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6096 }
6097 $syslog .= ", msg=".$msg;
6098 }
6099 }
6100 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6101 xdebug_print_function_stack();
6102 $out .= '<b>XDebug information:</b>'."<br>\n";
6103 $out .= 'File: '.xdebug_call_file()."<br>\n";
6104 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6105 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6106 $out .= "<br>\n";
6107 }
6108
6109 // Return a http header with error code if possible
6110 if (!headers_sent()) {
6111 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6112 top_httphead();
6113 }
6114 //http_response_code(500); // If we use 500, message is not output with some command line tools
6115 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
6116 }
6117
6118 if (empty($dolibarr_main_prod)) {
6119 print $out;
6120 } else {
6121 if (empty($langs->defaultlang)) {
6122 $langs->setDefaultLang();
6123 }
6124 $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.
6125 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6126 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";
6127 print $langs->trans("DolibarrHasDetectedError").'. ';
6128 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6129 if (!defined("MAIN_CORE_ERROR")) {
6130 define("MAIN_CORE_ERROR", 1);
6131 }
6132 }
6133
6134 dol_syslog("Error ".$syslog, LOG_ERR);
6135}
6136
6147function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6148{
6149 global $langs;
6150
6151 if (empty($email)) {
6152 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6153 }
6154
6155 $langs->load("errors");
6156 $now = dol_now();
6157
6158 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6159 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6160 if ($errormessage) {
6161 print '<br><br>'.$errormessage;
6162 }
6163 if (is_array($errormessages) && count($errormessages)) {
6164 foreach ($errormessages as $mesgtoshow) {
6165 print '<br><br>'.$mesgtoshow;
6166 }
6167 }
6168 print '</div></div>';
6169}
6170
6187function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6188{
6189 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6190}
6191
6210function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6211{
6212 global $langs, $form;
6213 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6214
6215 if ($moreattrib == 'class="right"') {
6216 $prefix .= 'right '; // For backward compatibility
6217 }
6218
6219 $sortorder = strtoupper($sortorder);
6220 $out = '';
6221 $sortimg = '';
6222
6223 $tag = 'th';
6224 if ($thead == 2) {
6225 $tag = 'div';
6226 }
6227
6228 $tmpsortfield = explode(',', $sortfield);
6229 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6230 $tmpfield = explode(',', $field);
6231 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6232
6233 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6234 $prefix = 'wrapcolumntitle '.$prefix;
6235 }
6236
6237 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6238 // If field is used as sort criteria we use a specific css class liste_titre_sel
6239 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6240 $liste_titre = 'liste_titre';
6241 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6242 $liste_titre = 'liste_titre_sel';
6243 }
6244
6245 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6246 //$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)).'"' : '');
6247 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6248 $tagstart .= '>';
6249
6250 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6251 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6252 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6253 $options = preg_replace('/&+/i', '&', $options);
6254 if (!preg_match('/^&/', $options)) {
6255 $options = '&'.$options;
6256 }
6257
6258 $sortordertouseinlink = '';
6259 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6260 if (preg_match('/^DESC/i', $sortorder)) {
6261 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6262 } else { // We reverse the var $sortordertouseinlink
6263 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6264 }
6265 } else { // We are on field that is the first current sorting criteria
6266 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6267 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6268 } else {
6269 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6270 }
6271 }
6272 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6273 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6274 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6275 $out .= '>';
6276 }
6277 if ($tooltip) {
6278 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6279 if (preg_match('/:\w+$/', $tooltip)) {
6280 $tmptooltip = explode(':', $tooltip);
6281 } else {
6282 $tmptooltip = array($tooltip);
6283 }
6284 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6285 } else {
6286 $out .= $langs->trans($name);
6287 }
6288
6289 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6290 $out .= '</a>';
6291 }
6292
6293 if (empty($thead) && $field) { // If this is a sort field
6294 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6295 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6296 $options = preg_replace('/&+/i', '&', $options);
6297 if (!preg_match('/^&/', $options)) {
6298 $options = '&'.$options;
6299 }
6300
6301 if (!$sortorder || ($field1 != $sortfield1)) {
6302 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6303 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6304 } else {
6305 if (preg_match('/^DESC/', $sortorder)) {
6306 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6307 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6308 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6309 }
6310 if (preg_match('/^ASC/', $sortorder)) {
6311 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6312 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6313 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6314 }
6315 }
6316 }
6317
6318 $tagend = '</'.$tag.'>';
6319
6320 $out = $tagstart.$sortimg.$out.$tagend;
6321
6322 return $out;
6323}
6324
6333function print_titre($title)
6334{
6335 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6336
6337 print '<div class="titre">'.$title.'</div>';
6338}
6339
6351function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6352{
6353 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6354}
6355
6369function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6370{
6371 $return = '';
6372
6373 if ($picto == 'setup') {
6374 $picto = 'generic';
6375 }
6376
6377 $return .= "\n";
6378 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6379 $return .= '<tr class="toptitle">';
6380 if ($picto) {
6381 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6382 }
6383 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6384 $return .= '<div class="titre inline-block">';
6385 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6386 $return .= '</div>';
6387 $return .= '</td>';
6388 if (dol_strlen($morehtmlcenter)) {
6389 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6390 }
6391 if (dol_strlen($morehtmlright)) {
6392 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6393 }
6394 $return .= '</tr></table>'."\n";
6395
6396 return $return;
6397}
6398
6422function 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 = '')
6423{
6424 global $conf, $langs;
6425
6426 $savlimit = $limit;
6427 $savtotalnboflines = $totalnboflines;
6428 if (is_numeric($totalnboflines)) {
6429 $totalnboflines = abs($totalnboflines);
6430 }
6431
6432 // Detect if there is a subtitle
6433 $subtitle = '';
6434 $tmparray = preg_split('/<br>/i', $title, 2);
6435 if (!empty($tmparray[1])) {
6436 $title = $tmparray[0];
6437 $subtitle = $tmparray[1];
6438 }
6439
6440 $page = (int) $page;
6441
6442 if ($picto == 'setup') {
6443 $picto = 'title_setup.png';
6444 }
6445 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6446 $picto = 'title.gif';
6447 }
6448 if ($limit < 0) {
6449 $limit = $conf->liste_limit;
6450 }
6451
6452 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6453 $nextpage = 1;
6454 } else {
6455 $nextpage = 0;
6456 }
6457 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6458
6459 print "\n";
6460 print "<!-- Begin print_barre_liste -->\n";
6461 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6462 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6463
6464 // Left
6465
6466 if ($picto && $title) {
6467 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6468 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6469 print '</td>';
6470 }
6471
6472 print '<td class="nobordernopadding valignmiddle col-title">';
6473 print '<div class="titre inline-block">';
6474 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()
6475 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6476 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6477 }
6478 print '</div>';
6479 if (!empty($subtitle)) {
6480 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6481 }
6482 print '</td>';
6483
6484 // Center
6485 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6486 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6487 }
6488
6489 // Right
6490 print '<td class="nobordernopadding valignmiddle right col-right">';
6491 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6492 if ($sortfield) {
6493 $options .= "&sortfield=".urlencode($sortfield);
6494 }
6495 if ($sortorder) {
6496 $options .= "&sortorder=".urlencode($sortorder);
6497 }
6498 // Show navigation bar
6499 $pagelist = '';
6500 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6501 if ($totalnboflines) { // If we know total nb of lines
6502 // Define nb of extra page links before and after selected page + ... + first or last
6503 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6504
6505 if ($limit > 0) {
6506 $nbpages = ceil($totalnboflines / $limit);
6507 } else {
6508 $nbpages = 1;
6509 }
6510 $cpt = ($page - $maxnbofpage);
6511 if ($cpt < 0) {
6512 $cpt = 0;
6513 }
6514
6515 if ($cpt >= 1) {
6516 if (empty($pagenavastextinput)) {
6517 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6518 if ($cpt > 2) {
6519 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6520 } elseif ($cpt == 2) {
6521 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6522 }
6523 }
6524 }
6525
6526 do {
6527 if ($pagenavastextinput) {
6528 if ($cpt == $page) {
6529 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6530 $pagelist .= '/';
6531 }
6532 } else {
6533 if ($cpt == $page) {
6534 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6535 } else {
6536 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6537 }
6538 }
6539 $cpt++;
6540 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6541
6542 if (empty($pagenavastextinput)) {
6543 if ($cpt < $nbpages) {
6544 if ($cpt < $nbpages - 2) {
6545 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6546 } elseif ($cpt == $nbpages - 2) {
6547 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6548 }
6549 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6550 }
6551 } else {
6552 //var_dump($page.' '.$cpt.' '.$nbpages);
6553 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6554 }
6555 } else {
6556 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6557 }
6558 }
6559
6560 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6561 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
6562 }
6563
6564 // js to autoselect page field on focus
6565 if ($pagenavastextinput) {
6566 print ajax_autoselect('.pageplusone');
6567 }
6568
6569 print '</td>';
6570 print '</tr>';
6571
6572 print '</table>'."\n";
6573
6574 // Center
6575 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6576 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6577 }
6578
6579 print "<!-- End title -->\n\n";
6580}
6581
6598function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6599{
6600 global $conf, $langs;
6601
6602 print '<div class="pagination"><ul>';
6603 if ($beforearrows) {
6604 print '<li class="paginationbeforearrows">';
6605 print $beforearrows;
6606 print '</li>';
6607 }
6608
6609 if (empty($hidenavigation)) {
6610 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6611 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6612 $pagesizechoices .= ',5000:5000';
6613 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6614 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6615 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6616 //$pagesizechoices .= ',2:2';
6617 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6618 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6619 }
6620
6621 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6622 print '<li class="pagination">';
6623 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.'">';
6624 print '<datalist id="limitlist">';
6625 } else {
6626 print '<li class="paginationcombolimit valignmiddle">';
6627 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")).'">';
6628 }
6629 $tmpchoice = explode(',', $pagesizechoices);
6630 $tmpkey = $limit.':'.$limit;
6631 if (!in_array($tmpkey, $tmpchoice)) {
6632 $tmpchoice[$tmpkey] = $tmpkey;
6633 }
6634 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6635 if (!in_array($tmpkey, $tmpchoice)) {
6636 $tmpchoice[$tmpkey] = $tmpkey;
6637 }
6638 asort($tmpchoice, SORT_NUMERIC);
6639 foreach ($tmpchoice as $val) {
6640 $selected = '';
6641 $tmp = explode(':', $val);
6642 $key = $tmp[0];
6643 $val = $tmp[1];
6644 if ($key != '' && $val != '') {
6645 if ((int) $key == (int) $limit) {
6646 $selected = ' selected="selected"';
6647 }
6648 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6649 }
6650 }
6651 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6652 print '</datalist>';
6653 } else {
6654 print '</select>';
6655 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
6656 //print ajax_combobox("limit");
6657 }
6658
6659 if ($conf->use_javascript_ajax) {
6660 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6661 <script>
6662 jQuery(document).ready(function () {
6663 jQuery(".selectlimit").change(function() {
6664 console.log("We change limit so we submit the form");
6665 $(this).parents(\'form:first\').submit();
6666 });
6667 });
6668 </script>
6669 ';
6670 }
6671 print '</li>';
6672 }
6673 if ($page > 0) {
6674 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>';
6675 }
6676 if ($betweenarrows) {
6677 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6678 print $betweenarrows;
6679 print '<!--</div>-->';
6680 }
6681 if ($nextpage > 0) {
6682 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>';
6683 }
6684 if ($afterarrows) {
6685 print '<li class="paginationafterarrows">';
6686 print $afterarrows;
6687 print '</li>';
6688 }
6689 }
6690 print '</ul></div>'."\n";
6691}
6692
6693
6705function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6706{
6707 $morelabel = '';
6708
6709 if (preg_match('/%/', $rate)) {
6710 $rate = str_replace('%', '', $rate);
6711 $addpercent = true;
6712 }
6713 $reg = array();
6714 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6715 $morelabel = ' ('.$reg[1].')';
6716 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6717 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6718 }
6719 if (preg_match('/\*/', $rate)) {
6720 $rate = str_replace('*', '', $rate);
6721 $info_bits |= 1;
6722 }
6723
6724 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6725 if (!preg_match('/\//', $rate)) {
6726 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6727 } else {
6728 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6729 $ret = $rate.($addpercent ? '%' : '');
6730 }
6731 if (($info_bits & 1) && $usestarfornpr >= 0) {
6732 $ret .= ' *';
6733 }
6734 $ret .= $morelabel;
6735 return $ret;
6736}
6737
6738
6754function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6755{
6756 global $langs, $conf;
6757
6758 // Clean parameters
6759 if (empty($amount)) {
6760 $amount = 0; // To have a numeric value if amount not defined or = ''
6761 }
6762 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6763 if ($rounding == -1) {
6764 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6765 }
6766 $nbdecimal = $rounding;
6767
6768 if ($outlangs === 'none') {
6769 // Use international separators
6770 $dec = '.';
6771 $thousand = '';
6772 } else {
6773 // Output separators by default (french)
6774 $dec = ',';
6775 $thousand = ' ';
6776
6777 // If $outlangs not forced, we use use language
6778 if (!($outlangs instanceof Translate)) {
6779 $outlangs = $langs;
6780 }
6781
6782 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6783 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6784 }
6785 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6786 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6787 }
6788 if ($thousand == 'None') {
6789 $thousand = '';
6790 } elseif ($thousand == 'Space') {
6791 $thousand = ' ';
6792 }
6793 }
6794 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6795
6796 //print "amount=".$amount."-";
6797 $amount = str_replace(',', '.', $amount); // should be useless
6798 //print $amount."-";
6799 $data = explode('.', $amount);
6800 $decpart = isset($data[1]) ? $data[1] : '';
6801 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6802 //print "decpart=".$decpart."<br>";
6803 $end = '';
6804
6805 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6806 if (dol_strlen($decpart) > $nbdecimal) {
6807 $nbdecimal = dol_strlen($decpart);
6808 }
6809
6810 // If nbdecimal is higher than max to show
6811 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6812 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6813 $nbdecimal = $nbdecimalmaxshown;
6814 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6815 // If output is truncated, we show ...
6816 $end = '...';
6817 }
6818 }
6819
6820 // If force rounding
6821 if ((string) $forcerounding != '-1') {
6822 if ($forcerounding === 'MU') {
6823 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6824 } elseif ($forcerounding === 'MT') {
6825 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
6826 } elseif ($forcerounding >= 0) {
6827 $nbdecimal = $forcerounding;
6828 }
6829 }
6830
6831 // Format number
6832 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6833 if ($form) {
6834 $output = preg_replace('/\s/', '&nbsp;', $output);
6835 $output = preg_replace('/\'/', '&#039;', $output);
6836 }
6837 // Add symbol of currency if requested
6838 $cursymbolbefore = $cursymbolafter = '';
6839 if ($currency_code && is_object($outlangs)) {
6840 if ($currency_code == 'auto') {
6841 $currency_code = $conf->currency;
6842 }
6843
6844 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6845 $listoflanguagesbefore = array('nl_NL');
6846 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6847 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6848 } else {
6849 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6850 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6851 }
6852 }
6853 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6854
6855 return $output;
6856}
6857
6882function price2num($amount, $rounding = '', $option = 0)
6883{
6884 global $langs, $conf;
6885
6886 // Clean parameters
6887 if (is_null($amount)) {
6888 $amount = '';
6889 }
6890
6891 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6892 // Numbers must be '1234.56'
6893 // Decimal delimiter for PHP and database SQL requests must be '.'
6894 $dec = ',';
6895 $thousand = ' ';
6896 if (is_null($langs)) { // $langs is not defined, we use english values.
6897 $dec = '.';
6898 $thousand = ',';
6899 } else {
6900 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6901 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6902 }
6903 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6904 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6905 }
6906 }
6907 if ($thousand == 'None') {
6908 $thousand = '';
6909 } elseif ($thousand == 'Space') {
6910 $thousand = ' ';
6911 }
6912 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6913
6914 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6915 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6916 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6917 if (!is_numeric($amount)) {
6918 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6919 }
6920
6921 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
6922 $amount = str_replace($thousand, '', $amount);
6923 }
6924
6925 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6926 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6927 // So if number was already a good number, it is converted into local Dolibarr setup.
6928 if (is_numeric($amount)) {
6929 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6930 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6931 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6932 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6933 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6934 }
6935 //print "QQ".$amount."<br>\n";
6936
6937 // Now make replace (the main goal of function)
6938 if ($thousand != ',' && $thousand != '.') {
6939 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6940 }
6941
6942 $amount = str_replace(' ', '', $amount); // To avoid spaces
6943 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6944 $amount = str_replace($dec, '.', $amount);
6945
6946 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6947 }
6948 //print ' XX'.$amount.' '.$rounding;
6949
6950 // Now, $amount is a real PHP float number. We make a rounding if required.
6951 if ($rounding) {
6952 $nbofdectoround = '';
6953 if ($rounding == 'MU') {
6954 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6955 } elseif ($rounding == 'MT') {
6956 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6957 } elseif ($rounding == 'MS') {
6958 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6959 } elseif ($rounding == 'CU') {
6960 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6961 } elseif ($rounding == 'CT') {
6962 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6963 } elseif (is_numeric($rounding)) {
6964 $nbofdectoround = (int) $rounding;
6965 }
6966
6967 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6968 if (dol_strlen($nbofdectoround)) {
6969 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6970 } else {
6971 return 'ErrorBadParameterProvidedToFunction';
6972 }
6973 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6974
6975 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6976 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6977 if (is_numeric($amount)) {
6978 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6979 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6980 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6981 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6982 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6983 }
6984 //print "TT".$amount.'<br>';
6985
6986 // Always make replace because each math function (like round) replace
6987 // with local values and we want a number that has a SQL string format x.y
6988 if ($thousand != ',' && $thousand != '.') {
6989 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6990 }
6991
6992 $amount = str_replace(' ', '', $amount); // To avoid spaces
6993 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6994 $amount = str_replace($dec, '.', $amount);
6995
6996 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6997 }
6998
6999 return $amount;
7000}
7001
7014function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7015{
7016 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7017
7018 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7019 $dimension *= 1000000;
7020 $unit -= 6;
7021 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7022 $dimension *= 1000;
7023 $unit -= 3;
7024 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7025 $dimension /= 1000000;
7026 $unit += 6;
7027 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7028 $dimension /= 1000;
7029 $unit += 3;
7030 }
7031 // Special case when we want output unit into pound or ounce
7032 /* TODO
7033 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7034 {
7035 $dimension = // convert dimension from standard unit into ounce or pound
7036 $unit = $forceunitoutput;
7037 }
7038 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7039 {
7040 $dimension = // convert dimension from standard unit into ounce or pound
7041 $unit = $forceunitoutput;
7042 }*/
7043
7044 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7045 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7046 $ret .= ' '.measuringUnitString(0, $type, (string) $unit, $use_short_label, $outputlangs);
7047
7048 return $ret;
7049}
7050
7051
7064function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7065{
7066 global $db, $conf, $mysoc;
7067
7068 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7069 $thirdparty_seller = $mysoc;
7070 }
7071
7072 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);
7073
7074 $vatratecleaned = $vatrate;
7075 $reg = array();
7076 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7077 $vatratecleaned = trim($reg[1]);
7078 $vatratecode = $reg[2];
7079 }
7080
7081 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7082 {
7083 return 0;
7084 }*/
7085
7086 // Some test to guess with no need to make database access
7087 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7088 if ($local == 1) {
7089 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7090 return 0;
7091 }
7092 if ($thirdparty_seller->id == $mysoc->id) {
7093 if (!$thirdparty_buyer->localtax1_assuj) {
7094 return 0;
7095 }
7096 } else {
7097 if (!$thirdparty_seller->localtax1_assuj) {
7098 return 0;
7099 }
7100 }
7101 }
7102
7103 if ($local == 2) {
7104 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7105 if (!$mysoc->localtax2_assuj) {
7106 return 0; // If main vat is 0, IRPF may be different than 0.
7107 }
7108 if ($thirdparty_seller->id == $mysoc->id) {
7109 if (!$thirdparty_buyer->localtax2_assuj) {
7110 return 0;
7111 }
7112 } else {
7113 if (!$thirdparty_seller->localtax2_assuj) {
7114 return 0;
7115 }
7116 }
7117 }
7118 } else {
7119 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7120 return 0;
7121 }
7122 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7123 return 0;
7124 }
7125 }
7126
7127 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7128 if (in_array($mysoc->country_code, array('ES'))) {
7129 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7130 }
7131
7132 // Search local taxes
7133 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7134 if ($local == 1) {
7135 if ($thirdparty_seller != $mysoc) {
7136 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7137 return $thirdparty_seller->localtax1_value;
7138 }
7139 } else { // i am the seller
7140 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7141 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7142 }
7143 }
7144 }
7145 if ($local == 2) {
7146 if ($thirdparty_seller != $mysoc) {
7147 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7148 // TODO We should also return value defined on thirdparty only if defined
7149 return $thirdparty_seller->localtax2_value;
7150 }
7151 } else { // i am the seller
7152 if (in_array($mysoc->country_code, array('ES'))) {
7153 return $thirdparty_buyer->localtax2_value;
7154 } else {
7155 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7156 }
7157 }
7158 }
7159 }
7160
7161 // By default, search value of local tax on line of common tax
7162 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7163 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7164 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7165 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7166 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7167 if (!empty($vatratecode)) {
7168 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7169 } else {
7170 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7171 }
7172
7173 $resql = $db->query($sql);
7174
7175 if ($resql) {
7176 $obj = $db->fetch_object($resql);
7177 if ($obj) {
7178 if ($local == 1) {
7179 return $obj->localtax1;
7180 } elseif ($local == 2) {
7181 return $obj->localtax2;
7182 }
7183 }
7184 }
7185
7186 return 0;
7187}
7188
7189
7198function isOnlyOneLocalTax($local)
7199{
7200 $tax = get_localtax_by_third($local);
7201
7202 $valors = explode(":", $tax);
7203
7204 if (count($valors) > 1) {
7205 return false;
7206 } else {
7207 return true;
7208 }
7209}
7210
7217function get_localtax_by_third($local)
7218{
7219 global $db, $mysoc;
7220
7221 $sql = " SELECT t.localtax".$local." as localtax";
7222 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7223 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7224 $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";
7225 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7226 $sql .= " AND t.localtax".$local."_type <> '0'";
7227 $sql .= " ORDER BY t.rowid DESC";
7228
7229 $resql = $db->query($sql);
7230 if ($resql) {
7231 $obj = $db->fetch_object($resql);
7232 if ($obj) {
7233 return $obj->localtax;
7234 } else {
7235 return '0';
7236 }
7237 }
7238
7239 return 'Error';
7240}
7241
7242
7254function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7255{
7256 global $db;
7257
7258 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7259
7260 // Search local taxes
7261 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7262 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7263 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7264 if ($firstparamisid) {
7265 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7266 } else {
7267 $vatratecleaned = $vatrate;
7268 $vatratecode = '';
7269 $reg = array();
7270 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7271 $vatratecleaned = $reg[1];
7272 $vatratecode = $reg[2];
7273 }
7274
7275 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7276 /*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 ??
7277 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7278 $sql .= " WHERE t.fk_pays = c.rowid";
7279 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7280 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7281 } else {
7282 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7283 }
7284 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7285 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7286 if ($vatratecode) {
7287 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7288 }
7289 }
7290
7291 $resql = $db->query($sql);
7292 if ($resql) {
7293 $obj = $db->fetch_object($resql);
7294 if ($obj) {
7295 return array(
7296 'rowid' => $obj->rowid,
7297 'code' => $obj->code,
7298 'rate' => $obj->rate,
7299 'localtax1' => $obj->localtax1,
7300 'localtax1_type' => $obj->localtax1_type,
7301 'localtax2' => $obj->localtax2,
7302 'localtax2_type' => $obj->localtax2_type,
7303 'npr' => $obj->npr,
7304 'accountancy_code_sell' => $obj->accountancy_code_sell,
7305 'accountancy_code_buy' => $obj->accountancy_code_buy
7306 );
7307 } else {
7308 return array();
7309 }
7310 } else {
7311 dol_print_error($db);
7312 }
7313
7314 return array();
7315}
7316
7333function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7334{
7335 global $db, $mysoc;
7336
7337 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7338
7339 // Search local taxes
7340 $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";
7341 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7342 if ($firstparamisid) {
7343 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7344 } else {
7345 $vatratecleaned = $vatrate;
7346 $vatratecode = '';
7347 $reg = array();
7348 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7349 $vatratecleaned = $reg[1];
7350 $vatratecode = $reg[2];
7351 }
7352
7353 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7354 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7355 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7356 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7357 } else {
7358 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7359 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7360 }
7361 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7362 if ($vatratecode) {
7363 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7364 }
7365 }
7366
7367 $resql = $db->query($sql);
7368 if ($resql) {
7369 $obj = $db->fetch_object($resql);
7370
7371 if ($obj) {
7372 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7373
7374 if ($local == 1) {
7375 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7376 } elseif ($local == 2) {
7377 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7378 } else {
7379 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);
7380 }
7381 }
7382 }
7383
7384 return array();
7385}
7386
7397function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7398{
7399 global $db, $mysoc;
7400
7401 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7402
7403 $ret = 0;
7404 $found = 0;
7405
7406 if ($idprod > 0) {
7407 // Load product
7408 $product = new Product($db);
7409 $product->fetch($idprod);
7410
7411 if (($mysoc->country_code == $thirdpartytouse->country_code)
7412 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7413 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7414 ) {
7415 // If country of thirdparty to consider is ours
7416 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7417 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7418 if ($result > 0) {
7419 $ret = $product->vatrate_supplier;
7420 if ($product->default_vat_code_supplier) {
7421 $ret .= ' ('.$product->default_vat_code_supplier.')';
7422 }
7423 $found = 1;
7424 }
7425 }
7426 if (!$found) {
7427 $ret = $product->tva_tx; // Default sales vat of product
7428 if ($product->default_vat_code) {
7429 $ret .= ' ('.$product->default_vat_code.')';
7430 }
7431 $found = 1;
7432 }
7433 } else {
7434 // TODO Read default product vat according to product and an other countrycode.
7435 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7436 }
7437 }
7438
7439 if (!$found) {
7440 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7441 // 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).
7442 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7443 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7444 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7445 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7446 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7447 $sql .= $db->plimit(1);
7448
7449 $resql = $db->query($sql);
7450 if ($resql) {
7451 $obj = $db->fetch_object($resql);
7452 if ($obj) {
7453 $ret = $obj->vat_rate;
7454 if ($obj->default_vat_code) {
7455 $ret .= ' ('.$obj->default_vat_code.')';
7456 }
7457 }
7458 $db->free($resql);
7459 } else {
7460 dol_print_error($db);
7461 }
7462 } else {
7463 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7464 // '1.23'
7465 // or '1.23 (CODE)'
7466 $defaulttx = '';
7467 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7468 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7469 }
7470 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7471 $defaultcode = $reg[1];
7472 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7473 }*/
7474
7475 $ret = $defaulttx;
7476 }
7477 }
7478
7479 dol_syslog("get_product_vat_for_country: ret=".$ret);
7480 return $ret;
7481}
7482
7492function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7493{
7494 global $db, $mysoc;
7495
7496 if (!class_exists('Product')) {
7497 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7498 }
7499
7500 $ret = 0;
7501 $found = 0;
7502
7503 if ($idprod > 0) {
7504 // Load product
7505 $product = new Product($db);
7506 $result = $product->fetch($idprod);
7507
7508 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7509 /* Not defined yet, so we don't use this
7510 if ($local==1) $ret=$product->localtax1_tx;
7511 elseif ($local==2) $ret=$product->localtax2_tx;
7512 $found=1;
7513 */
7514 } else {
7515 // TODO Read default product vat according to product and another countrycode.
7516 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7517 }
7518 }
7519
7520 if (!$found) {
7521 // If vat of product for the country not found or not defined, we return higher vat of country.
7522 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7523 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7524 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7525 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7526 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7527 $sql .= $db->plimit(1);
7528
7529 $resql = $db->query($sql);
7530 if ($resql) {
7531 $obj = $db->fetch_object($resql);
7532 if ($obj) {
7533 if ($local == 1) {
7534 $ret = $obj->localtax1;
7535 } elseif ($local == 2) {
7536 $ret = $obj->localtax2;
7537 }
7538 }
7539 } else {
7540 dol_print_error($db);
7541 }
7542 }
7543
7544 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7545 return $ret;
7546}
7547
7565function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7566{
7567 global $conf, $db;
7568
7569 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7570
7571 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7572 $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;
7573
7574 $seller_country_code = $thirdparty_seller->country_code;
7575 $seller_in_cee = isInEEC($thirdparty_seller);
7576
7577 $buyer_country_code = $thirdparty_buyer->country_code;
7578 $buyer_in_cee = isInEEC($thirdparty_buyer);
7579
7580 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 in cee=".((string) (int) $buyer_in_cee).", idprod=".$idprod.", idprodfournprice=".$idprodfournprice.", SERVICE_ARE_ECOMMERCE_200238EC=".(getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC') ? $conf->global->SERVICE_ARE_ECOMMERCE_200238EC : ''));
7581
7582 // 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)
7583 // we use the buyer VAT.
7584 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7585 if ($seller_in_cee && $buyer_in_cee) {
7586 $isacompany = $thirdparty_buyer->isACompany();
7587 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7588 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7589 if (!isValidVATID($thirdparty_buyer)) {
7590 $isacompany = 0;
7591 }
7592 }
7593
7594 if (!$isacompany) {
7595 //print 'VATRULE 0';
7596 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7597 }
7598 }
7599 }
7600
7601 // If seller does not use VAT, default VAT is 0. End of rule.
7602 if (!$seller_use_vat) {
7603 //print 'VATRULE 1';
7604 return 0;
7605 }
7606
7607 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
7608 if (!empty($thirdparty_buyer->state_id)) {
7609 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
7610 $sql .= " FROM ".$db->prefix()."c_tva as t";
7611 $sql .= " INNER JOIN ".$db->prefix()."c_departements as d ON t.fk_department_buyer = d.rowid";
7612 $sql .= " WHERE d.rowid = ".((int) $thirdparty_buyer->state_id);
7613 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7614
7615 $res = $db->query($sql);
7616 if ($res) {
7617 if ($db->num_rows($res)) {
7618 $obj = $db->fetch_object($res);
7619 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7620 }
7621 $db->free($res);
7622 }
7623 }
7624
7625 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7626 if (($seller_country_code == $buyer_country_code)
7627 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7628 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7629 ) { // Warning ->country_code not always defined
7630 //print 'VATRULE 3';
7631 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7632
7633 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7634 // Special case for india.
7635 //print 'VATRULE 3b';
7636 $reg = array();
7637 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7638 // we must revert the C+S into I
7639 $tmpvat = str_replace("C+S", "I", $tmpvat);
7640 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7641 // we must revert the I into C+S
7642 $tmpvat = str_replace("I", "C+S", $tmpvat);
7643 }
7644 }
7645
7646 return $tmpvat;
7647 }
7648
7649 // 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.
7650 // 'VATRULE 4' - Not supported
7651
7652 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7653 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7654 if (($seller_in_cee && $buyer_in_cee)) {
7655 $isacompany = $thirdparty_buyer->isACompany();
7656 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7657 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7658 if (!isValidVATID($thirdparty_buyer)) {
7659 $isacompany = 0;
7660 }
7661 }
7662
7663 if (!$isacompany) {
7664 //print 'VATRULE 5';
7665 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7666 } else {
7667 //print 'VATRULE 6';
7668 return 0;
7669 }
7670 }
7671
7672 // 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
7673 // I don't see any use case that need this rule.
7674 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7675 $isacompany = $thirdparty_buyer->isACompany();
7676 if (!$isacompany) {
7677 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7678 //print 'VATRULE extra';
7679 }
7680 }
7681
7682 // Otherwise the VAT proposed by default=0. End of rule.
7683 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7684 //print 'VATRULE 7';
7685 return 0;
7686}
7687
7688
7699function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7700{
7701 global $db;
7702
7703 if ($idprodfournprice > 0) {
7704 if (!class_exists('ProductFournisseur')) {
7705 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7706 }
7707 $prodprice = new ProductFournisseur($db);
7708 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7709 return $prodprice->fourn_tva_npr;
7710 } elseif ($idprod > 0) {
7711 if (!class_exists('Product')) {
7712 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7713 }
7714 $prod = new Product($db);
7715 $prod->fetch($idprod);
7716 return $prod->tva_npr;
7717 }
7718
7719 return 0;
7720}
7721
7735function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7736{
7737 global $mysoc;
7738
7739 if (!is_object($thirdparty_seller)) {
7740 return -1;
7741 }
7742 if (!is_object($thirdparty_buyer)) {
7743 return -1;
7744 }
7745
7746 if ($local == 1) { // Localtax 1
7747 if ($mysoc->country_code == 'ES') {
7748 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7749 return 0;
7750 }
7751 } else {
7752 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7753 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7754 return 0;
7755 }
7756 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7757 return 0;
7758 }
7759 }
7760 } elseif ($local == 2) { //I Localtax 2
7761 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7762 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7763 return 0;
7764 }
7765 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7766 return 0;
7767 }
7768 }
7769
7770 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7771 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7772 }
7773
7774 return 0;
7775}
7776
7785function yn($yesno, $format = 1, $color = 0)
7786{
7787 global $langs;
7788
7789 $result = 'unknown';
7790 $classname = '';
7791 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7792 $result = $langs->trans('yes');
7793 if ($format == 1 || $format == 3) {
7794 $result = $langs->trans("Yes");
7795 }
7796 if ($format == 2) {
7797 $result = '<input type="checkbox" value="1" checked disabled>';
7798 }
7799 if ($format == 3) {
7800 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7801 }
7802 if ($format == 4 || !is_numeric($format)) {
7803 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
7804 }
7805
7806 $classname = 'ok';
7807 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7808 $result = $langs->trans("no");
7809 if ($format == 1 || $format == 3) {
7810 $result = $langs->trans("No");
7811 }
7812 if ($format == 2) {
7813 $result = '<input type="checkbox" value="0" disabled>';
7814 }
7815 if ($format == 3) {
7816 $result = '<input type="checkbox" value="0" disabled> '.$result;
7817 }
7818 if ($format == 4 || !is_numeric($format)) {
7819 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
7820 }
7821
7822 if ($color == 2) {
7823 $classname = 'ok';
7824 } else {
7825 $classname = 'error';
7826 }
7827 }
7828 if ($color) {
7829 return '<span class="'.$classname.'">'.$result.'</span>';
7830 }
7831 return $result;
7832}
7833
7852function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7853{
7854 if (empty($modulepart) && is_object($object)) {
7855 if (!empty($object->module)) {
7856 $modulepart = $object->module;
7857 } elseif (!empty($object->element)) {
7858 $modulepart = $object->element;
7859 }
7860 }
7861
7862 $path = '';
7863
7864 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7865 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7866 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7867 $arrayforoldpath['product'] = 2;
7868 }
7869
7870 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7871 $level = $arrayforoldpath[$modulepart];
7872 }
7873
7874 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7875 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7876 if (empty($num) && is_object($object)) {
7877 $num = $object->id;
7878 }
7879 if (empty($alpha)) {
7880 $num = preg_replace('/([^0-9])/i', '', $num);
7881 } else {
7882 $num = preg_replace('/^.*\-/i', '', $num);
7883 }
7884 $num = substr("000".$num, -$level);
7885 if ($level == 1) {
7886 $path = substr($num, 0, 1);
7887 }
7888 if ($level == 2) {
7889 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7890 }
7891 if ($level == 3) {
7892 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7893 }
7894 } else {
7895 // We will enhance here a common way of forging path for document storage.
7896 // In a future, we may distribute directories on several levels depending on setup and object.
7897 // Here, $object->id, $object->ref and $modulepart are required.
7898 //var_dump($modulepart);
7899 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7900 }
7901
7902 if (empty($withoutslash) && !empty($path)) {
7903 $path .= '/';
7904 }
7905
7906 return $path;
7907}
7908
7917function dol_mkdir($dir, $dataroot = '', $newmask = '')
7918{
7919 global $conf;
7920
7921 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7922
7923 $dir_osencoded = dol_osencode($dir);
7924 if (@is_dir($dir_osencoded)) {
7925 return 0;
7926 }
7927
7928 $nberr = 0;
7929 $nbcreated = 0;
7930
7931 $ccdir = '';
7932 if (!empty($dataroot)) {
7933 // Remove data root from loop
7934 $dir = str_replace($dataroot.'/', '', $dir);
7935 $ccdir = $dataroot.'/';
7936 }
7937
7938 $cdir = explode("/", $dir);
7939 $num = count($cdir);
7940 for ($i = 0; $i < $num; $i++) {
7941 if ($i > 0) {
7942 $ccdir .= '/'.$cdir[$i];
7943 } else {
7944 $ccdir .= $cdir[$i];
7945 }
7946 $regs = array();
7947 if (preg_match("/^.:$/", $ccdir, $regs)) {
7948 continue; // If the Windows path is incomplete, continue with next directory
7949 }
7950
7951 // Attention, is_dir() can fail event if the directory exists
7952 // (i.e. according the open_basedir configuration)
7953 if ($ccdir) {
7954 $ccdir_osencoded = dol_osencode($ccdir);
7955 if (!@is_dir($ccdir_osencoded)) {
7956 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7957
7958 umask(0);
7959 $dirmaskdec = octdec((string) $newmask);
7960 if (empty($newmask)) {
7961 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7962 }
7963 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7964 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7965 // If the is_dir has returned a false information, we arrive here
7966 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7967 $nberr++;
7968 } else {
7969 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7970 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7971 $nbcreated++;
7972 }
7973 } else {
7974 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7975 }
7976 }
7977 }
7978 return ($nberr ? -$nberr : $nbcreated);
7979}
7980
7981
7989function dolChmod($filepath, $newmask = '')
7990{
7991 global $conf;
7992
7993 if (!empty($newmask)) {
7994 @chmod($filepath, octdec($newmask));
7995 } elseif (getDolGlobalString('MAIN_UMASK')) {
7996 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7997 }
7998}
7999
8000
8006function picto_required()
8007{
8008 return '<span class="fieldrequired">*</span>';
8009}
8010
8011
8028function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8029{
8030 if (is_null($stringtoclean)) {
8031 return '';
8032 }
8033
8034 if ($removelinefeed == 2) {
8035 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8036 }
8037 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8038
8039 // 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)
8040 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8041
8042 $temp = str_replace('< ', '__ltspace__', $temp);
8043 $temp = str_replace('<:', '__lttwopoints__', $temp);
8044
8045 if ($strip_tags) {
8046 $temp = strip_tags($temp);
8047 } else {
8048 // 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).
8049 $pattern = "/<[^<>]+>/";
8050 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8051 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8052 // pass 2 - $temp after pass 2: 0000-021
8053 $tempbis = $temp;
8054 do {
8055 $temp = $tempbis;
8056 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8057 $tempbis = preg_replace($pattern, '', $tempbis);
8058 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8059 } while ($tempbis != $temp);
8060
8061 $temp = $tempbis;
8062
8063 // 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).
8064 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8065 }
8066
8067 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8068
8069 // Remove also carriage returns
8070 if ($removelinefeed == 1) {
8071 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8072 }
8073
8074 // And double quotes
8075 if ($removedoublespaces) {
8076 while (strpos($temp, " ")) {
8077 $temp = str_replace(" ", " ", $temp);
8078 }
8079 }
8080
8081 $temp = str_replace('__ltspace__', '< ', $temp);
8082 $temp = str_replace('__lttwopoints__', '<:', $temp);
8083
8084 return trim($temp);
8085}
8086
8104function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0)
8105{
8106 if (empty($allowed_tags)) {
8107 $allowed_tags = array(
8108 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8109 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8110 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8111 );
8112 }
8113 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8114 if ($allowiframe) {
8115 if (!in_array('iframe', $allowed_tags)) {
8116 $allowed_tags[] = "iframe";
8117 }
8118 }
8119 if ($allowlink) {
8120 if (!in_array('link', $allowed_tags)) {
8121 $allowed_tags[] = "link";
8122 }
8123 }
8124 if ($allowscript) {
8125 if (!in_array('script', $allowed_tags)) {
8126 $allowed_tags[] = "script";
8127 }
8128 }
8129 if ($allowstyle) {
8130 if (!in_array('style', $allowed_tags)) {
8131 $allowed_tags[] = "style";
8132 }
8133 }
8134
8135 $allowed_tags_string = implode("><", $allowed_tags);
8136 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8137
8138 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8139
8140 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8141
8142 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8143 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8144
8145 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8146 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8147
8148 // Remove all HTML tags
8149 $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
8150
8151 if ($cleanalsosomestyles) { // Clean for remaining html tags
8152 $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
8153 }
8154 if ($removeclassattribute) { // Clean for remaining html tags
8155 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8156 }
8157
8158 // Remove 'javascript:' that we should not find into a text with
8159 // 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)).
8160 if ($cleanalsojavascript) {
8161 $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);
8162 }
8163
8164 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8165
8166 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8167
8168
8169 return $temp;
8170}
8171
8172
8185function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8186{
8187 if (is_null($allowed_attributes)) {
8188 $allowed_attributes = array(
8189 "allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width",
8190 // HTML5
8191 "header", "footer", "nav", "section", "menu", "menuitem"
8192 );
8193 }
8194
8195 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8196 $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
8197
8198 // Warning: loadHTML does not support HTML5 on old libxml versions.
8199 $dom = new DOMDocument('', 'UTF-8');
8200 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8201 $savwarning = error_reporting();
8202 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8203 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8204 error_reporting($savwarning);
8205
8206 if ($dom instanceof DOMDocument) {
8207 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8208 $el = $els->item($i);
8209 if (!$el instanceof DOMElement) {
8210 continue;
8211 }
8212 $attrs = $el->attributes;
8213 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8214 //var_dump($attrs->item($ii));
8215 if (!empty($attrs->item($ii)->name)) {
8216 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8217 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8218 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8219 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8220 // If attribute is 'style'
8221 $valuetoclean = $attrs->item($ii)->value;
8222
8223 if (isset($valuetoclean)) {
8224 do {
8225 $oldvaluetoclean = $valuetoclean;
8226 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8227 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8228 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8229 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8230 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8231 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8232 }
8233
8234 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8235 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8236 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8237 } while ($oldvaluetoclean != $valuetoclean);
8238 }
8239
8240 $attrs->item($ii)->value = $valuetoclean;
8241 }
8242 }
8243 }
8244 }
8245 }
8246
8247 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8248 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8249
8250 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8251 $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
8252 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
8253 return trim($return);
8254 } else {
8255 return $stringtoclean;
8256 }
8257}
8258
8270function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8271{
8272 $temp = $stringtoclean;
8273 foreach ($disallowed_tags as $tagtoremove) {
8274 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8275 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8276 }
8277
8278 if ($cleanalsosomestyles) {
8279 $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
8280 }
8281
8282 return $temp;
8283}
8284
8285
8295function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8296{
8297 if ($nboflines == 1) {
8298 if (dol_textishtml($text)) {
8299 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8300 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8301 } else {
8302 if (isset($text)) {
8303 $firstline = preg_replace('/[\n\r].*/', '', $text);
8304 } else {
8305 $firstline = '';
8306 }
8307 }
8308 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8309 } else {
8310 $ishtml = 0;
8311 if (dol_textishtml($text)) {
8312 $text = preg_replace('/\n/', '', $text);
8313 $ishtml = 1;
8314 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8315 } else {
8316 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8317 }
8318
8319 $text = strtr($text, $repTable);
8320 if ($charset == 'UTF-8') {
8321 $pattern = '/(<br[^>]*>)/Uu';
8322 } else {
8323 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8324 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8325 }
8326 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8327
8328 $firstline = '';
8329 $i = 0;
8330 $countline = 0;
8331 $lastaddediscontent = 1;
8332 while ($countline < $nboflines && isset($a[$i])) {
8333 if (preg_match('/<br[^>]*>/', $a[$i])) {
8334 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8335 $firstline .= ($ishtml ? "<br>\n" : "\n");
8336 // Is it a br for a new line of after a printed line ?
8337 if (!$lastaddediscontent) {
8338 $countline++;
8339 }
8340 $lastaddediscontent = 0;
8341 }
8342 } else {
8343 $firstline .= $a[$i];
8344 $lastaddediscontent = 1;
8345 $countline++;
8346 }
8347 $i++;
8348 }
8349
8350 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8351 //unset($a);
8352 $ret = $firstline.($adddots ? '...' : '');
8353 //exit;
8354 return $ret;
8355 }
8356}
8357
8358
8370function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8371{
8372 if (is_null($stringtoencode)) {
8373 return '';
8374 }
8375
8376 if (!$nl2brmode) {
8377 return nl2br($stringtoencode, $forxml);
8378 } else {
8379 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8380 return $ret;
8381 }
8382}
8383
8393function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8394{
8395 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8396 // TODO using sandbox on inline html content is not possible yet with current browsers
8397 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8398 //$s .= $stringtoencode;
8399 //$s .= '</body></html></iframe>';
8400 return $stringtoencode;
8401 } else {
8402 $out = $stringtoencode;
8403
8404 // First clean HTML content
8405 do {
8406 $oldstringtoclean = $out;
8407
8408 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8409 try {
8410 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8411 if (LIBXML_VERSION < 20900) {
8412 // Avoid load of external entities (security problem).
8413 // Required only if LIBXML_VERSION < 20900
8414 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8415 libxml_disable_entity_loader(true);
8416 }
8417
8418 $dom = new DOMDocument();
8419 // Add a trick to solve pb with text without parent tag
8420 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8421 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8422
8423 if (dol_textishtml($out)) {
8424 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.$out.'</div>';
8425 } else {
8426 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.dol_nl2br($out).'</div>';
8427 }
8428
8429 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8430 $out = trim($dom->saveHTML());
8431
8432 // Remove the trick added to solve pb with text without parent tag
8433 $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8434 $out = preg_replace('/<\/div>$/', '', $out);
8435 } catch (Exception $e) {
8436 // If error, invalid HTML string with no way to clean it
8437 //print $e->getMessage();
8438 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8439 }
8440 }
8441
8442 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript'))) {
8443 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8444 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8445 try {
8446 // Try cleaning using tidy
8447 if (extension_loaded('tidy') && class_exists("tidy")) {
8448 //print "aaa".$out."\n";
8449
8450 // See options at https://tidy.sourceforge.net/docs/quickref.html
8451 $config = array(
8452 'clean' => false,
8453 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8454 'doctype' => 'strict',
8455 'show-body-only' => true,
8456 "indent-attributes" => false,
8457 "vertical-space" => false,
8458 //'ident' => false, // Not always supported
8459 "wrap" => 0
8460 // HTML5 tags
8461 //'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',
8462 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8463 //'new-empty-tags' => 'command embed keygen source track wbr',
8464 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8465 );
8466
8467 // Tidy
8468 $tidy = new tidy();
8469 $out = $tidy->repairString($out, $config, 'utf8');
8470
8471 //print "xxx".$out;exit;
8472 }
8473 } catch (Exception $e) {
8474 // If error, invalid HTML string with no way to clean it
8475 //print $e->getMessage();
8476 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8477 }
8478 }
8479
8480 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8481 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8482
8483 // Clean some html entities that are useless so text is cleaner
8484 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8485
8486 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8487 // encoded using text entities) so we can then exclude all numeric entities.
8488 $out = preg_replace('/&#39;/i', '&apos;', $out);
8489
8490 // 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).
8491 // 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
8492 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8493 $out = preg_replace_callback(
8494 '/&#(x?[0-9][0-9a-f]+;?)/i',
8499 static function ($m) {
8500 return realCharForNumericEntities($m);
8501 },
8502 $out
8503 );
8504
8505 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8506 $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'.
8507
8508 // Keep only some html tags and remove also some 'javascript:' strings
8509 if ($check == 'restricthtmlallowlinkscript') {
8510 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1);
8511 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8512 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8513 } else {
8514 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8515 }
8516
8517 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8518 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8520 }
8521
8522 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8523 $out = preg_replace('/&apos;/i', "&#39;", $out);
8524
8525 // Now remove js
8526 // 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
8527 $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)>
8528 $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);
8529 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8530 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8531 $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);
8532 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8533 // More not into the previous list
8534 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8535 } while ($oldstringtoclean != $out);
8536
8537 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8538 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8539 // 'url(' to avoid inline style like background: url(http...
8540 // '<link' to avoid <link href="http...">
8541 $reg = array();
8542 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8543 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8544 $nblinks = count($reg[0]);
8545 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8546 $out = 'ErrorTooManyLinksIntoHTMLString';
8547 }
8548
8549 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8550 if ($nblinks > 0) {
8551 $out = 'ErrorHTMLLinksNotAllowed';
8552 }
8553 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8554 $nblinks = 0;
8555 // Loop on each url in src= and url(
8556 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8557
8558 $matches = array();
8559 if (preg_match_all($pattern, $out, $matches)) {
8560 // URLs are into $matches[1]
8561 $urls = $matches[1];
8562
8563 // Affiche les URLs
8564 foreach ($urls as $url) {
8565 $nblinks++;
8566 echo "Found url = ".$url . "\n";
8567 }
8568 if ($nblinks > 0) {
8569 $out = 'ErrorHTMLExternalLinksNotAllowed';
8570 }
8571 }
8572 }
8573
8574 return $out;
8575 }
8576}
8577
8598function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8599{
8600 if (is_null($stringtoencode)) {
8601 return '';
8602 }
8603
8604 $newstring = $stringtoencode;
8605 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8606 $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.
8607 if ($removelasteolbr) {
8608 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8609 }
8610 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8611 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8612 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8613 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8614 } else {
8615 if ($removelasteolbr) {
8616 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8617 }
8618 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8619 }
8620 // Other substitutions that htmlentities does not do
8621 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8622 return $newstring;
8623}
8624
8632function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8633{
8634 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8635 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8636 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8637 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8638 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8639 return $ret;
8640}
8641
8648function dol_htmlcleanlastbr($stringtodecode)
8649{
8650 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8651 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8652 return $ret;
8653}
8654
8664function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8665{
8666 $newstring = $a;
8667 if ($keepsomeentities) {
8668 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8669 }
8670 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8671 if ($keepsomeentities) {
8672 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8673 }
8674 return $newstring;
8675}
8676
8688function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8689{
8690 return htmlentities($string, $flags, $encoding, $double_encode);
8691}
8692
8704function dol_string_is_good_iso($s, $clean = 0)
8705{
8706 $len = dol_strlen($s);
8707 $out = '';
8708 $ok = 1;
8709 for ($scursor = 0; $scursor < $len; $scursor++) {
8710 $ordchar = ord($s[$scursor]);
8711 //print $scursor.'-'.$ordchar.'<br>';
8712 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8713 $ok = 0;
8714 break;
8715 } elseif ($ordchar > 126 && $ordchar < 160) {
8716 $ok = 0;
8717 break;
8718 } elseif ($clean) {
8719 $out .= $s[$scursor];
8720 }
8721 }
8722 if ($clean) {
8723 return $out;
8724 }
8725 return $ok;
8726}
8727
8736function dol_nboflines($s, $maxchar = 0)
8737{
8738 if ($s == '') {
8739 return 0;
8740 }
8741 $arraystring = explode("\n", $s);
8742 $nb = count($arraystring);
8743
8744 return $nb;
8745}
8746
8747
8757function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8758{
8759 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8760 if (dol_textishtml($text)) {
8761 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8762 }
8763
8764 $text = strtr($text, $repTable);
8765 if ($charset == 'UTF-8') {
8766 $pattern = '/(<br[^>]*>)/Uu';
8767 } else {
8768 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8769 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8770 }
8771 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8772
8773 $nblines = (int) floor((count($a) + 1) / 2);
8774 // count possible auto line breaks
8775 if ($maxlinesize) {
8776 foreach ($a as $line) {
8777 if (dol_strlen($line) > $maxlinesize) {
8778 //$line_dec = html_entity_decode(strip_tags($line));
8779 $line_dec = html_entity_decode($line);
8780 if (dol_strlen($line_dec) > $maxlinesize) {
8781 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8782 $nblines += substr_count($line_dec, '\n');
8783 }
8784 }
8785 }
8786 }
8787
8788 unset($a);
8789 return $nblines;
8790}
8791
8800function dol_textishtml($msg, $option = 0)
8801{
8802 if (is_null($msg)) {
8803 return false;
8804 }
8805
8806 if ($option == 1) {
8807 if (preg_match('/<(html|link|script)/i', $msg)) {
8808 return true;
8809 } elseif (preg_match('/<body/i', $msg)) {
8810 return true;
8811 } elseif (preg_match('/<\/textarea/i', $msg)) {
8812 return true;
8813 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8814 return true;
8815 } elseif (preg_match('/<br/i', $msg)) {
8816 return true;
8817 }
8818 return false;
8819 } else {
8820 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8821 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8822 if (preg_match('/<(html|link|script|body)/i', $msg)) {
8823 return true;
8824 } elseif (preg_match('/<\/textarea/i', $msg)) {
8825 return true;
8826 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8827 return true;
8828 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8829 return true;
8830 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8831 return true;
8832 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8833 return true;
8834 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8835 return true; // must accept <img src="http://example.com/aaa.png" />
8836 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8837 return true; // must accept <a href="http://example.com/aaa.png" />
8838 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8839 return true;
8840 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8841 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8842 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8843 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8844 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8845 }
8846
8847 return false;
8848 }
8849}
8850
8865function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8866{
8867 if (!empty($invert)) {
8868 $tmp = $text1;
8869 $text1 = $text2;
8870 $text2 = $tmp;
8871 }
8872
8873 $ret = '';
8874 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8875 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8876 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8877 return $ret;
8878}
8879
8880
8881
8895function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8896{
8897 global $db, $conf, $mysoc, $user, $extrafields;
8898
8899 $substitutionarray = array();
8900
8901 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
8902 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8903 // this will include signature content first and then replace var found into content of signature
8904 //var_dump($onlykey);
8905 $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()
8906 $usersignature = $user->signature;
8907 $substitutionarray = array_merge($substitutionarray, array(
8908 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8909 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8910 ));
8911
8912 if (is_object($user) && ($user instanceof User)) {
8913 $substitutionarray = array_merge($substitutionarray, array(
8914 '__USER_ID__' => (string) $user->id,
8915 '__USER_LOGIN__' => (string) $user->login,
8916 '__USER_EMAIL__' => (string) $user->email,
8917 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8918 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8919 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8920 '__USER_FAX__' => (string) $user->office_fax,
8921 '__USER_LASTNAME__' => (string) $user->lastname,
8922 '__USER_FIRSTNAME__' => (string) $user->firstname,
8923 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8924 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8925 '__USER_JOB__' => (string) $user->job,
8926 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8927 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8928 ));
8929 }
8930 }
8931 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8932 $substitutionarray = array_merge($substitutionarray, array(
8933 '__MYCOMPANY_NAME__' => $mysoc->name,
8934 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8935 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8936 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8937 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8938 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8939 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8940 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8941 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8942 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8943 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8944 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8945 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8946 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8947 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8948 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8949 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8950 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8951 '__MYCOMPANY_TOWN__' => $mysoc->town,
8952 '__MYCOMPANY_STATE__' => $mysoc->state,
8953 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
8954 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
8955 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
8956 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
8957 ));
8958 }
8959
8960 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
8961 if ($onlykey) {
8962 $substitutionarray['__ID__'] = '__ID__';
8963 $substitutionarray['__REF__'] = '__REF__';
8964 $substitutionarray['__NEWREF__'] = '__NEWREF__';
8965 $substitutionarray['__LABEL__'] = '__LABEL__';
8966 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
8967 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
8968 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
8969 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
8970 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
8971
8972 if (isModEnabled("societe")) { // Most objects are concerned
8973 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
8974 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
8975 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
8976 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
8977 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
8978 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
8979 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
8980 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
8981 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
8982 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
8983 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
8984 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
8985 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
8986 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
8987 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
8988 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
8989 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
8990 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
8991 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
8992 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
8993 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
8994 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
8995 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
8996 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
8997 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
8998 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
8999 }
9000 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9001 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9002 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9003 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9004 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9005 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9006 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9007 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9008 }
9009 // add substitution variables for ticket
9010 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9011 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9012 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9013 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9014 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9015 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9016 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9017 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9018 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9019 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9020 }
9021
9022 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9023 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9024 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9025 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9026 }
9027 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9028 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9029 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9030 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9031 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9032 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9033 }
9034 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9035 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9036 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9037 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9038 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9039 }
9040 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9041 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9042 }
9043 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9044 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9045 }
9046 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9047 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9048 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9049 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9050 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9051 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9052 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9053
9054 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9055 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9056 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9057 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9058 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9059
9060 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9061 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9062 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9063 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9064 }
9065 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9066 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9067 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9068 }
9069 } else {
9070 '@phan-var-force Adherent|Delivery $object';
9071 $substitutionarray['__ID__'] = $object->id;
9072 $substitutionarray['__REF__'] = $object->ref;
9073 $substitutionarray['__NEWREF__'] = $object->newref;
9074 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9075 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9076 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9077 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9078 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9079 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9080 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9081 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9082 $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', false, $outputlangs) : '');
9083 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%d") : '');
9084 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%A") : '');
9085 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%m") : '');
9086 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%b") : '');
9087 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%Y") : '');
9088 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%H") : '');
9089 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%M") : '');
9090 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%S") : '');
9091
9092 // For backward compatibility (deprecated)
9093 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9094 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9095 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->delivery_date) ? dol_print_date($object->delivery_date, 'day', false, $outputlangs) : '');
9096 $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 : '')) : '');
9097 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9098
9099 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9100 '@phan-var-force Adherent $object';
9101 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9102
9103 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9104 if (method_exists($object, 'getCivilityLabel')) {
9105 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9106 }
9107 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9108 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9109 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9110 if (method_exists($object, 'getFullName')) {
9111 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9112 }
9113 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9114 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9115 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9116 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9117 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9118 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9119 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9120 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9121 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9122 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9123 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9124 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9125 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9126 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9127 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9128 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9129
9130 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9131 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9132 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9133 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9134 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9135 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9136 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9137 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9138 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9139 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9140 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9141 }
9142
9143 if (is_object($object) && $object->element == 'societe') {
9144 '@phan-var-force Societe $object';
9145 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
9146 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
9147 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
9148 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
9149 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
9150 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
9151 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
9152 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
9153 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
9154 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
9155 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
9156 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
9157 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object) ? $object->state : '');
9158 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
9159 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
9160 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
9161 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
9162 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
9163 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
9164 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
9165 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
9166 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
9167 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
9168 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
9169 } elseif (is_object($object->thirdparty)) {
9170 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
9171 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9172 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9173 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9174 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9175 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9176 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9177 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9178 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9179 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9180 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9181 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9182 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object->thirdparty) ? $object->thirdparty->state : '');
9183 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9184 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9185 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9186 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9187 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9188 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9189 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9190 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9191 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9192 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9193 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9194 }
9195
9196 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9197 '@phan-var-force RecruitmentCandidature $object';
9198 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9199 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9200 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9201 }
9202 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9203 '@phan-var-force ConferenceOrBoothAttendee $object';
9204 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9205 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9206 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9207 }
9208
9209 if (is_object($object) && $object->element == 'project') {
9210 '@phan-var-force Project $object';
9211 $substitutionarray['__PROJECT_ID__'] = $object->id;
9212 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9213 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9214 } elseif (is_object($object)) {
9215 $project = null;
9216 if (!empty($object->project)) {
9217 $project = $object->project;
9218 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9219 $project = $object->projet;
9220 }
9221 if (!is_null($project) && is_object($project)) {
9222 $substitutionarray['__PROJECT_ID__'] = $project->id;
9223 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9224 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9225 } else {
9226 // can substitute variables for project : uses lazy load in "make_substitutions" method
9227 $project_id = 0;
9228 if (!empty($object->fk_project) && $object->fk_project > 0) {
9229 $project_id = $object->fk_project;
9230 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9231 $project_id = $object->fk_project;
9232 }
9233 if ($project_id > 0) {
9234 // path:class:method:id
9235 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9236 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9237 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9238 }
9239 }
9240 }
9241
9242 if (is_object($object) && $object->element == 'facture') {
9243 '@phan-var-force Facture $object';
9244 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9245 }
9246 if (is_object($object) && $object->element == 'shipping') {
9247 '@phan-var-force Expedition $object';
9248 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9249 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9250 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9251 }
9252 if (is_object($object) && $object->element == 'reception') {
9253 '@phan-var-force Reception $object';
9254 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9255 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9256 }
9257
9258 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9259 '@phan-var-force Contrat $object';
9260 $dateplannedstart = '';
9261 $datenextexpiration = '';
9262 foreach ($object->lines as $line) {
9263 if ($line->date_start > $dateplannedstart) {
9264 $dateplannedstart = $line->date_start;
9265 }
9266 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9267 $datenextexpiration = $line->date_end;
9268 }
9269 }
9270 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9271 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9272 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9273
9274 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9275 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9276 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9277 }
9278 // add substitution variables for ticket
9279 if (is_object($object) && $object->element == 'ticket') {
9280 '@phan-var-force Ticket $object';
9281 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9282 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9283 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9284 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9285 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9286 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9287 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9288 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9289 $userstat = new User($db);
9290 if ($object->fk_user_assign > 0) {
9291 $userstat->fetch($object->fk_user_assign);
9292 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9293 }
9294
9295 if ($object->fk_user_create > 0) {
9296 $userstat->fetch($object->fk_user_create);
9297 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9298 }
9299 }
9300
9301 // Create dynamic tags for __EXTRAFIELD_FIELD__
9302 if ($object->table_element && $object->id > 0) {
9303 if (!is_object($extrafields)) {
9304 $extrafields = new ExtraFields($db);
9305 }
9306 $extrafields->fetch_name_optionals_label($object->table_element, true);
9307
9308 if ($object->fetch_optionals() > 0) {
9309 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9310 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9311 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9312 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9313 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9314 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9315 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9316 $datetime = $object->array_options['options_'.$key];
9317 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9318 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9319 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9320 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9321 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9322 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9323 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9324 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9325 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9326 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9327 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9328 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9329 }
9330 }
9331 }
9332 }
9333 }
9334
9335 // Complete substitution array with the url to make online payment
9336 if (empty($substitutionarray['__REF__'])) {
9337 $paymenturl = '';
9338 } else {
9339 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9340 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9341 $outputlangs->loadLangs(array('paypal', 'other'));
9342
9343 $amounttouse = 0;
9344 $typeforonlinepayment = 'free';
9345 if (is_object($object) && $object->element == 'commande') {
9346 $typeforonlinepayment = 'order';
9347 }
9348 if (is_object($object) && $object->element == 'facture') {
9349 $typeforonlinepayment = 'invoice';
9350 }
9351 if (is_object($object) && $object->element == 'member') {
9352 $typeforonlinepayment = 'member';
9353 if (!empty($object->last_subscription_amount)) {
9354 $amounttouse = $object->last_subscription_amount;
9355 }
9356 }
9357 if (is_object($object) && $object->element == 'contrat') {
9358 $typeforonlinepayment = 'contract';
9359 }
9360 if (is_object($object) && $object->element == 'fichinter') {
9361 $typeforonlinepayment = 'ficheinter';
9362 }
9363
9364 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9365 $paymenturl = $url;
9366 }
9367
9368 if ($object->id > 0) {
9369 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9370 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9371
9372 // Show structured communication
9373 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9374 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9375 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9376 }
9377
9378 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9379 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9380 } else {
9381 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9382 }
9383 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9384 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9385 } else {
9386 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9387 }
9388 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9389 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9390 } else {
9391 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9392 }
9393 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9394 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9395 } else {
9396 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9397 }
9398 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9399 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9400 } else {
9401 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9402 }
9403 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9404 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9405 } else {
9406 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9407 }
9408
9409 if (is_object($object) && $object->element == 'propal') {
9410 '@phan-var-force Propal $object';
9411 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9412 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9413 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9414 }
9415 if (is_object($object) && $object->element == 'commande') {
9416 '@phan-var-force Commande $object';
9417 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9418 }
9419 if (is_object($object) && $object->element == 'facture') {
9420 '@phan-var-force Facture $object';
9421 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9422 }
9423 if (is_object($object) && $object->element == 'contrat') {
9424 '@phan-var-force Contrat $object';
9425 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9426 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9427 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9428 }
9429 if (is_object($object) && $object->element == 'fichinter') {
9430 '@phan-var-force Fichinter $object';
9431 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9432 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9433 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9434 }
9435 if (is_object($object) && $object->element == 'supplier_proposal') {
9436 '@phan-var-force SupplierProposal $object';
9437 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9438 }
9439 if (is_object($object) && $object->element == 'invoice_supplier') {
9440 '@phan-var-force FactureFournisseur $object';
9441 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9442 }
9443 if (is_object($object) && $object->element == 'shipping') {
9444 '@phan-var-force Expedition $object';
9445 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9446 }
9447 }
9448
9449 if (is_object($object) && $object->element == 'action') {
9450 '@phan-var-force ActionComm $object';
9451 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9452 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9453 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9454 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9455 }
9456 }
9457 }
9458 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9459 '@phan-var-force Facture|FactureRec $object';
9460 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9461
9462 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9463 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9464 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9465 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9466
9467 $already_payed_all = 0;
9468 if (is_object($object) && ($object instanceof Facture)) {
9469 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9470 }
9471
9472 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9473 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9474 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9475
9476 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9477 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9478 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9479
9480 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9481
9482 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9483 $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)) : '';
9484 $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)) : '';
9485
9486 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9487 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9488 }
9489 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9490 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9491 }
9492
9493 // Amount keys formatted in a currency
9494 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9495 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9496 $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) : '';
9497 $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)) : '';
9498 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9499 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9500 }
9501 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9502 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9503 }
9504 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9505 if ($onlykey != 2) {
9506 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9507 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9508 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9509 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9510 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9511 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9512 }
9513 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9514 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9515 }
9516 }
9517
9518 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9519 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9520 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9521 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9522 // TODO Add other keys for foreign multicurrency
9523
9524 // For backward compatibility
9525 if ($onlykey != 2) {
9526 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9527 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9528 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9529 }
9530 }
9531
9532
9533 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9534 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9535
9536 $now = dol_now();
9537
9538 $tmp = dol_getdate($now, true);
9539 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9540 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9541 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9542 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9543
9544 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9545
9546 $substitutionarray = array_merge($substitutionarray, array(
9547 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9548 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9549 '__DAY__' => (string) $tmp['mday'],
9550 '__DAY_TEXT__' => $daytext, // Monday
9551 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9552 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9553 '__MONTH__' => (string) $tmp['mon'],
9554 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9555 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9556 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9557 '__YEAR__' => (string) $tmp['year'],
9558 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9559 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9560 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9561 '__NEXT_DAY__' => (string) $tmp4['day'],
9562 '__NEXT_MONTH__' => (string) $tmp5['month'],
9563 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9564 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9565 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9566 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9567 ));
9568 }
9569
9570 if (isModEnabled('multicompany')) {
9571 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9572 }
9573 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9574 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9575 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9576 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9577 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9578 }
9579
9580 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9581
9582 return $substitutionarray;
9583}
9584
9601function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9602{
9603 global $conf, $db, $langs;
9604
9605 if (!is_array($substitutionarray)) {
9606 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9607 }
9608
9609 if (empty($outputlangs)) {
9610 $outputlangs = $langs;
9611 }
9612
9613 // Is initial text HTML or simple text ?
9614 $msgishtml = 0;
9615 if (dol_textishtml($text, 1)) {
9616 $msgishtml = 1;
9617 }
9618
9619 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9620 if (is_object($outputlangs)) {
9621 $reg = array();
9622 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9623 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9624 $tmp = explode('|', $reg[1]);
9625 if (!empty($tmp[1])) {
9626 $outputlangs->load($tmp[1]);
9627 }
9628
9629 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9630
9631 if (empty($converttextinhtmlifnecessary)) {
9632 // convert $newval into HTML is necessary
9633 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9634 } else {
9635 if (! $msgishtml) {
9636 $valueishtml = dol_textishtml($value, 1);
9637 //var_dump("valueishtml=".$valueishtml);
9638
9639 if ($valueishtml) {
9640 $text = dol_htmlentitiesbr($text);
9641 $msgishtml = 1;
9642 }
9643 } else {
9644 $value = dol_nl2br("$value");
9645 }
9646
9647 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9648 }
9649 }
9650 }
9651
9652 // Make substitution for constant keys.
9653 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9654 $reg = array();
9655 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9656 $keyfound = $reg[1];
9657 if (isASecretKey($keyfound)) {
9658 $value = '*****forbidden*****';
9659 } else {
9660 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9661 }
9662
9663 if (empty($converttextinhtmlifnecessary)) {
9664 // convert $newval into HTML is necessary
9665 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9666 } else {
9667 if (! $msgishtml) {
9668 $valueishtml = dol_textishtml($value, 1);
9669
9670 if ($valueishtml) {
9671 $text = dol_htmlentitiesbr($text);
9672 $msgishtml = 1;
9673 }
9674 } else {
9675 $value = dol_nl2br("$value");
9676 }
9677
9678 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9679 }
9680 }
9681
9682 // Make substitution for array $substitutionarray
9683 foreach ($substitutionarray as $key => $value) {
9684 if (!isset($value)) {
9685 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9686 }
9687
9688 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9689 $value = ''; // Protection
9690 }
9691
9692 if (empty($converttextinhtmlifnecessary)) {
9693 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9694 } else {
9695 if (! $msgishtml) {
9696 $valueishtml = dol_textishtml($value, 1);
9697
9698 if ($valueishtml) {
9699 $text = dol_htmlentitiesbr($text);
9700 $msgishtml = 1;
9701 }
9702 } else {
9703 $value = dol_nl2br("$value");
9704 }
9705 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9706 }
9707 }
9708
9709 // TODO Implement the lazyload substitution
9710 /*
9711 add a loop to scan $substitutionarray:
9712 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.
9713 If no, we don't need to make replacement, so we do nothing.
9714 If yes, we can make the substitution:
9715
9716 include_once $path;
9717 $tmpobj = new $class($db);
9718 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9719 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9720 */
9721 $memory_object_list = array();
9722 foreach ($substitutionarray as $key => $value) {
9723 $lazy_load_arr = array();
9724 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9725 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9726 $key_to_substitute = $lazy_load_arr[1];
9727 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9728 $param_arr = explode(':', $value);
9729 // path:class:method:id
9730 if (count($param_arr) == 4) {
9731 $path = $param_arr[0];
9732 $class = $param_arr[1];
9733 $method = $param_arr[2];
9734 $id = (int) $param_arr[3];
9735
9736 // load class file and init object list in memory
9737 if (!isset($memory_object_list[$class])) {
9738 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9739 require_once DOL_DOCUMENT_ROOT . $path;
9740 if (class_exists($class)) {
9741 $memory_object_list[$class] = array(
9742 'list' => array(),
9743 );
9744 }
9745 }
9746 }
9747
9748 // fetch object and set substitution
9749 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9750 if (method_exists($class, $method)) {
9751 if (!isset($memory_object_list[$class]['list'][$id])) {
9752 $tmpobj = new $class($db);
9753 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9754 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9755 $memory_object_list[$class]['list'][$id] = $tmpobj;
9756 } else {
9757 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9758 $tmpobj = $memory_object_list[$class]['list'][$id];
9759 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9760 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9761 }
9762
9763 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9764 }
9765 }
9766 }
9767 }
9768 }
9769 }
9770 }
9771
9772 return $text;
9773}
9774
9787function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9788{
9789 global $conf, $user;
9790
9791 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9792
9793 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9794
9795 // Check if there is external substitution to do, requested by plugins
9796 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9797
9798 foreach ($dirsubstitutions as $reldir) {
9799 $dir = dol_buildpath($reldir, 0);
9800
9801 // Check if directory exists
9802 if (!dol_is_dir($dir)) {
9803 continue;
9804 }
9805
9806 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9807 foreach ($substitfiles as $substitfile) {
9808 $reg = array();
9809 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9810 $module = $reg[1];
9811
9812 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9813 // Include the user's functions file
9814 require_once $dir.$substitfile['name'];
9815 // Call the user's function, and only if it is defined
9816 $function_name = $module."_".$callfunc;
9817 if (function_exists($function_name)) {
9818 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9819 }
9820 }
9821 }
9822 }
9823 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9824 // to list all tags in odt template
9825 $tags = '';
9826 foreach ($substitutionarray as $key => $value) {
9827 $tags .= '{'.$key.'} => '.$value."\n";
9828 }
9829 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9830 }
9831}
9832
9842function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9843{
9844 print get_date_range($date_start, $date_end, $format, $outputlangs);
9845}
9846
9857function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9858{
9859 global $langs;
9860
9861 $out = '';
9862
9863 if (!is_object($outputlangs)) {
9864 $outputlangs = $langs;
9865 }
9866
9867 if ($date_start && $date_end) {
9868 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9869 }
9870 if ($date_start && !$date_end) {
9871 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9872 }
9873 if (!$date_start && $date_end) {
9874 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9875 }
9876
9877 return $out;
9878}
9879
9888function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9889{
9890 global $conf;
9891
9892 $ret = '';
9893 // If order not defined, we use the setup
9894 if ($nameorder < 0) {
9895 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9896 }
9897 if ($nameorder == 1) {
9898 $ret .= $firstname;
9899 if ($firstname && $lastname) {
9900 $ret .= ' ';
9901 }
9902 $ret .= $lastname;
9903 } elseif ($nameorder == 2 || $nameorder == 3) {
9904 $ret .= $firstname;
9905 if (empty($ret) && $nameorder == 3) {
9906 $ret .= $lastname;
9907 }
9908 } else { // 0, 4 or 5
9909 $ret .= $lastname;
9910 if (empty($ret) && $nameorder == 5) {
9911 $ret .= $firstname;
9912 }
9913 if ($nameorder == 0) {
9914 if ($firstname && $lastname) {
9915 $ret .= ' ';
9916 }
9917 $ret .= $firstname;
9918 }
9919 }
9920 return $ret;
9921}
9922
9923
9936function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
9937{
9938 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9939 if (!is_array($mesgs)) {
9940 $mesgs = trim((string) $mesgs);
9941 // If mesgs is a not an empty string
9942 if ($mesgs) {
9943 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9944 return;
9945 }
9946 if ($attop) {
9947 array_unshift($_SESSION['dol_events'][$style], $mesgs);
9948 } else {
9949 $_SESSION['dol_events'][$style][] = $mesgs;
9950 }
9951 }
9952 } else {
9953 // If mesgs is an array
9954 foreach ($mesgs as $mesg) {
9955 $mesg = trim((string) $mesg);
9956 if ($mesg) {
9957 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
9958 return;
9959 }
9960 if ($attop) {
9961 array_unshift($_SESSION['dol_events'][$style], $mesgs);
9962 } else {
9963 $_SESSION['dol_events'][$style][] = $mesg;
9964 }
9965 }
9966 }
9967 }
9968}
9969
9983function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
9984{
9985 if (empty($mesg) && empty($mesgs)) {
9986 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
9987 } else {
9988 if ($messagekey) {
9989 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
9990 // TODO
9991 $mesg .= '';
9992 }
9993 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
9994 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
9995 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
9996 }
9997 if (empty($mesgs)) {
9998 setEventMessage($mesg, $style, $noduplicate, $attop);
9999 } else {
10000 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10001 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10002 }
10003 setEventMessage($mesgs, $style, $noduplicate, $attop);
10004 }
10005 }
10006 }
10007}
10008
10018function dol_htmloutput_events($disabledoutputofmessages = 0)
10019{
10020 // Show mesgs
10021 if (isset($_SESSION['dol_events']['mesgs'])) {
10022 if (empty($disabledoutputofmessages)) {
10023 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10024 }
10025 unset($_SESSION['dol_events']['mesgs']);
10026 }
10027 // Show errors
10028 if (isset($_SESSION['dol_events']['errors'])) {
10029 if (empty($disabledoutputofmessages)) {
10030 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10031 }
10032 unset($_SESSION['dol_events']['errors']);
10033 }
10034
10035 // Show warnings
10036 if (isset($_SESSION['dol_events']['warnings'])) {
10037 if (empty($disabledoutputofmessages)) {
10038 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10039 }
10040 unset($_SESSION['dol_events']['warnings']);
10041 }
10042}
10043
10058function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10059{
10060 global $conf, $langs;
10061
10062 $ret = 0;
10063 $return = '';
10064 $out = '';
10065 $divstart = $divend = '';
10066
10067 // If inline message with no format, we add it.
10068 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10069 $divstart = '<div class="'.$style.' clearboth">';
10070 $divend = '</div>';
10071 }
10072
10073 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10074 $langs->load("errors");
10075 $out .= $divstart;
10076 if (is_array($mesgarray) && count($mesgarray)) {
10077 foreach ($mesgarray as $message) {
10078 $ret++;
10079 $out .= $langs->trans($message);
10080 if ($ret < count($mesgarray)) {
10081 $out .= "<br>\n";
10082 }
10083 }
10084 }
10085 if ($mesgstring) {
10086 $ret++;
10087 $out .= $langs->trans($mesgstring);
10088 }
10089 $out .= $divend;
10090 }
10091
10092 if ($out) {
10093 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10094 $return = '<script nonce="'.getNonce().'">
10095 $(document).ready(function() {
10096 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
10097 if (block) {
10098 $.dolEventValid("","'.dol_escape_js($out).'");
10099 } else {
10100 /* jnotify(message, preset of message type, keepmessage) */
10101 $.jnotify("'.dol_escape_js($out).'",
10102 "'.($style == "ok" ? 3000 : $style).'",
10103 '.($style == "ok" ? "false" : "true").',
10104 { remove: function (){} } );
10105 }
10106 });
10107 </script>';
10108 } else {
10109 $return = $out;
10110 }
10111 }
10112
10113 return $return;
10114}
10115
10127function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10128{
10129 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10130}
10131
10145function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10146{
10147 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10148 return;
10149 }
10150
10151 $iserror = 0;
10152 $iswarning = 0;
10153 if (is_array($mesgarray)) {
10154 foreach ($mesgarray as $val) {
10155 if ($val && preg_match('/class="error"/i', $val)) {
10156 $iserror++;
10157 break;
10158 }
10159 if ($val && preg_match('/class="warning"/i', $val)) {
10160 $iswarning++;
10161 break;
10162 }
10163 }
10164 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10165 $iserror++;
10166 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10167 $iswarning++;
10168 }
10169 if ($style == 'error') {
10170 $iserror++;
10171 }
10172 if ($style == 'warning') {
10173 $iswarning++;
10174 }
10175
10176 if ($iserror || $iswarning) {
10177 // Remove div from texts
10178 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10179 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10180 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10181 // Remove div from texts array
10182 if (is_array($mesgarray)) {
10183 $newmesgarray = array();
10184 foreach ($mesgarray as $val) {
10185 if (is_string($val)) {
10186 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10187 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10188 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10189 $newmesgarray[] = $tmpmesgstring;
10190 } else {
10191 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10192 }
10193 }
10194 $mesgarray = $newmesgarray;
10195 }
10196 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10197 } else {
10198 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10199 }
10200}
10201
10213function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10214{
10215 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10216}
10217
10238function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10239{
10240 // Clean parameters
10241 $order = strtolower($order);
10242
10243 if (is_array($array)) {
10244 $sizearray = count($array);
10245 if ($sizearray > 0) {
10246 $temp = array();
10247 foreach (array_keys($array) as $key) {
10248 if (is_object($array[$key])) {
10249 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10250 } else {
10251 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10252 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10253 }
10254 if ($natsort == -1) {
10255 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10256 }
10257 }
10258
10259 if (empty($natsort) || $natsort == -1) {
10260 if ($order == 'asc') {
10261 asort($temp);
10262 } else {
10263 arsort($temp);
10264 }
10265 } else {
10266 if ($case_sensitive) {
10267 natsort($temp);
10268 } else {
10269 natcasesort($temp); // natecasesort is not sensible to case
10270 }
10271 if ($order != 'asc') {
10272 $temp = array_reverse($temp, true);
10273 }
10274 }
10275
10276 $sorted = array();
10277
10278 foreach (array_keys($temp) as $key) {
10279 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10280 }
10281
10282 return $sorted;
10283 }
10284 }
10285 return $array;
10286}
10287
10288
10296function utf8_check($str)
10297{
10298 $str = (string) $str; // Sometimes string is an int.
10299
10300 // We must use here a binary strlen function (so not dol_strlen)
10301 $strLength = strlen($str);
10302 for ($i = 0; $i < $strLength; $i++) {
10303 if (ord($str[$i]) < 0x80) {
10304 continue; // 0bbbbbbb
10305 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10306 $n = 1; // 110bbbbb
10307 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10308 $n = 2; // 1110bbbb
10309 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10310 $n = 3; // 11110bbb
10311 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10312 $n = 4; // 111110bb
10313 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10314 $n = 5; // 1111110b
10315 } else {
10316 return false; // Does not match any model
10317 }
10318 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10319 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10320 return false;
10321 }
10322 }
10323 }
10324 return true;
10325}
10326
10334function utf8_valid($str)
10335{
10336 /* 2 other methods to test if string is utf8
10337 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10338 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10339 */
10340 return preg_match('//u', $str) ? true : false;
10341}
10342
10343
10350function ascii_check($str)
10351{
10352 if (function_exists('mb_check_encoding')) {
10353 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10354 if (!mb_check_encoding($str, 'ASCII')) {
10355 return false;
10356 }
10357 } else {
10358 if (preg_match('/[^\x00-\x7f]/', $str)) {
10359 return false; // Contains a byte > 7f
10360 }
10361 }
10362
10363 return true;
10364}
10365
10366
10374function dol_osencode($str)
10375{
10376 $tmp = ini_get("unicode.filesystem_encoding");
10377 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10378 $tmp = 'iso-8859-1'; // By default for windows
10379 }
10380 if (empty($tmp)) {
10381 $tmp = 'utf-8'; // By default for other
10382 }
10383 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10384 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10385 }
10386
10387 if ($tmp == 'iso-8859-1') {
10388 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10389 }
10390 return $str;
10391}
10392
10393
10408function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
10409{
10410 global $conf;
10411
10412 // If key empty
10413 if ($key == '') {
10414 return 0;
10415 }
10416
10417 // Check in cache
10418 if (isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10419 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10420 }
10421
10422 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10423
10424 $sql = "SELECT ".$fieldid." as valuetoget";
10425 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10426 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10427 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10428 } else {
10429 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10430 }
10431 if (!empty($entityfilter)) {
10432 $sql .= " AND entity IN (".getEntity($tablename).")";
10433 }
10434 if ($filters) {
10435 $sql .= $filters;
10436 }
10437
10438 $resql = $db->query($sql);
10439 if ($resql) {
10440 $obj = $db->fetch_object($resql);
10441 if ($obj) {
10442 $conf->cache['codeid'][$tablename][$key][$fieldid] = $obj->valuetoget;
10443 } else {
10444 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10445 }
10446 $db->free($resql);
10447
10448 return $conf->cache['codeid'][$tablename][$key][$fieldid];
10449 } else {
10450 return -1;
10451 }
10452}
10453
10463function isStringVarMatching($var, $regextext, $matchrule = 1)
10464{
10465 if ($matchrule == 1) {
10466 if ($var == 'mainmenu') {
10467 global $mainmenu;
10468 return (preg_match('/^'.$regextext.'/', $mainmenu));
10469 } elseif ($var == 'leftmenu') {
10470 global $leftmenu;
10471 return (preg_match('/^'.$regextext.'/', $leftmenu));
10472 } else {
10473 return 'This variable is not accessible with dol_eval';
10474 }
10475 } else {
10476 return 'This value for matchrule is not implemented';
10477 }
10478}
10479
10480
10490function verifCond($strToEvaluate, $onlysimplestring = '1')
10491{
10492 //print $strToEvaluate."<br>\n";
10493 $rights = true;
10494 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10495 //var_dump($strToEvaluate);
10496 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10497 $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
10498 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10499 //var_dump($rights);
10500 }
10501 return $rights;
10502}
10503
10518function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10519{
10520 // Only this global variables can be read by eval function and returned to caller
10521 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10522 global $db, $langs, $user, $website, $websitepage;
10523 global $action, $mainmenu, $leftmenu;
10524 global $mysoc;
10525 global $objectoffield; // To allow the use of $objectoffield in computed fields
10526
10527 // Old variables used
10528 global $object;
10529 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10530
10531 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10532 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10533 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10534 }
10535
10536 try {
10537 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10538 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10539 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10540 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10541 // We must accept with 2: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found"
10542
10543 // Check if there is dynamic call (first we check chars are all into use a whitelist chars)
10544 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10545 if ($onlysimplestring == '2') {
10546 $specialcharsallowed .= '[]';
10547 }
10548 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10549 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10550 }
10551 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10552 if ($returnvalue) {
10553 return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s;
10554 } else {
10555 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s, LOG_WARNING);
10556 return '';
10557 }
10558 }
10559
10560 // Check if there is dynamic call (first we use black list patterns)
10561 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10562 if ($returnvalue) {
10563 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a call using of "$abc(" or "$abc (" instead of using the direct name of the function): '.$s;
10564 } else {
10565 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a call using of "$abc(" or "$abc (" instead of using the direct name of the function): '.$s, LOG_WARNING);
10566 return '';
10567 }
10568 }
10569
10570 // Now we check if we try dynamic call (by removing white list pattern of using parenthesis then testing if a parenthesis exists)
10571 $savescheck = '';
10572 $scheck = $s;
10573 while ($scheck && $savescheck != $scheck) {
10574 $savescheck = $scheck;
10575 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10576 $scheck = preg_replace('/^\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10577 $scheck = preg_replace('/\s\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in 'if ($a == 1)'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10578 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10579 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10580 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10581 }
10582 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10583 if (strpos($scheck, '(') !== false) {
10584 if ($returnvalue) {
10585 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10586 } else {
10587 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);
10588 return '';
10589 }
10590 }
10591
10592 // TODO
10593 // We can exclude $ char that are not:
10594 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10595 }
10596 if (is_array($s) || $s === 'Array') {
10597 if ($returnvalue) {
10598 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10599 } else {
10600 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10601 return '';
10602 }
10603 }
10604 if (strpos($s, '::') !== false) {
10605 if ($returnvalue) {
10606 return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
10607 } else {
10608 dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s, LOG_WARNING);
10609 return '';
10610 }
10611 }
10612 if (strpos($s, '`') !== false) {
10613 if ($returnvalue) {
10614 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10615 } else {
10616 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10617 return '';
10618 }
10619 }
10620 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10621 if ($returnvalue) {
10622 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10623 } else {
10624 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10625 return '';
10626 }
10627 }
10628
10629 // We block use of php exec or php file functions
10630 $forbiddenphpstrings = array('$$', '$_', '}[');
10631 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10632
10633 $forbiddenphpfunctions = array();
10634 // @phpcs:ignore
10635 $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
10636 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10637 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10638 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10639 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
10640 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10641 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10642 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10643 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
10644 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
10645
10646 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10647
10648 $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
10649
10650 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10651
10652 do {
10653 $oldstringtoclean = $s;
10654 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10655 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10656 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10657 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10658 } while ($oldstringtoclean != $s);
10659
10660
10661 if (strpos($s, '__forbiddenstring__') !== false) {
10662 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10663 if ($returnvalue) {
10664 return 'Bad string syntax to evaluate: '.$s;
10665 } else {
10666 dol_syslog('Bad string syntax to evaluate: '.$s);
10667 return '';
10668 }
10669 }
10670
10671 //print $s."<br>\n";
10672 if ($returnvalue) {
10673 ob_start(); // An evaluation has no reason to output data
10674 $isObBufferActive = true;
10675 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
10676 $tmpo = ob_get_clean();
10677 $isObBufferActive = false;
10678 if ($tmpo) {
10679 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
10680 }
10681 return $tmps;
10682 } else {
10683 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10684 if ($hideerrors) {
10685 @eval($s);
10686 } else {
10687 eval($s);
10688 }
10689 return '';
10690 }
10691 } catch (Error $e) {
10692 if ($isObBufferActive) {
10693 // Clean up buffer which was left behind due to exception.
10694 $tmpo = ob_get_clean();
10695 $isObBufferActive = false;
10696 }
10697 $error = 'dol_eval try/catch error : ';
10698 $error .= $e->getMessage();
10699 dol_syslog($error, LOG_WARNING);
10700 if ($returnvalue) {
10701 return 'Exception during evaluation: '.$s;
10702 } else {
10703 return '';
10704 }
10705 }
10706}
10707
10715function dol_validElement($element)
10716{
10717 return (trim($element) != '');
10718}
10719
10728function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10729{
10730 if (empty($codelang)) {
10731 return '';
10732 }
10733
10734 if ($codelang == 'auto') {
10735 return '<span class="fa fa-language"></span>';
10736 }
10737
10738 $langtocountryflag = array(
10739 'ar_AR' => '',
10740 'ca_ES' => 'catalonia',
10741 'da_DA' => 'dk',
10742 'fr_CA' => 'mq',
10743 'sv_SV' => 'se',
10744 'sw_SW' => 'unknown',
10745 'AQ' => 'unknown',
10746 'CW' => 'unknown',
10747 'IM' => 'unknown',
10748 'JE' => 'unknown',
10749 'MF' => 'unknown',
10750 'BL' => 'unknown',
10751 'SX' => 'unknown'
10752 );
10753
10754 if (isset($langtocountryflag[$codelang])) {
10755 $flagImage = $langtocountryflag[$codelang];
10756 } else {
10757 $tmparray = explode('_', $codelang);
10758 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10759 }
10760
10761 $morecss = '';
10762 $reg = array();
10763 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10764 $morecss = $reg[1];
10765 $moreatt = "";
10766 }
10767
10768 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10769 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10770}
10771
10779function getLanguageCodeFromCountryCode($countrycode)
10780{
10781 global $mysoc;
10782
10783 if (empty($countrycode)) {
10784 return null;
10785 }
10786
10787 if (strtoupper($countrycode) == 'MQ') {
10788 return 'fr_CA';
10789 }
10790 if (strtoupper($countrycode) == 'SE') {
10791 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10792 }
10793 if (strtoupper($countrycode) == 'CH') {
10794 if ($mysoc->country_code == 'FR') {
10795 return 'fr_CH';
10796 }
10797 if ($mysoc->country_code == 'DE') {
10798 return 'de_CH';
10799 }
10800 if ($mysoc->country_code == 'IT') {
10801 return 'it_CH';
10802 }
10803 }
10804
10805 // Locale list taken from:
10806 // http://stackoverflow.com/questions/3191664/
10807 // list-of-all-locales-and-their-short-codes
10808 $locales = array(
10809 'af-ZA',
10810 'am-ET',
10811 'ar-AE',
10812 'ar-BH',
10813 'ar-DZ',
10814 'ar-EG',
10815 'ar-IQ',
10816 'ar-JO',
10817 'ar-KW',
10818 'ar-LB',
10819 'ar-LY',
10820 'ar-MA',
10821 'ar-OM',
10822 'ar-QA',
10823 'ar-SA',
10824 'ar-SY',
10825 'ar-TN',
10826 'ar-YE',
10827 //'as-IN', // Moved after en-IN
10828 'ba-RU',
10829 'be-BY',
10830 'bg-BG',
10831 'bn-BD',
10832 //'bn-IN', // Moved after en-IN
10833 'bo-CN',
10834 'br-FR',
10835 'ca-ES',
10836 'co-FR',
10837 'cs-CZ',
10838 'cy-GB',
10839 'da-DK',
10840 'de-AT',
10841 'de-CH',
10842 'de-DE',
10843 'de-LI',
10844 'de-LU',
10845 'dv-MV',
10846 'el-GR',
10847 'en-AU',
10848 'en-BZ',
10849 'en-CA',
10850 'en-GB',
10851 'en-IE',
10852 'en-IN',
10853 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10854 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10855 'en-JM',
10856 'en-MY',
10857 'en-NZ',
10858 'en-PH',
10859 'en-SG',
10860 'en-TT',
10861 'en-US',
10862 'en-ZA',
10863 'en-ZW',
10864 'es-AR',
10865 'es-BO',
10866 'es-CL',
10867 'es-CO',
10868 'es-CR',
10869 'es-DO',
10870 'es-EC',
10871 'es-ES',
10872 'es-GT',
10873 'es-HN',
10874 'es-MX',
10875 'es-NI',
10876 'es-PA',
10877 'es-PE',
10878 'es-PR',
10879 'es-PY',
10880 'es-SV',
10881 'es-US',
10882 'es-UY',
10883 'es-VE',
10884 'et-EE',
10885 'eu-ES',
10886 'fa-IR',
10887 'fi-FI',
10888 'fo-FO',
10889 'fr-BE',
10890 'fr-CA',
10891 'fr-CH',
10892 'fr-FR',
10893 'fr-LU',
10894 'fr-MC',
10895 'fy-NL',
10896 'ga-IE',
10897 'gd-GB',
10898 'gl-ES',
10899 'gu-IN',
10900 'he-IL',
10901 'hi-IN',
10902 'hr-BA',
10903 'hr-HR',
10904 'hu-HU',
10905 'hy-AM',
10906 'id-ID',
10907 'ig-NG',
10908 'ii-CN',
10909 'is-IS',
10910 'it-CH',
10911 'it-IT',
10912 'ja-JP',
10913 'ka-GE',
10914 'kk-KZ',
10915 'kl-GL',
10916 'km-KH',
10917 'kn-IN',
10918 'ko-KR',
10919 'ky-KG',
10920 'lb-LU',
10921 'lo-LA',
10922 'lt-LT',
10923 'lv-LV',
10924 'mi-NZ',
10925 'mk-MK',
10926 'ml-IN',
10927 'mn-MN',
10928 'mr-IN',
10929 'ms-BN',
10930 'ms-MY',
10931 'mt-MT',
10932 'nb-NO',
10933 'ne-NP',
10934 'nl-BE',
10935 'nl-NL',
10936 'nn-NO',
10937 'oc-FR',
10938 'or-IN',
10939 'pa-IN',
10940 'pl-PL',
10941 'ps-AF',
10942 'pt-BR',
10943 'pt-PT',
10944 'rm-CH',
10945 'ro-MD',
10946 'ro-RO',
10947 'ru-RU',
10948 'rw-RW',
10949 'sa-IN',
10950 'se-FI',
10951 'se-NO',
10952 'se-SE',
10953 'si-LK',
10954 'sk-SK',
10955 'sl-SI',
10956 'sq-AL',
10957 'sv-FI',
10958 'sv-SE',
10959 'sw-KE',
10960 'ta-IN',
10961 'te-IN',
10962 'th-TH',
10963 'tk-TM',
10964 'tn-ZA',
10965 'tr-TR',
10966 'tt-RU',
10967 'ug-CN',
10968 'uk-UA',
10969 'ur-PK',
10970 'vi-VN',
10971 'wo-SN',
10972 'xh-ZA',
10973 'yo-NG',
10974 'zh-CN',
10975 'zh-HK',
10976 'zh-MO',
10977 'zh-SG',
10978 'zh-TW',
10979 'zu-ZA',
10980 );
10981
10982 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
10983 if (in_array($buildprimarykeytotest, $locales)) {
10984 return strtolower($countrycode).'_'.strtoupper($countrycode);
10985 }
10986
10987 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
10988 foreach ($locales as $locale) {
10989 $locale_language = locale_get_primary_language($locale);
10990 $locale_region = locale_get_region($locale);
10991 if (strtoupper($countrycode) == $locale_region) {
10992 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
10993 return strtolower($locale_language).'_'.strtoupper($locale_region);
10994 }
10995 }
10996 } else {
10997 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
10998 }
10999
11000 return null;
11001}
11002
11033function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11034{
11035 global $hookmanager, $db;
11036
11037 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11038 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11039 $values = explode(':', $value);
11040
11041 $reg = array();
11042 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11043 $newtab = array();
11044 $postab = $h;
11045 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11046 $str = $values[1];
11047 $posstart = strpos($str, '(');
11048 if ($posstart > 0) {
11049 $posend = strpos($str, ')');
11050 if ($posstart > 0) {
11051 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11052 if (is_numeric($res1)) {
11053 $postab = (int) $res1;
11054 $values[1] = '+' . substr($str, $posend + 1);
11055 }
11056 }
11057 }
11058 if (count($values) == 6) {
11059 // new declaration with permissions:
11060 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11061 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11062 if ($values[0] != $type) {
11063 continue;
11064 }
11065
11066 if (verifCond($values[4], '2')) {
11067 if ($values[3]) {
11068 if ($filterorigmodule) { // If a filter of module origin has been requested
11069 if (strpos($values[3], '@')) { // This is an external module
11070 if ($filterorigmodule != 'external') {
11071 continue;
11072 }
11073 } else { // This looks a core module
11074 if ($filterorigmodule != 'core') {
11075 continue;
11076 }
11077 }
11078 }
11079 $langs->load($values[3]);
11080 }
11081 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11082 // If label is "SUBSTITUION_..."
11083 $substitutionarray = array();
11084 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11085 $label = make_substitutions($reg[1], $substitutionarray);
11086 } else {
11087 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11088 $labeltemp = explode(',', $values[2]);
11089 $label = $langs->trans($labeltemp[0]);
11090
11091 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11092 dol_include_once($labeltemp[2]);
11093 $classtoload = $labeltemp[1];
11094 if (class_exists($classtoload)) {
11095 $obj = new $classtoload($db);
11096 $function = $labeltemp[3];
11097 if ($obj && $function && method_exists($obj, $function)) {
11098 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11099 $nbrec = $obj->$function($object->id, $obj);
11100 if (!empty($nbrec)) {
11101 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11102 }
11103 }
11104 }
11105 }
11106 }
11107
11108 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11109 $newtab[1] = $label;
11110 $newtab[2] = str_replace('+', '', $values[1]);
11111 $h++;
11112 } else {
11113 continue;
11114 }
11115 } elseif (count($values) == 5) { // case deprecated
11116 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11117
11118 if ($values[0] != $type) {
11119 continue;
11120 }
11121 if ($values[3]) {
11122 if ($filterorigmodule) { // If a filter of module origin has been requested
11123 if (strpos($values[3], '@')) { // This is an external module
11124 if ($filterorigmodule != 'external') {
11125 continue;
11126 }
11127 } else { // This looks a core module
11128 if ($filterorigmodule != 'core') {
11129 continue;
11130 }
11131 }
11132 }
11133 $langs->load($values[3]);
11134 }
11135 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11136 $substitutionarray = array();
11137 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11138 $label = make_substitutions($reg[1], $substitutionarray);
11139 } else {
11140 $label = $langs->trans($values[2]);
11141 }
11142
11143 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11144 $newtab[1] = $label;
11145 $newtab[2] = str_replace('+', '', $values[1]);
11146 $h++;
11147 }
11148 // set tab at its position
11149 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11150 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11151 if ($values[0] != $type) {
11152 continue;
11153 }
11154 $tabname = str_replace('-', '', $values[1]);
11155 foreach ($head as $key => $val) {
11156 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11157 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11158 if ($head[$key][2] == $tabname && $condition) {
11159 unset($head[$key]);
11160 break;
11161 }
11162 }
11163 }
11164 }
11165 }
11166
11167 // No need to make a return $head. Var is modified as a reference
11168 if (!empty($hookmanager)) {
11169 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11170 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11171 if ($reshook > 0) { // Hook ask to replace completely the array
11172 $head = $hookmanager->resArray;
11173 } else { // Hook
11174 $head = array_merge($head, $hookmanager->resArray);
11175 }
11176 $h = count($head);
11177 }
11178}
11179
11191function printCommonFooter($zone = 'private')
11192{
11193 global $conf, $hookmanager, $user, $langs;
11194 global $debugbar;
11195 global $action;
11196 global $micro_start_time;
11197
11198 if ($zone == 'private') {
11199 print "\n".'<!-- Common footer for private page -->'."\n";
11200 } else {
11201 print "\n".'<!-- Common footer for public page -->'."\n";
11202 }
11203
11204 // A div to store page_y POST parameter so we can read it using javascript
11205 print "\n<!-- A div to store page_y POST parameter -->\n";
11206 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11207
11208 $parameters = array();
11209 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11210 if (empty($reshook)) {
11211 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11212 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11213 }
11214
11215 print "\n";
11216 if (!empty($conf->use_javascript_ajax)) {
11217 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11218 print '<script>'."\n";
11219 print 'jQuery(document).ready(function() {'."\n";
11220
11221 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11222 print "\n";
11223 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11224 print 'jQuery("li.menuhider").click(function(event) {';
11225 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11226 print ' console.log("We click on .menuhider");'."\n";
11227 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11228 print '});'."\n";
11229 }
11230
11231 // Management of focus and mandatory for fields
11232 if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))) || ((empty($action) || $action == 'addline') && (preg_match('/card\.php/', $_SERVER["PHP_SELF"])))) {
11233 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11234 $relativepathstring = $_SERVER["PHP_SELF"];
11235 // Clean $relativepathstring
11236 if (constant('DOL_URL_ROOT')) {
11237 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11238 }
11239 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11240 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11241 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11242
11243 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11244 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11245 $qualified = 0;
11246 if ($defkey != '_noquery_') {
11247 $tmpqueryarraytohave = explode('&', $defkey);
11248 $foundintru = 0;
11249 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11250 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11251 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11252 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11253 $foundintru = 1;
11254 }
11255 }
11256 if (!$foundintru) {
11257 $qualified = 1;
11258 }
11259 //var_dump($defkey.'-'.$qualified);
11260 } else {
11261 $qualified = 1;
11262 }
11263
11264 if ($qualified) {
11265 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11266 foreach ($defval as $paramkey => $paramval) {
11267 // Set focus on field
11268 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11269 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11270 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11271 }
11272 }
11273 }
11274 }
11275 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11276 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11277 $qualified = 0;
11278 if ($defkey != '_noquery_') {
11279 $tmpqueryarraytohave = explode('&', $defkey);
11280 $foundintru = 0;
11281 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11282 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11283 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11284 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11285 $foundintru = 1;
11286 }
11287 }
11288 if (!$foundintru) {
11289 $qualified = 1;
11290 }
11291 //var_dump($defkey.'-'.$qualified);
11292 } else {
11293 $qualified = 1;
11294 }
11295
11296 if ($qualified) {
11297 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11298
11299 foreach ($defval as $paramkey => $paramval) {
11300 // Solution 1: Add handler on submit to check if mandatory fields are empty
11301 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11302 print "form.on('submit', function(event) {
11303 var submitter = event.originalEvent.submitter;
11304 if (submitter) {
11305 var buttonName = $(submitter).attr('name');
11306 if (buttonName == 'cancel') {
11307 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11308 return true;
11309 }
11310 }
11311
11312 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11313
11314 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11315 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11316
11317 if (tmptypefield == 'textarea') {
11318 // We must instead check the content of ckeditor
11319 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11320 if (tmpeditor) {
11321 tmpvalue = tmpeditor.getData();
11322 console.log('For textarea tmpvalue is '+tmpvalue);
11323 }
11324 }
11325
11326 let tmpvalueisempty = false;
11327 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
11328 tmpvalueisempty = true;
11329 }
11330 if (tmpvalue === '0' && tmptypefield == 'select') {
11331 tmpvalueisempty = true;
11332 }
11333 if (tmpvalueisempty) {
11334 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11335 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11336 event.stopPropagation(); // Stop other handlers.
11337 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11338 return false;
11339 }
11340 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11341 return true;
11342 });
11343 \n";
11344
11345 // Solution 2: Add property 'required' on input
11346 // so browser will check value and try to focus on it when submitting the form.
11347 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11348 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11349 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11350 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11351 //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";
11352 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11353 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11354 // Add 'field required' class on closest td for all input elements : input, textarea and select
11355 //print '}, 500);'; // 500 milliseconds delay
11356
11357 // Now set the class "fieldrequired"
11358 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11359 }
11360
11361
11362 // If we submit using the cancel button, we remove the required attributes
11363 print 'jQuery("input[name=\'cancel\']").click(function() {
11364 console.log("We click on cancel button so removed all required attribute");
11365 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11366 });'."\n";
11367 }
11368 }
11369 }
11370 }
11371
11372 print '});'."\n";
11373
11374 // End of tuning
11375 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11376 print "\n";
11377 print "/* JS CODE TO ENABLE to add memory info */\n";
11378 print 'window.console && console.log("';
11379 if (getDolGlobalString('MEMCACHED_SERVER')) {
11380 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11381 }
11382 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11383 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11384 $micro_end_time = microtime(true);
11385 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11386 }
11387
11388 if (function_exists("memory_get_usage")) {
11389 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11390 }
11391 if (function_exists("memory_get_peak_usage")) {
11392 print ' - Real mem peak: '.memory_get_peak_usage(true);
11393 }
11394 if (function_exists("zend_loader_file_encoded")) {
11395 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11396 }
11397 print '");'."\n";
11398 }
11399
11400 print "\n".'</script>'."\n";
11401
11402 // Google Analytics
11403 // TODO Add a hook here
11404 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11405 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11406 foreach ($tmptagarray as $tmptag) {
11407 print "\n";
11408 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11409 print '
11410 <!-- Global site tag (gtag.js) - Google Analytics -->
11411 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11412 <script>
11413 window.dataLayer = window.dataLayer || [];
11414 function gtag(){dataLayer.push(arguments);}
11415 gtag(\'js\', new Date());
11416
11417 gtag(\'config\', \''.trim($tmptag).'\');
11418 </script>';
11419 print "\n";
11420 }
11421 }
11422 }
11423
11424 // Add Xdebug coverage of code
11425 if (defined('XDEBUGCOVERAGE')) {
11426 print_r(xdebug_get_code_coverage());
11427 }
11428
11429 // Add DebugBar data
11430 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11431 if (isset($debugbar['time'])) {
11432 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11433 $debugbar['time']->stopMeasure('pageaftermaster');
11434 }
11435 print '<!-- Output debugbar data -->'."\n";
11436 $renderer = $debugbar->getJavascriptRenderer();
11437 print $renderer->render();
11438 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11439 print "\n";
11440 print "<!-- Start of log output\n";
11441 //print '<div class="hidden">'."\n";
11442 foreach ($conf->logbuffer as $logline) {
11443 print $logline."<br>\n";
11444 }
11445 //print '</div>'."\n";
11446 print "End of log output -->\n";
11447 }
11448 }
11449}
11450
11460function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11461{
11462 if (is_null($string)) {
11463 return array();
11464 }
11465
11466 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11467 // This is a regex string
11468 $newdelimiter = $delimiter;
11469 } else {
11470 // This is a simple string
11471 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11472 $newdelimiter = preg_quote($delimiter, '/');
11473 }
11474
11475 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11476 $ka = array();
11477 foreach ($a as $s) { // each part
11478 if ($s) {
11479 if ($pos = strpos($s, $kv)) { // key/value delimiter
11480 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11481 } else { // key delimiter not found
11482 $ka[] = trim($s);
11483 }
11484 }
11485 }
11486 return $ka;
11487 }
11488
11489 return array();
11490}
11491
11492
11499function dol_set_focus($selector)
11500{
11501 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11502 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11503}
11504
11505
11513function dol_getmypid()
11514{
11515 if (!function_exists('getmypid')) {
11516 return mt_rand(99900000, 99965535);
11517 } else {
11518 return getmypid(); // May be a number on 64 bits (depending on OS)
11519 }
11520}
11521
11522
11544function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11545{
11546 global $db, $langs;
11547
11548 $value = trim($value);
11549
11550 if ($mode == 0) {
11551 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11552 }
11553 if ($mode == 1) {
11554 $value = preg_replace('/([!<>=]+)\s+([0-9'.preg_quote($langs->trans("DecimalSeparator"), '/').'\-])/', '\1\2', $value); // Clean string '< 10' into '<10' so we can then explode on space to get all tests to do
11555 }
11556
11557 $value = preg_replace('/\s*\|\s*/', '|', $value);
11558
11559 $crits = explode(' ', $value);
11560 $res = '';
11561 if (!is_array($fields)) {
11562 $fields = array($fields);
11563 }
11564
11565 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11566 foreach ($crits as $crit) { // Loop on each AND criteria
11567 $crit = trim($crit);
11568 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11569 $newres = '';
11570 foreach ($fields as $field) {
11571 if ($mode == 1) {
11572 $tmpcrits = explode('|', $crit);
11573 $i3 = 0; // count the nb of valid criteria added for this current field
11574 foreach ($tmpcrits as $tmpcrit) {
11575 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11576 continue;
11577 }
11578 $tmpcrit = trim($tmpcrit);
11579
11580 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11581
11582 $operator = '=';
11583 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11584
11585 $reg = array();
11586 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11587 if (!empty($reg[1])) {
11588 $operator = $reg[1];
11589 }
11590 if ($newcrit != '') {
11591 $numnewcrit = price2num($newcrit);
11592 if (is_numeric($numnewcrit)) {
11593 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11594 } else {
11595 $newres .= '1 = 2'; // force false, we received a corrupted data
11596 }
11597 $i3++; // a criteria was added to string
11598 }
11599 }
11600 $i2++; // a criteria for 1 more field was added to string
11601 } elseif ($mode == 2 || $mode == -2) {
11602 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11603 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11604 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11605 if ($mode == -2) {
11606 $newres .= ' OR '.$field.' IS NULL';
11607 }
11608 $i2++; // a criteria for 1 more field was added to string
11609 } elseif ($mode == 3 || $mode == -3) {
11610 $tmparray = explode(',', $crit);
11611 if (count($tmparray)) {
11612 $listofcodes = '';
11613 foreach ($tmparray as $val) {
11614 $val = trim($val);
11615 if ($val) {
11616 $listofcodes .= ($listofcodes ? ',' : '');
11617 $listofcodes .= "'".$db->escape($val)."'";
11618 }
11619 }
11620 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
11621 $i2++; // a criteria for 1 more field was added to string
11622 }
11623 if ($mode == -3) {
11624 $newres .= ' OR '.$field.' IS NULL';
11625 }
11626 } elseif ($mode == 4) {
11627 $tmparray = explode(',', $crit);
11628 if (count($tmparray)) {
11629 $listofcodes = '';
11630 foreach ($tmparray as $val) {
11631 $val = trim($val);
11632 if ($val) {
11633 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11634 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11635 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11636 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11637 $newres .= ')';
11638 $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)
11639 }
11640 }
11641 }
11642 } else { // $mode=0
11643 $tmpcrits = explode('|', $crit);
11644 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11645 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11646 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11647 continue;
11648 }
11649 $tmpcrit = trim($tmpcrit);
11650
11651 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11652 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11653 } else {
11654 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11655 }
11656
11657 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11658 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11659 } else {
11660 $tmpcrit2 = $tmpcrit;
11661 $tmpbefore = '%';
11662 $tmpafter = '%';
11663 $tmps = '';
11664
11665 if (preg_match('/^!/', $tmpcrit)) {
11666 $tmps .= $field." NOT LIKE "; // ! as exclude character
11667 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11668 } else {
11669 $tmps .= $field." LIKE ";
11670 }
11671 $tmps .= "'";
11672
11673 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11674 $tmpbefore = '';
11675 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11676 }
11677 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11678 $tmpafter = '';
11679 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11680 }
11681
11682 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11683 $tmps = "(".$tmps;
11684 }
11685 $newres .= $tmps;
11686 $newres .= $tmpbefore;
11687 $newres .= $db->escape($tmpcrit2);
11688 $newres .= $tmpafter;
11689 $newres .= "'";
11690 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11691 $newres .= " OR ".$field." IS NULL)";
11692 }
11693 }
11694
11695 $i3++;
11696 }
11697
11698 $i2++; // a criteria for 1 more field was added to string
11699 }
11700 }
11701
11702 if ($newres) {
11703 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11704 }
11705 $i1++;
11706 }
11707 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11708
11709 return $res;
11710}
11711
11718function showDirectDownloadLink($object)
11719{
11720 global $conf, $langs;
11721
11722 $out = '';
11723 $url = $object->getLastMainDocLink($object->element);
11724
11725 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11726 if ($url) {
11727 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11728 $out .= ajax_autoselect("directdownloadlink", '');
11729 } else {
11730 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11731 }
11732
11733 return $out;
11734}
11735
11744function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11745{
11746 $dirName = dirname($file);
11747 if ($dirName == '.') {
11748 $dirName = '';
11749 }
11750
11751 if (!in_array($extName, array('', '_small', '_mini'))) {
11752 return 'Bad parameter extName';
11753 }
11754
11755 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
11756 $fileName = basename($fileName);
11757
11758 if (empty($extImgTarget)) {
11759 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11760 }
11761 if (empty($extImgTarget)) {
11762 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11763 }
11764 if (empty($extImgTarget)) {
11765 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11766 }
11767 if (empty($extImgTarget)) {
11768 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11769 }
11770 if (empty($extImgTarget)) {
11771 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11772 }
11773 if (empty($extImgTarget)) {
11774 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11775 }
11776
11777 if (!$extImgTarget) {
11778 return $file;
11779 }
11780
11781 $subdir = '';
11782 if ($extName) {
11783 $subdir = 'thumbs/';
11784 }
11785
11786 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11787}
11788
11789
11799function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11800{
11801 global $conf, $langs;
11802
11803 if (empty($conf->use_javascript_ajax)) {
11804 return '';
11805 }
11806
11807 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11808
11809 if ($alldata == 1) {
11810 if ($isAllowedForPreview) {
11811 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));
11812 } else {
11813 return array();
11814 }
11815 }
11816
11817 // old behavior, return a string
11818 if ($isAllowedForPreview) {
11819 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11820 $title = $langs->transnoentities("Preview");
11821 //$title = '%27-alert(document.domain)-%27';
11822 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg");
11823
11824 // We need to urlencode the parameter after the dol_escape_js($tmpurl) because $tmpurl may contain n url with param file=abc%27def if file has a ' inside.
11825 // and when we click on href with this javascript string, a urlcode is done by browser, converted the %27 of file param
11826 return 'javascript:document_preview(\''.urlencode(dol_escape_js($tmpurl)).'\', \''.urlencode(dol_mimetype($relativepath)).'\', \''.urlencode(dol_escape_js($title)).'\')';
11827 } else {
11828 return '';
11829 }
11830}
11831
11832
11841function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11842{
11843 global $langs;
11844 $out = '<script nonce="'.getNonce().'">
11845 jQuery(document).ready(function () {
11846 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11847 });
11848 </script>';
11849 if ($addlink) {
11850 if ($textonlink === 'image') {
11851 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11852 } else {
11853 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11854 }
11855 }
11856 return $out;
11857}
11858
11866function dolIsAllowedForPreview($file)
11867{
11868 // Check .noexe extension in filename
11869 if (preg_match('/\.noexe$/i', $file)) {
11870 return 0;
11871 }
11872
11873 // Check mime types
11874 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11875 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11876 $mime_preview[] = 'svg+xml';
11877 }
11878 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11879 //$mime_preview[]='archive';
11880 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11881 if ($num_mime !== false) {
11882 return 1;
11883 }
11884
11885 // By default, not allowed for preview
11886 return 0;
11887}
11888
11889
11899function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11900{
11901 $mime = $default;
11902 $imgmime = 'other.png';
11903 $famime = 'file-o';
11904 $srclang = '';
11905
11906 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11907
11908 // Plain text files
11909 if (preg_match('/\.txt$/i', $tmpfile)) {
11910 $mime = 'text/plain';
11911 $imgmime = 'text.png';
11912 $famime = 'file-alt';
11913 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
11914 $mime = 'text/richtext';
11915 $imgmime = 'text.png';
11916 $famime = 'file-alt';
11917 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
11918 $mime = 'text/csv';
11919 $imgmime = 'text.png';
11920 $famime = 'file-csv';
11921 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
11922 $mime = 'text/tab-separated-values';
11923 $imgmime = 'text.png';
11924 $famime = 'file-alt';
11925 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
11926 $mime = 'text/plain';
11927 $imgmime = 'text.png';
11928 $famime = 'file-alt';
11929 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
11930 $mime = 'text/plain';
11931 $imgmime = 'text.png';
11932 $srclang = 'ini';
11933 $famime = 'file-alt';
11934 } elseif (preg_match('/\.md$/i', $tmpfile)) {
11935 $mime = 'text/plain';
11936 $imgmime = 'text.png';
11937 $srclang = 'md';
11938 $famime = 'file-alt';
11939 } elseif (preg_match('/\.css$/i', $tmpfile)) {
11940 $mime = 'text/css';
11941 $imgmime = 'css.png';
11942 $srclang = 'css';
11943 $famime = 'file-alt';
11944 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
11945 $mime = 'text/plain';
11946 $imgmime = 'text.png';
11947 $srclang = 'lang';
11948 $famime = 'file-alt';
11949 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
11950 $mime = 'text/plain';
11951 $imgmime = 'text.png';
11952 $famime = 'file-alt';
11953 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
11954 $mime = 'text/html';
11955 $imgmime = 'html.png';
11956 $srclang = 'html';
11957 $famime = 'file-alt';
11958 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
11959 $mime = 'text/xml';
11960 $imgmime = 'other.png';
11961 $srclang = 'xml';
11962 $famime = 'file-alt';
11963 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
11964 $mime = 'text/xml';
11965 $imgmime = 'other.png';
11966 $srclang = 'xaml';
11967 $famime = 'file-alt';
11968 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
11969 $mime = 'text/plain';
11970 $imgmime = 'text.png';
11971 $srclang = 'bas';
11972 $famime = 'file-code';
11973 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
11974 $mime = 'text/plain';
11975 $imgmime = 'text.png';
11976 $srclang = 'c';
11977 $famime = 'file-code';
11978 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
11979 $mime = 'text/plain';
11980 $imgmime = 'text.png';
11981 $srclang = 'cpp';
11982 $famime = 'file-code';
11983 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
11984 $mime = 'text/plain';
11985 $imgmime = 'text.png';
11986 $srclang = 'cs';
11987 $famime = 'file-code';
11988 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
11989 $mime = 'text/plain';
11990 $imgmime = 'text.png';
11991 $srclang = 'h';
11992 $famime = 'file-code';
11993 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
11994 $mime = 'text/plain';
11995 $imgmime = 'text.png';
11996 $srclang = 'java';
11997 $famime = 'file-code';
11998 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
11999 $mime = 'text/plain';
12000 $imgmime = 'php.png';
12001 $srclang = 'php';
12002 $famime = 'file-code';
12003 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12004 $mime = 'text/plain';
12005 $imgmime = 'php.png';
12006 $srclang = 'php';
12007 $famime = 'file-code';
12008 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12009 $mime = 'text/plain';
12010 $imgmime = 'pl.png';
12011 $srclang = 'perl';
12012 $famime = 'file-code';
12013 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12014 $mime = 'text/plain';
12015 $imgmime = 'text.png';
12016 $srclang = 'sql';
12017 $famime = 'file-code';
12018 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12019 $mime = 'text/x-javascript';
12020 $imgmime = 'jscript.png';
12021 $srclang = 'js';
12022 $famime = 'file-code';
12023 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12024 $mime = 'application/vnd.oasis.opendocument.presentation';
12025 $imgmime = 'ooffice.png';
12026 $famime = 'file-powerpoint';
12027 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12028 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12029 $imgmime = 'ooffice.png';
12030 $famime = 'file-excel';
12031 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12032 $mime = 'application/vnd.oasis.opendocument.text';
12033 $imgmime = 'ooffice.png';
12034 $famime = 'file-word';
12035 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12036 $mime = 'application/msaccess';
12037 $imgmime = 'mdb.png';
12038 $famime = 'file';
12039 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12040 $mime = 'application/msword';
12041 $imgmime = 'doc.png';
12042 $famime = 'file-word';
12043 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12044 $mime = 'application/msword';
12045 $imgmime = 'doc.png';
12046 $famime = 'file-word';
12047 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12048 $mime = 'application/vnd.ms-excel';
12049 $imgmime = 'xls.png';
12050 $famime = 'file-excel';
12051 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12052 $mime = 'application/vnd.ms-excel';
12053 $imgmime = 'xls.png';
12054 $famime = 'file-excel';
12055 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12056 $mime = 'application/vnd.ms-excel';
12057 $imgmime = 'xls.png';
12058 $famime = 'file-excel';
12059 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12060 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12061 $imgmime = 'xls.png';
12062 $famime = 'file-excel';
12063 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12064 $mime = 'application/vnd.ms-powerpoint';
12065 $imgmime = 'ppt.png';
12066 $famime = 'file-powerpoint';
12067 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12068 $mime = 'application/x-mspowerpoint';
12069 $imgmime = 'ppt.png';
12070 $famime = 'file-powerpoint';
12071 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12072 $mime = 'application/pdf';
12073 $imgmime = 'pdf.png';
12074 $famime = 'file-pdf';
12075 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12076 $mime = 'text/x-bat';
12077 $imgmime = 'script.png';
12078 $srclang = 'dos';
12079 $famime = 'file-code';
12080 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12081 $mime = 'text/x-sh';
12082 $imgmime = 'script.png';
12083 $srclang = 'bash';
12084 $famime = 'file-code';
12085 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12086 $mime = 'text/x-ksh';
12087 $imgmime = 'script.png';
12088 $srclang = 'bash';
12089 $famime = 'file-code';
12090 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12091 $mime = 'text/x-bash';
12092 $imgmime = 'script.png';
12093 $srclang = 'bash';
12094 $famime = 'file-code';
12095 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12096 $mime = 'image/x-icon';
12097 $imgmime = 'image.png';
12098 $famime = 'file-image';
12099 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12100 $mime = 'image/jpeg';
12101 $imgmime = 'image.png';
12102 $famime = 'file-image';
12103 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12104 $mime = 'image/png';
12105 $imgmime = 'image.png';
12106 $famime = 'file-image';
12107 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12108 $mime = 'image/gif';
12109 $imgmime = 'image.png';
12110 $famime = 'file-image';
12111 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12112 $mime = 'image/bmp';
12113 $imgmime = 'image.png';
12114 $famime = 'file-image';
12115 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12116 $mime = 'image/tiff';
12117 $imgmime = 'image.png';
12118 $famime = 'file-image';
12119 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12120 $mime = 'image/svg+xml';
12121 $imgmime = 'image.png';
12122 $famime = 'file-image';
12123 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12124 $mime = 'image/webp';
12125 $imgmime = 'image.png';
12126 $famime = 'file-image';
12127 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12128 $mime = 'text/calendar';
12129 $imgmime = 'other.png';
12130 $famime = 'file-alt';
12131 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12132 $mime = 'text/calendar';
12133 $imgmime = 'other.png';
12134 $famime = 'file-alt';
12135 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12136 $mime = 'application/x-bittorrent';
12137 $imgmime = 'other.png';
12138 $famime = 'file-o';
12139 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12140 $mime = 'audio';
12141 $imgmime = 'audio.png';
12142 $famime = 'file-audio';
12143 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12144 $mime = 'video/mp4';
12145 $imgmime = 'video.png';
12146 $famime = 'file-video';
12147 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12148 $mime = 'video/ogg';
12149 $imgmime = 'video.png';
12150 $famime = 'file-video';
12151 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12152 $mime = 'video/webm';
12153 $imgmime = 'video.png';
12154 $famime = 'file-video';
12155 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12156 $mime = 'video/x-msvideo';
12157 $imgmime = 'video.png';
12158 $famime = 'file-video';
12159 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12160 $mime = 'video/divx';
12161 $imgmime = 'video.png';
12162 $famime = 'file-video';
12163 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12164 $mime = 'video/xvid';
12165 $imgmime = 'video.png';
12166 $famime = 'file-video';
12167 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12168 $mime = 'video';
12169 $imgmime = 'video.png';
12170 $famime = 'file-video';
12171 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12172 // application/xxx where zzz is zip, ...
12173 $mime = 'archive';
12174 $imgmime = 'archive.png';
12175 $famime = 'file-archive';
12176 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12177 $mime = 'application/octet-stream';
12178 $imgmime = 'other.png';
12179 $famime = 'file-o';
12180 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12181 $mime = 'library';
12182 $imgmime = 'library.png';
12183 $famime = 'file-o';
12184 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
12185 $mime = 'error';
12186 $imgmime = 'error.png';
12187 $famime = 'file-alt';
12188 }
12189
12190 // Return mimetype string
12191 switch ((int) $mode) {
12192 case 1:
12193 $tmp = explode('/', $mime);
12194 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12195 case 2:
12196 return $imgmime;
12197 case 3:
12198 return $srclang;
12199 case 4:
12200 return $famime;
12201 }
12202 return $mime;
12203}
12204
12216function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12217{
12218 global $conf, $db;
12219
12220 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12221
12222 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12223
12224 if (is_null($dictvalues)) {
12225 $dictvalues = array();
12226
12227 $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
12228 if ($checkentity) {
12229 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12230 }
12231
12232 $resql = $db->query($sql);
12233 if ($resql) {
12234 while ($obj = $db->fetch_object($resql)) {
12235 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12236 }
12237 } else {
12238 dol_print_error($db);
12239 }
12240
12241 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12242 }
12243
12244 if (!empty($dictvalues[$id])) {
12245 // Found
12246 $tmp = $dictvalues[$id];
12247 return (property_exists($tmp, $field) ? $tmp->$field : '');
12248 } else {
12249 // Not found
12250 return '';
12251 }
12252}
12253
12260function colorIsLight($stringcolor)
12261{
12262 $stringcolor = str_replace('#', '', $stringcolor);
12263 $res = -1;
12264 if (!empty($stringcolor)) {
12265 $res = 0;
12266 $tmp = explode(',', $stringcolor);
12267 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12268 $r = $tmp[0];
12269 $g = $tmp[1];
12270 $b = $tmp[2];
12271 } else {
12272 $hexr = $stringcolor[0].$stringcolor[1];
12273 $hexg = $stringcolor[2].$stringcolor[3];
12274 $hexb = $stringcolor[4].$stringcolor[5];
12275 $r = hexdec($hexr);
12276 $g = hexdec($hexg);
12277 $b = hexdec($hexb);
12278 }
12279 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12280 if ($bright > 0.6) {
12281 $res = 1;
12282 }
12283 }
12284 return $res;
12285}
12286
12295function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12296{
12297 global $conf;
12298
12299 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12300 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12301 if (empty($menuentry['enabled'])) {
12302 return 0; // Entry disabled by condition
12303 }
12304 if ($type_user && $menuentry['module']) {
12305 $tmploops = explode('|', $menuentry['module']);
12306 $found = 0;
12307 foreach ($tmploops as $tmploop) {
12308 if (in_array($tmploop, $listofmodulesforexternal)) {
12309 $found++;
12310 break;
12311 }
12312 }
12313 if (!$found) {
12314 return 0; // Entry is for menus all excluded to external users
12315 }
12316 }
12317 if (!$menuentry['perms'] && $type_user) {
12318 return 0; // No permissions and user is external
12319 }
12320 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12321 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12322 }
12323 if (!$menuentry['perms']) {
12324 return 2; // No permissions and user is external
12325 }
12326 return 1;
12327}
12328
12336function roundUpToNextMultiple($n, $x = 5)
12337{
12338 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12339 return (int) $result;
12340}
12341
12353function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12354{
12355 $csstouse = 'badge';
12356 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12357 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12358 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12359
12360 $attr = array(
12361 'class' => $csstouse
12362 );
12363
12364 if (empty($html)) {
12365 $html = $label;
12366 }
12367
12368 if (!empty($url)) {
12369 $attr['href'] = $url;
12370 }
12371
12372 if ($mode === 'dot') {
12373 $attr['class'] .= ' classfortooltip';
12374 $attr['title'] = $html;
12375 $attr['aria-label'] = $label;
12376 $html = '';
12377 }
12378
12379 // Override attr
12380 if (!empty($params['attr']) && is_array($params['attr'])) {
12381 foreach ($params['attr'] as $key => $value) {
12382 if ($key == 'class') {
12383 $attr['class'] .= ' '.$value;
12384 } elseif ($key == 'classOverride') {
12385 $attr['class'] = $value;
12386 } else {
12387 $attr[$key] = $value;
12388 }
12389 }
12390 }
12391
12392 // TODO: add hook
12393
12394 // escape all attribute
12395 $attr = array_map('dol_escape_htmltag', $attr);
12396
12397 $TCompiledAttr = array();
12398 foreach ($attr as $key => $value) {
12399 $TCompiledAttr[] = $key.'="'.$value.'"';
12400 }
12401
12402 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12403
12404 $tag = !empty($url) ? 'a' : 'span';
12405
12406 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12407}
12408
12409
12422function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12423{
12424 global $conf;
12425
12426 $return = '';
12427 $dolGetBadgeParams = array();
12428
12429 if (!empty($params['badgeParams'])) {
12430 $dolGetBadgeParams = $params['badgeParams'];
12431 }
12432
12433 // TODO : add a hook
12434 if ($displayMode == 0) {
12435 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12436 } elseif ($displayMode == 1) {
12437 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12438 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12439 // Use status with images (for backward compatibility)
12440 $return = '';
12441 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12442 $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>' : '');
12443
12444 // For small screen, we always use the short label instead of long label.
12445 if (!empty($conf->dol_optimize_smallscreen)) {
12446 if ($displayMode == 0) {
12447 $displayMode = 1;
12448 } elseif ($displayMode == 4) {
12449 $displayMode = 2;
12450 } elseif ($displayMode == 6) {
12451 $displayMode = 5;
12452 }
12453 }
12454
12455 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12456 $statusImg = array(
12457 'status0' => 'statut0',
12458 'status1' => 'statut1',
12459 'status2' => 'statut2',
12460 'status3' => 'statut3',
12461 'status4' => 'statut4',
12462 'status5' => 'statut5',
12463 'status6' => 'statut6',
12464 'status7' => 'statut7',
12465 'status8' => 'statut8',
12466 'status9' => 'statut9'
12467 );
12468
12469 if (!empty($statusImg[$statusType])) {
12470 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12471 } else {
12472 $htmlImg = img_picto($statusLabel, $statusType);
12473 }
12474
12475 if ($displayMode === 2) {
12476 $return = $htmlImg.' '.$htmlLabelShort;
12477 } elseif ($displayMode === 3) {
12478 $return = $htmlImg;
12479 } elseif ($displayMode === 4) {
12480 $return = $htmlImg.' '.$htmlLabel;
12481 } elseif ($displayMode === 5) {
12482 $return = $htmlLabelShort.' '.$htmlImg;
12483 } else { // $displayMode >= 6
12484 $return = $htmlLabel.' '.$htmlImg;
12485 }
12486 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12487 // Use new badge
12488 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12489
12490 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12491 if (empty($dolGetBadgeParams['attr']['title'])) {
12492 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12493 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12494 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12495 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
12496 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12497 }
12498
12499 if ($displayMode == 3) {
12500 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12501 } elseif ($displayMode === 5) {
12502 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12503 } else {
12504 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12505 }
12506 }
12507
12508 return $return;
12509}
12510
12511
12550function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12551{
12552 global $hookmanager, $action, $object, $langs;
12553
12554 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12555 if (is_array($url)) {
12556 // Loop on $url array to remove entries of disabled modules
12557 foreach ($url as $key => $subbutton) {
12558 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12559 unset($url[$key]);
12560 }
12561 }
12562
12563 $out = '';
12564
12565 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
12566 foreach ($url as $button) {
12567 if (!empty($button['lang'])) {
12568 $langs->load($button['lang']);
12569 }
12570 $label = $langs->trans($button['label']);
12571 $text = $button['text'] ?? '';
12572 $actionType = $button['actionType'] ?? '';
12573 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12574 $id = $button['id'] ?? '';
12575 $userRight = $button['perm'] ?? 1;
12576 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
12577
12578 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12579 }
12580 return $out;
12581 }
12582
12583 if (count($url) > 1) {
12584 $out .= '<div class="dropdown inline-block dropdown-holder">';
12585 $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>';
12586 $out .= '<div class="dropdown-content">';
12587 foreach ($url as $subbutton) {
12588 if (!empty($subbutton['lang'])) {
12589 $langs->load($subbutton['lang']);
12590 }
12591
12592 if (!empty($subbutton['urlraw'])) {
12593 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12594 } else {
12595 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12596 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12597 }
12598
12599 $subbuttonparam = array();
12600 if (!empty($subbutton['attr'])) {
12601 $subbuttonparam['attr'] = $subbutton['attr'];
12602 }
12603 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
12604
12605 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12606 }
12607 $out .= "</div>";
12608 $out .= "</div>";
12609 } else {
12610 foreach ($url as $subbutton) { // Should loop on 1 record only
12611 if (!empty($subbutton['lang'])) {
12612 $langs->load($subbutton['lang']);
12613 }
12614
12615 if (!empty($subbutton['urlraw'])) {
12616 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12617 } else {
12618 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12619 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12620 }
12621
12622 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12623 }
12624 }
12625
12626 return $out;
12627 }
12628
12629 // Here, $url is a simple link
12630
12631 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12632 $class = "dropdown-item";
12633 } else {
12634 $class = 'butAction';
12635 if ($actionType == 'danger' || $actionType == 'delete') {
12636 $class = 'butActionDelete';
12637 if (!empty($url) && strpos($url, 'token=') === false) {
12638 $url .= '&token='.newToken();
12639 }
12640 }
12641 }
12642 $attr = array(
12643 'class' => $class,
12644 'href' => empty($url) ? '' : $url,
12645 'title' => $label
12646 );
12647
12648 if (empty($text)) {
12649 $text = $label;
12650 $attr['title'] = ''; // if html not set, leave label on title is redundant
12651 } else {
12652 $attr['title'] = $label;
12653 $attr['aria-label'] = $label;
12654 }
12655
12656 if (empty($userRight)) {
12657 $attr['class'] = 'butActionRefused';
12658 $attr['href'] = '';
12659 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12660 $attr['title'] = ($attr['title'] ? $attr['title'].'<br>' : '').$langs->trans('NotEnoughPermissions');
12661 }
12662
12663 if (!empty($id)) {
12664 $attr['id'] = $id;
12665 }
12666
12667 // Override attr
12668 if (!empty($params['attr']) && is_array($params['attr'])) {
12669 foreach ($params['attr'] as $key => $value) {
12670 if ($key == 'class') {
12671 $attr['class'] .= ' '.$value;
12672 } elseif ($key == 'classOverride') {
12673 $attr['class'] = $value;
12674 } else {
12675 $attr[$key] = $value;
12676 }
12677 }
12678 }
12679
12680 // automatic add tooltip when title is detected
12681 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12682 $attr['class'] .= ' classfortooltip';
12683 }
12684
12685 // Js Confirm button
12686 if ($userRight && !empty($params['confirm'])) {
12687 if (!is_array($params['confirm'])) {
12688 $params['confirm'] = array();
12689 }
12690
12691 if (empty($params['confirm']['url'])) {
12692 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12693 }
12694
12695 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12696 $attr['data-confirm-url'] = $params['confirm']['url'];
12697 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12698 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12699 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12700 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12701 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12702 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12703
12704 $attr['class'] .= ' butActionConfirm';
12705 }
12706
12707 if (isset($attr['href']) && empty($attr['href'])) {
12708 unset($attr['href']);
12709 }
12710
12711 // escape all attribute
12712 $attr = array_map('dol_escape_htmltag', $attr);
12713
12714 $TCompiledAttr = array();
12715 foreach ($attr as $key => $value) {
12716 $TCompiledAttr[] = $key.'= "'.$value.'"';
12717 }
12718
12719 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12720
12721 $tag = !empty($attr['href']) ? 'a' : 'span';
12722
12723
12724 $parameters = array(
12725 'TCompiledAttr' => $TCompiledAttr, // array
12726 'compiledAttributes' => $compiledAttributes, // string
12727 'attr' => $attr,
12728 'tag' => $tag,
12729 'label' => $label,
12730 'html' => $text,
12731 'actionType' => $actionType,
12732 'url' => $url,
12733 'id' => $id,
12734 'userRight' => $userRight,
12735 'params' => $params
12736 );
12737
12738 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12739 if ($reshook < 0) {
12740 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12741 }
12742
12743 if (empty($reshook)) {
12744 if (dol_textishtml($text)) { // If content already HTML encoded
12745 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
12746 } else {
12747 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
12748 }
12749 } else {
12750 return $hookmanager->resPrint;
12751 }
12752}
12753
12754
12763function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
12764{
12765 if (empty($url)) {
12766 return '';
12767 }
12768
12769 $parsedUrl = parse_url($url);
12770 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
12771 return $url;
12772 }
12773
12774 if (!empty($parsedUrl['query'])) {
12775 // Use parse_str() function to parse the string passed via URL
12776 parse_str($parsedUrl['query'], $urlQuery);
12777 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
12778 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
12779 }
12780 }
12781
12782 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
12783 $url = DOL_URL_ROOT.$url;
12784 }
12785
12786 return $url;
12787}
12788
12789
12796function dolGetButtonTitleSeparator($moreClass = "")
12797{
12798 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12799}
12800
12807function getFieldErrorIcon($fieldValidationErrorMsg)
12808{
12809 $out = '';
12810 if (!empty($fieldValidationErrorMsg)) {
12811 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12812 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12813 $out .= '</span>';
12814 }
12815
12816 return $out;
12817}
12818
12831function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12832{
12833 global $langs, $conf, $user;
12834
12835 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12836 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12837 return '';
12838 }
12839
12840 $class = 'btnTitle';
12841 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12842 $class .= ' btnTitlePlus';
12843 }
12844 $useclassfortooltip = 1;
12845
12846 if (!empty($params['morecss'])) {
12847 $class .= ' '.$params['morecss'];
12848 }
12849
12850 $attr = array(
12851 'class' => $class,
12852 'href' => empty($url) ? '' : $url
12853 );
12854
12855 if (!empty($helpText)) {
12856 $attr['title'] = dol_escape_htmltag($helpText);
12857 } elseif ($label) { // empty($attr['title']) &&
12858 $attr['title'] = $label;
12859 $useclassfortooltip = 0;
12860 }
12861
12862 if ($status == 2) {
12863 $attr['class'] .= ' btnTitleSelected';
12864 } elseif ($status <= 0) {
12865 $attr['class'] .= ' refused';
12866
12867 $attr['href'] = '';
12868
12869 if ($status == -1) { // disable
12870 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12871 } elseif ($status == 0) { // Not enough permissions
12872 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12873 }
12874 }
12875
12876 if (!empty($attr['title']) && $useclassfortooltip) {
12877 $attr['class'] .= ' classfortooltip';
12878 }
12879
12880 if (!empty($id)) {
12881 $attr['id'] = $id;
12882 }
12883
12884 // Override attr
12885 if (!empty($params['attr']) && is_array($params['attr'])) {
12886 foreach ($params['attr'] as $key => $value) {
12887 if ($key == 'class') {
12888 $attr['class'] .= ' '.$value;
12889 } elseif ($key == 'classOverride') {
12890 $attr['class'] = $value;
12891 } else {
12892 $attr[$key] = $value;
12893 }
12894 }
12895 }
12896
12897 if (isset($attr['href']) && empty($attr['href'])) {
12898 unset($attr['href']);
12899 }
12900
12901 // TODO : add a hook
12902
12903 $TCompiledAttr = array();
12904 foreach ($attr as $key => $value) {
12905 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
12906 }
12907
12908 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12909
12910 $tag = (empty($attr['href']) ? 'span' : 'a');
12911
12912 $button = '<'.$tag.' '.$compiledAttributes.'>';
12913 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
12914 if (!empty($params['forcenohideoftext'])) {
12915 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
12916 }
12917 $button .= '</'.$tag.'>';
12918
12919 return $button;
12920}
12921
12931function getElementProperties($elementType)
12932{
12933 global $conf, $db, $hookmanager;
12934
12935 $regs = array();
12936
12937 //$element_type='facture';
12938
12939 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
12940
12941 // Parse element/subelement
12942 $module = $elementType;
12943 $element = $elementType;
12944 $subelement = $elementType;
12945 $table_element = $elementType;
12946
12947 // If we ask a resource form external module (instead of default path)
12948 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
12949 $element = $subelement = $regs[1];
12950 $module = $regs[2];
12951 }
12952
12953 // If we ask a resource for a string with an element and a subelement
12954 // Example 'project_task'
12955 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
12956 $module = $element = $regs[1];
12957 $subelement = $regs[2];
12958 }
12959
12960 // Object lines will use parent classpath and module ref
12961 if (substr($elementType, -3) == 'det') {
12962 $module = preg_replace('/det$/', '', $element);
12963 $subelement = preg_replace('/det$/', '', $subelement);
12964 $classpath = $module.'/class';
12965 $classfile = $module;
12966 $classname = preg_replace('/det$/', 'Line', $element);
12967 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
12968 $classname = preg_replace('/det$/', 'Ligne', $element);
12969 }
12970 }
12971 // For compatibility and to work with non standard path
12972 if ($elementType == "action" || $elementType == "actioncomm") {
12973 $classpath = 'comm/action/class';
12974 $subelement = 'Actioncomm';
12975 $module = 'agenda';
12976 $table_element = 'actioncomm';
12977 } elseif ($elementType == 'cronjob') {
12978 $classpath = 'cron/class';
12979 $module = 'cron';
12980 $table_element = 'cron';
12981 } elseif ($elementType == 'adherent_type') {
12982 $classpath = 'adherents/class';
12983 $classfile = 'adherent_type';
12984 $module = 'adherent';
12985 $subelement = 'adherent_type';
12986 $classname = 'AdherentType';
12987 $table_element = 'adherent_type';
12988 } elseif ($elementType == 'bank_account') {
12989 $classpath = 'compta/bank/class';
12990 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
12991 $classfile = 'account';
12992 $classname = 'Account';
12993 } elseif ($elementType == 'category') {
12994 $classpath = 'categories/class';
12995 $module = 'categorie';
12996 $subelement = 'categorie';
12997 $table_element = 'categorie';
12998 } elseif ($elementType == 'contact') {
12999 $classpath = 'contact/class';
13000 $classfile = 'contact';
13001 $module = 'societe';
13002 $subelement = 'contact';
13003 $table_element = 'socpeople';
13004 } elseif ($elementType == 'inventory') {
13005 $module = 'product';
13006 $classpath = 'product/inventory/class';
13007 } elseif ($elementType == 'inventoryline') {
13008 $module = 'product';
13009 $classpath = 'product/inventory/class';
13010 $table_element = 'inventorydet';
13011 $parent_element = 'inventory';
13012 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
13013 $module = 'stock';
13014 $classpath = 'product/stock/class';
13015 $classfile = 'entrepot';
13016 $classname = 'Entrepot';
13017 $table_element = 'entrepot';
13018 } elseif ($elementType == 'project') {
13019 $classpath = 'projet/class';
13020 $module = 'projet';
13021 $table_element = 'projet';
13022 } elseif ($elementType == 'project_task') {
13023 $classpath = 'projet/class';
13024 $module = 'projet';
13025 $subelement = 'task';
13026 $table_element = 'projet_task';
13027 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13028 $classpath = 'compta/facture/class';
13029 $module = 'facture';
13030 $subelement = 'facture';
13031 $table_element = 'facture';
13032 } elseif ($elementType == 'facturedet') {
13033 $classpath = 'compta/facture/class';
13034 $classfile = 'facture';
13035 $classname = 'FactureLigne';
13036 $module = 'facture';
13037 $table_element = 'facturedet';
13038 $parent_element = 'facture';
13039 } elseif ($elementType == 'facturerec') {
13040 $classpath = 'compta/facture/class';
13041 $module = 'facture';
13042 $classname = 'FactureRec';
13043 } elseif ($elementType == 'commande' || $elementType == 'order') {
13044 $classpath = 'commande/class';
13045 $module = 'commande';
13046 $subelement = 'commande';
13047 $table_element = 'commande';
13048 } elseif ($elementType == 'commandedet') {
13049 $classpath = 'commande/class';
13050 $classfile = 'commande';
13051 $classname = 'OrderLine';
13052 $module = 'commande';
13053 $table_element = 'commandedet';
13054 $parent_element = 'commande';
13055 } elseif ($elementType == 'propal') {
13056 $classpath = 'comm/propal/class';
13057 $table_element = 'propal';
13058 } elseif ($elementType == 'propaldet') {
13059 $classpath = 'comm/propal/class';
13060 $classfile = 'propal';
13061 $subelement = 'propaleligne';
13062 $module = 'propal';
13063 $table_element = 'propaldet';
13064 $parent_element = 'propal';
13065 } elseif ($elementType == 'shipping') {
13066 $classpath = 'expedition/class';
13067 $classfile = 'expedition';
13068 $classname = 'Expedition';
13069 $module = 'expedition';
13070 $table_element = 'expedition';
13071 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13072 $classpath = 'expedition/class';
13073 $classfile = 'expedition';
13074 $classname = 'ExpeditionLigne';
13075 $module = 'expedition';
13076 $table_element = 'expeditiondet';
13077 $parent_element = 'expedition';
13078 } elseif ($elementType == 'delivery_note') {
13079 $classpath = 'delivery/class';
13080 $subelement = 'delivery';
13081 $module = 'expedition';
13082 } elseif ($elementType == 'delivery') {
13083 $classpath = 'delivery/class';
13084 $subelement = 'delivery';
13085 $module = 'expedition';
13086 } elseif ($elementType == 'deliverydet') {
13087 // @todo
13088 } elseif ($elementType == 'supplier_proposal') {
13089 $classpath = 'supplier_proposal/class';
13090 $module = 'supplier_proposal';
13091 $element = 'supplierproposal';
13092 $classfile = 'supplier_proposal';
13093 $subelement = 'supplierproposal';
13094 } elseif ($elementType == 'supplier_proposaldet') {
13095 $classpath = 'supplier_proposal/class';
13096 $module = 'supplier_proposal';
13097 $classfile = 'supplier_proposal';
13098 $table_element = 'supplier_proposaldet';
13099 $parent_element = 'supplier_proposal';
13100 } elseif ($elementType == 'contract') {
13101 $classpath = 'contrat/class';
13102 $module = 'contrat';
13103 $subelement = 'contrat';
13104 $table_element = 'contract';
13105 } elseif ($elementType == 'contratdet') {
13106 $classpath = 'contrat/class';
13107 $module = 'contrat';
13108 $table_element = 'contratdet';
13109 $parent_element = 'contrat';
13110 } elseif ($elementType == 'mailing') {
13111 $classpath = 'comm/mailing/class';
13112 $module = 'mailing';
13113 $classfile = 'mailing';
13114 $classname = 'Mailing';
13115 $subelement = '';
13116 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13117 $classpath = 'adherents/class';
13118 $module = 'adherent';
13119 $subelement = 'adherent';
13120 $table_element = 'adherent';
13121 } elseif ($elementType == 'usergroup') {
13122 $classpath = 'user/class';
13123 $module = 'user';
13124 } elseif ($elementType == 'mo') {
13125 $classpath = 'mrp/class';
13126 $classfile = 'mo';
13127 $classname = 'Mo';
13128 $module = 'mrp';
13129 $subelement = '';
13130 $table_element = 'mrp_mo';
13131 } elseif ($elementType == 'mrp_production') {
13132 $classpath = 'mrp/class';
13133 $classfile = 'mo';
13134 $classname = 'MoLine';
13135 $module = 'mrp';
13136 $subelement = '';
13137 $table_element = 'mrp_production';
13138 $parent_element = 'mo';
13139 } elseif ($elementType == 'cabinetmed_cons') {
13140 $classpath = 'cabinetmed/class';
13141 $module = 'cabinetmed';
13142 $subelement = 'cabinetmedcons';
13143 $table_element = 'cabinetmedcons';
13144 } elseif ($elementType == 'fichinter') {
13145 $classpath = 'fichinter/class';
13146 $module = 'ficheinter';
13147 $subelement = 'fichinter';
13148 $table_element = 'fichinter';
13149 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13150 $classpath = 'resource/class';
13151 $module = 'resource';
13152 $subelement = 'dolresource';
13153 $table_element = 'resource';
13154 } elseif ($elementType == 'opensurvey_sondage') {
13155 $classpath = 'opensurvey/class';
13156 $module = 'opensurvey';
13157 $subelement = 'opensurveysondage';
13158 } elseif ($elementType == 'order_supplier' || $elementType == 'commande_fournisseur') {
13159 $classpath = 'fourn/class';
13160 $module = 'fournisseur';
13161 $classfile = 'fournisseur.commande';
13162 $element = 'order_supplier';
13163 $subelement = '';
13164 $classname = 'CommandeFournisseur';
13165 $table_element = 'commande_fournisseur';
13166 } elseif ($elementType == 'commande_fournisseurdet') {
13167 $classpath = 'fourn/class';
13168 $module = 'fournisseur';
13169 $classfile = 'fournisseur.commande';
13170 $element = 'commande_fournisseurdet';
13171 $subelement = '';
13172 $classname = 'CommandeFournisseurLigne';
13173 $table_element = 'commande_fournisseurdet';
13174 $parent_element = 'commande_fournisseur';
13175 } elseif ($elementType == 'invoice_supplier') {
13176 $classpath = 'fourn/class';
13177 $module = 'fournisseur';
13178 $classfile = 'fournisseur.facture';
13179 $element = 'invoice_supplier';
13180 $subelement = '';
13181 $classname = 'FactureFournisseur';
13182 $table_element = 'facture_fourn';
13183 } elseif ($elementType == 'facture_fourn_det') {
13184 $classpath = 'fourn/class';
13185 $module = 'fournisseur';
13186 $classfile = 'fournisseur.facture';
13187 $element = 'facture_fourn_det';
13188 $subelement = '';
13189 $classname = 'SupplierInvoiceLine';
13190 $table_element = 'facture_fourn_det';
13191 $parent_element = 'invoice_supplier';
13192 } elseif ($elementType == "service") {
13193 $classpath = 'product/class';
13194 $subelement = 'product';
13195 $table_element = 'product';
13196 } elseif ($elementType == 'salary') {
13197 $classpath = 'salaries/class';
13198 $module = 'salaries';
13199 } elseif ($elementType == 'payment_salary') {
13200 $classpath = 'salaries/class';
13201 $classfile = 'paymentsalary';
13202 $classname = 'PaymentSalary';
13203 $module = 'salaries';
13204 } elseif ($elementType == 'productlot') {
13205 $module = 'productbatch';
13206 $classpath = 'product/stock/class';
13207 $classfile = 'productlot';
13208 $classname = 'Productlot';
13209 $element = 'productlot';
13210 $subelement = '';
13211 $table_element = 'product_lot';
13212 } elseif ($elementType == 'societeaccount') {
13213 $classpath = 'societe/class';
13214 $classfile = 'societeaccount';
13215 $classname = 'SocieteAccount';
13216 $module = 'societe';
13217 } elseif ($elementType == 'websitepage') {
13218 $classpath = 'website/class';
13219 $classfile = 'websitepage';
13220 $classname = 'Websitepage';
13221 $module = 'website';
13222 $subelement = 'websitepage';
13223 $table_element = 'website_page';
13224 } elseif ($elementType == 'fiscalyear') {
13225 $classpath = 'core/class';
13226 $module = 'accounting';
13227 $subelement = 'fiscalyear';
13228 } elseif ($elementType == 'chargesociales') {
13229 $classpath = 'compta/sociales/class';
13230 $module = 'tax';
13231 $table_element = 'chargesociales';
13232 } elseif ($elementType == 'tva') {
13233 $classpath = 'compta/tva/class';
13234 $module = 'tax';
13235 $subdir = '/vat';
13236 $table_element = 'tva';
13237 } elseif ($elementType == 'emailsenderprofile') {
13238 $module = '';
13239 $classpath = 'core/class';
13240 $classfile = 'emailsenderprofile';
13241 $classname = 'EmailSenderProfile';
13242 $table_element = 'c_email_senderprofile';
13243 $subelement = '';
13244 } elseif ($elementType == 'conferenceorboothattendee') {
13245 $classpath = 'eventorganization/class';
13246 $classfile = 'conferenceorboothattendee';
13247 $classname = 'ConferenceOrBoothAttendee';
13248 $module = 'eventorganization';
13249 } elseif ($elementType == 'conferenceorbooth') {
13250 $classpath = 'eventorganization/class';
13251 $classfile = 'conferenceorbooth';
13252 $classname = 'ConferenceOrBooth';
13253 $module = 'eventorganization';
13254 } elseif ($elementType == 'ccountry') {
13255 $module = '';
13256 $classpath = 'core/class';
13257 $classfile = 'ccountry';
13258 $classname = 'Ccountry';
13259 $table_element = 'c_country';
13260 $subelement = '';
13261 } elseif ($elementType == 'ecmfiles') {
13262 $module = 'ecm';
13263 $classpath = 'ecm/class';
13264 $classfile = 'ecmfiles';
13265 $classname = 'Ecmfiles';
13266 $table_element = 'ecmfiles';
13267 $subelement = '';
13268 } elseif ($elementType == 'knowledgerecord') {
13269 $module = '';
13270 $classpath = 'knowledgemanagement/class';
13271 $classfile = 'knowledgerecord';
13272 $classname = 'KnowledgeRecord';
13273 $table_element = 'knowledgemanagement_knowledgerecord';
13274 $subelement = '';
13275 }
13276
13277 if (empty($classfile)) {
13278 $classfile = strtolower($subelement);
13279 }
13280 if (empty($classname)) {
13281 $classname = ucfirst($subelement);
13282 }
13283 if (empty($classpath)) {
13284 $classpath = $module.'/class';
13285 }
13286
13287 //print 'getElementProperties subdir='.$subdir;
13288
13289 // Set dir_output
13290 if ($module && isset($conf->$module)) { // The generic case
13291 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13292 $dir_output = $conf->$module->multidir_output[$conf->entity];
13293 } elseif (!empty($conf->$module->output[$conf->entity])) {
13294 $dir_output = $conf->$module->output[$conf->entity];
13295 } elseif (!empty($conf->$module->dir_output)) {
13296 $dir_output = $conf->$module->dir_output;
13297 }
13298 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
13299 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
13300 } elseif (!empty($conf->$module->temp[$conf->entity])) {
13301 $dir_temp = $conf->$module->temp[$conf->entity];
13302 } elseif (!empty($conf->$module->dir_temp)) {
13303 $dir_temp = $conf->$module->dir_temp;
13304 }
13305 }
13306
13307 // Overwrite value for special cases
13308 if ($element == 'order_supplier') {
13309 $dir_output = $conf->fournisseur->commande->dir_output;
13310 $dir_temp = $conf->fournisseur->commande->dir_temp;
13311 } elseif ($element == 'invoice_supplier') {
13312 $dir_output = $conf->fournisseur->facture->dir_output;
13313 $dir_temp = $conf->fournisseur->facture->dir_temp;
13314 }
13315 $dir_output .= $subdir;
13316 $dir_temp .= $subdir;
13317
13318 $elementProperties = array(
13319 'module' => $module,
13320 'element' => $element,
13321 'table_element' => $table_element,
13322 'subelement' => $subelement,
13323 'classpath' => $classpath,
13324 'classfile' => $classfile,
13325 'classname' => $classname,
13326 'dir_output' => $dir_output,
13327 'dir_temp' => $dir_temp,
13328 'parent_element' => $parent_element,
13329 );
13330
13331
13332 // Add hook
13333 if (!is_object($hookmanager)) {
13334 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13335 $hookmanager = new HookManager($db);
13336 }
13337 $hookmanager->initHooks(array('elementproperties'));
13338
13339
13340 // Hook params
13341 $parameters = array(
13342 'elementType' => $elementType,
13343 'elementProperties' => $elementProperties
13344 );
13345
13346 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13347
13348 if ($reshook) {
13349 $elementProperties = $hookmanager->resArray;
13350 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13351 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13352 }
13353
13354 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13355 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13356 unset($hookmanager->contextarray[$key]);
13357 }
13358
13359 return $elementProperties;
13360}
13361
13374function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13375{
13376 global $db, $conf;
13377
13378 $ret = 0;
13379
13380 $element_prop = getElementProperties($element_type);
13381
13382 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13383 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13384 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13385 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13386 // of service and we will return properties of a product.
13387 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13388 } elseif ($element_prop['module'] == 'societeaccount') {
13389 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13390 } else {
13391 $ismodenabled = isModEnabled($element_prop['module']);
13392 }
13393 //var_dump('element_type='.$element_type);
13394 //var_dump($element_prop);
13395 //var_dump($element_prop['module'].' '.$ismodenabled);
13396 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13397 if ($useCache === 1
13398 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13399 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13400 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13401 ) {
13402 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13403 }
13404
13405 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13406
13407 if (class_exists($element_prop['classname'])) {
13408 $className = $element_prop['classname'];
13409 $objecttmp = new $className($db);
13410 '@phan-var-force CommonObject $objecttmp';
13411
13412 if ($element_id > 0 || !empty($element_ref)) {
13413 $ret = $objecttmp->fetch($element_id, $element_ref);
13414 if ($ret >= 0) {
13415 if (empty($objecttmp->module)) {
13416 $objecttmp->module = $element_prop['module'];
13417 }
13418
13419 if ($useCache > 0) {
13420 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13421 $conf->cache['fetchObjectByElement'][$element_type] = [];
13422 }
13423
13424 // Manage cache limit
13425 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13426 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13427 }
13428
13429 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13430 }
13431
13432 return $objecttmp;
13433 }
13434 } else {
13435 return $objecttmp; // returned an object without fetch
13436 }
13437 } else {
13438 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13439 return -1;
13440 }
13441 }
13442
13443 return $ret;
13444}
13445
13452function isAFileWithExecutableContent($filename)
13453{
13454 if (preg_match('/\.(htm|html|js|phar|php|php\d+|phtml|pht|pl|py|cgi|ksh|sh|shtml|bash|bat|cmd|wpk|exe|dmg)$/i', $filename)) {
13455 return true;
13456 }
13457
13458 return false;
13459}
13460
13468function newToken()
13469{
13470 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13471}
13472
13480function currentToken()
13481{
13482 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13483}
13484
13490function getNonce()
13491{
13492 global $conf;
13493
13494 if (empty($conf->cache['nonce'])) {
13495 $conf->cache['nonce'] = dolGetRandomBytes(8);
13496 }
13497
13498 return $conf->cache['nonce'];
13499}
13500
13501
13515function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13516{
13517 global $langs;
13518
13519 print '<div class="div-table-responsive-no-min">';
13520 print '<table class="noborder centpercent">';
13521 print '<tr class="liste_titre">';
13522
13523 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13524
13525 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13526
13527 if (!empty($link)) {
13528 if (!empty($arguments)) {
13529 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13530 } else {
13531 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13532 }
13533 }
13534
13535 if ($number > -1) {
13536 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13537 } elseif (!empty($link)) {
13538 print '<span class="badge marginleftonlyshort">...</span>';
13539 }
13540
13541 if (!empty($link)) {
13542 print '</a>';
13543 }
13544
13545 print '</th>';
13546
13547 if ($number < 0 && !empty($link)) {
13548 print '<th class="right">';
13549 print '</th>';
13550 }
13551
13552 print '</tr>';
13553}
13554
13563function finishSimpleTable($addLineBreak = false)
13564{
13565 print '</table>';
13566 print '</div>';
13567
13568 if ($addLineBreak) {
13569 print '<br>';
13570 }
13571}
13572
13584function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13585{
13586 global $langs;
13587
13588 if ($num === 0) {
13589 print '<tr class="oddeven">';
13590 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13591 print '</tr>';
13592 return;
13593 }
13594
13595 if ($nbofloop === 0) {
13596 // don't show a summary line
13597 return;
13598 }
13599
13600 /* Case already handled above, commented to satisfy phpstan.
13601 if ($num === 0) {
13602 $colspan = $tableColumnCount;
13603 } else
13604 */
13605 if ($num > $nbofloop) {
13606 $colspan = $tableColumnCount;
13607 } else {
13608 $colspan = $tableColumnCount - 1;
13609 }
13610
13611 if ($extraRightColumn) {
13612 $colspan--;
13613 }
13614
13615 print '<tr class="liste_total">';
13616
13617 if ($nbofloop > 0 && $num > $nbofloop) {
13618 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13619 } else {
13620 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13621 print '<td class="right centpercent">'.price($total).'</td>';
13622 }
13623
13624 if ($extraRightColumn) {
13625 print '<td></td>';
13626 }
13627
13628 print '</tr>';
13629}
13630
13639function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13640{
13641 if ($method == -1) {
13642 $method = 0;
13643 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13644 $method = 1;
13645 }
13646 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13647 $method = 2;
13648 }
13649 }
13650
13651 // Be sure we don't have output buffering enabled to have readfile working correctly
13652 while (ob_get_level()) {
13653 ob_end_flush();
13654 }
13655
13656 // Solution 0
13657 if ($method == 0) {
13658 readfile($fullpath_original_file_osencoded);
13659 } elseif ($method == 1) {
13660 // Solution 1
13661 $handle = fopen($fullpath_original_file_osencoded, "rb");
13662 while (!feof($handle)) {
13663 print fread($handle, 8192);
13664 }
13665 fclose($handle);
13666 } elseif ($method == 2) {
13667 // Solution 2
13668 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13669 $handle2 = fopen("php://output", "wb");
13670 stream_copy_to_stream($handle1, $handle2);
13671 fclose($handle1);
13672 fclose($handle2);
13673 }
13674}
13675
13685function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13686{
13687 global $langs;
13688
13689 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13690
13691 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
13692 if ($texttoshow === 'none') {
13693 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13694 $result .= '<span class="clipboardCPValueToPrint"></span>';
13695 } elseif ($texttoshow) {
13696 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13697 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
13698 } else {
13699 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13700 }
13701 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
13702 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
13703 $result .= '<span class="clipboardCPText"></span>';
13704 $result .= '</span>';
13705
13706 return $result;
13707}
13708
13709
13716function jsonOrUnserialize($stringtodecode)
13717{
13718 $result = json_decode($stringtodecode);
13719 if ($result === null) {
13720 $result = unserialize($stringtodecode);
13721 }
13722
13723 return $result;
13724}
13725
13726
13743function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13744{
13745 global $db, $user;
13746
13747 if (is_null($filter) || !is_string($filter) || $filter === '') {
13748 return '';
13749 }
13750 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13751 $filter = '(' . $filter . ')';
13752 }
13753
13754 $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'
13755 $firstandlastparenthesis = 0;
13756
13757 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13758 if ($noerror) {
13759 return '1 = 2';
13760 } else {
13761 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13762 }
13763 }
13764
13765 // Test the filter syntax
13766 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13767 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
13768 // If the string result contains something else than '()', the syntax was wrong
13769
13770 if (preg_match('/[^\‍(\‍)]/', $t)) {
13771 $tmperrorstr = 'Bad syntax of the search string';
13772 $errorstr = 'Bad syntax of the search string: '.$filter;
13773 if ($noerror) {
13774 return '1 = 2';
13775 } else {
13776 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
13777 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13778 }
13779 }
13780
13781 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
13782
13783 if (is_object($db)) {
13784 $ret = str_replace('__NOW__', $db->idate(dol_now()), $ret);
13785 }
13786 if (is_object($user)) {
13787 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13788 }
13789
13790 return $ret;
13791}
13792
13800function dolForgeExplodeAnd($sqlfilters)
13801{
13802 $arrayofandtags = array();
13803 $nbofchars = dol_strlen($sqlfilters);
13804
13805 $error = '';
13806 $parenthesislevel = 0;
13807 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13808 if (!$result) {
13809 return array();
13810 }
13811 if ($parenthesislevel >= 1) {
13812 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13813 }
13814
13815 $i = 0;
13816 $s = '';
13817 $countparenthesis = 0;
13818 while ($i < $nbofchars) {
13819 $char = dol_substr($sqlfilters, $i, 1);
13820
13821 if ($char == '(') {
13822 $countparenthesis++;
13823 } elseif ($char == ')') {
13824 $countparenthesis--;
13825 }
13826
13827 if ($countparenthesis == 0) {
13828 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13829 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13830 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13831 // We found a AND
13832 $s = trim($s);
13833 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13834 $s = '('.$s.')';
13835 }
13836 $arrayofandtags[] = $s;
13837 $s = '';
13838 $i += 2;
13839 } else {
13840 $s .= $char;
13841 }
13842 } else {
13843 $s .= $char;
13844 }
13845 $i++;
13846 }
13847 if ($s) {
13848 $s = trim($s);
13849 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13850 $s = '('.$s.')';
13851 }
13852 $arrayofandtags[] = $s;
13853 }
13854
13855 return $arrayofandtags;
13856}
13857
13867function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13868{
13869 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13870 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13871 $tmp = $sqlfilters;
13872
13873 $nb = dol_strlen($tmp);
13874 $counter = 0;
13875 $parenthesislevel = 0;
13876
13877 $error = '';
13878
13879 $i = 0;
13880 while ($i < $nb) {
13881 $char = dol_substr($tmp, $i, 1);
13882
13883 if ($char == '(') {
13884 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13885 // We open a parenthesis and it is the first char
13886 $parenthesislevel++;
13887 }
13888 $counter++;
13889 } elseif ($char == ')') {
13890 $nbcharremaining = ($nb - $i - 1);
13891 if ($nbcharremaining >= $counter) {
13892 $parenthesislevel = min($parenthesislevel, $counter - 1);
13893 }
13894 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13895 $parenthesislevel = $counter;
13896 }
13897 $counter--;
13898 }
13899
13900 if ($counter < 0) {
13901 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13902 $parenthesislevel = 0;
13903 dol_syslog($error, LOG_WARNING);
13904 return false;
13905 }
13906
13907 $i++;
13908 }
13909
13910 if ($counter > 0) {
13911 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13912 $parenthesislevel = 0;
13913 dol_syslog($error, LOG_WARNING);
13914 return false;
13915 }
13916
13917 return true;
13918}
13919
13927function dolForgeDummyCriteriaCallback($matches)
13928{
13929 //dol_syslog("Convert matches ".$matches[1]);
13930 if (empty($matches[1])) {
13931 return '';
13932 }
13933 $tmp = explode(':', $matches[1]);
13934 if (count($tmp) < 3) {
13935 return '';
13936 }
13937
13938 return '()'; // An empty criteria
13939}
13940
13949function dolForgeSQLCriteriaCallback($matches)
13950{
13951 global $db;
13952
13953 //dol_syslog("Convert matches ".$matches[1]);
13954 if (empty($matches[1])) {
13955 return '';
13956 }
13957 $tmp = explode(':', $matches[1], 3);
13958 if (count($tmp) < 3) {
13959 return '';
13960 }
13961
13962 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
13963
13964 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
13965
13966 $realOperator = [
13967 'NOTLIKE' => 'NOT LIKE',
13968 'ISNOT' => 'IS NOT',
13969 'NOTIN' => 'NOT IN',
13970 '!=' => '<>',
13971 ];
13972
13973 if (array_key_exists($operator, $realOperator)) {
13974 $operator = $realOperator[$operator];
13975 }
13976
13977 $tmpescaped = $tmp[2];
13978
13979 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
13980
13981 $regbis = array();
13982
13983 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
13984 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
13985 $tmpescaped2 = '(';
13986 // Explode and sanitize each element in list
13987 $tmpelemarray = explode(',', $tmpescaped);
13988 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
13989 $reg = array();
13990 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
13991 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
13992 } else {
13993 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
13994 }
13995 }
13996 $tmpescaped2 .= implode(',', $tmpelemarray);
13997 $tmpescaped2 .= ')';
13998
13999 $tmpescaped = $tmpescaped2;
14000 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14001 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14002 $tmpescaped = $regbis[1];
14003 }
14004 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14005 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14006 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14007 // TODO Retrieve type of field for $operand field name.
14008 // So we can complete format. For example we could complete a year with month and day.
14009 $tmpescaped = "'".$db->escape($regbis[1])."'";
14010 } else {
14011 if (strtoupper($tmpescaped) == 'NULL') {
14012 $tmpescaped = 'NULL';
14013 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
14014 $tmpescaped = (int) $tmpescaped;
14015 } else {
14016 $tmpescaped = (float) $tmpescaped;
14017 }
14018 }
14019
14020 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14021}
14022
14023
14033function getTimelineIcon($actionstatic, &$histo, $key)
14034{
14035 global $langs;
14036
14037 $out = '<!-- timeline icon -->'."\n";
14038 $iconClass = 'fa fa-comments';
14039 $img_picto = '';
14040 $colorClass = '';
14041 $pictoTitle = '';
14042
14043 if ($histo[$key]['percent'] == -1) {
14044 $colorClass = 'timeline-icon-not-applicble';
14045 $pictoTitle = $langs->trans('StatusNotApplicable');
14046 } elseif ($histo[$key]['percent'] == 0) {
14047 $colorClass = 'timeline-icon-todo';
14048 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14049 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14050 $colorClass = 'timeline-icon-in-progress';
14051 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14052 } elseif ($histo[$key]['percent'] >= 100) {
14053 $colorClass = 'timeline-icon-done';
14054 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14055 }
14056
14057 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14058 $iconClass = 'fa fa-ticket';
14059 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14060 $iconClass = 'fa fa-pencilxxx';
14061 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14062 $iconClass = 'fa fa-comments';
14063 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14064 $iconClass = 'fa fa-mask';
14065 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14066 if ($actionstatic->type_picto) {
14067 $img_picto = img_picto('', $actionstatic->type_picto);
14068 } else {
14069 if ($actionstatic->type_code == 'AC_RDV') {
14070 $iconClass = 'fa fa-handshake';
14071 } elseif ($actionstatic->type_code == 'AC_TEL') {
14072 $iconClass = 'fa fa-phone';
14073 } elseif ($actionstatic->type_code == 'AC_FAX') {
14074 $iconClass = 'fa fa-fax';
14075 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14076 $iconClass = 'fa fa-envelope';
14077 } elseif ($actionstatic->type_code == 'AC_INT') {
14078 $iconClass = 'fa fa-shipping-fast';
14079 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14080 $iconClass = 'fa fa-robot';
14081 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14082 $iconClass = 'fa fa-robot';
14083 }
14084 }
14085 }
14086
14087 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14088 return $out;
14089}
14090
14098{
14099 global $conf, $db;
14100
14101 $documents = array();
14102
14103 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
14104 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14105 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14106 //$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
14107 $sql .= ' ORDER BY ecm.position ASC';
14108
14109 $resql = $db->query($sql);
14110 if ($resql) {
14111 if ($db->num_rows($resql)) {
14112 while ($obj = $db->fetch_object($resql)) {
14113 $documents[$obj->id] = $obj;
14114 }
14115 }
14116 }
14117
14118 return $documents;
14119}
14120
14121
14139function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
14140{
14141 global $user, $conf;
14142 global $form;
14143
14144 global $param, $massactionbutton;
14145
14146 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
14147
14148 // Check parameters
14149 if (!is_object($filterobj) && !is_object($objcon)) {
14150 dol_print_error(null, 'BadParameter');
14151 }
14152
14153 $histo = array();
14154 '@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';
14155
14156 $numaction = 0;
14157 $now = dol_now();
14158
14159 $sortfield_list = explode(',', $sortfield);
14160 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
14161 $sortfield_new_list = array();
14162 foreach ($sortfield_list as $sortfield_value) {
14163 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
14164 }
14165 $sortfield_new = implode(',', $sortfield_new_list);
14166
14167 $sql = null;
14168 $sql2 = null;
14169
14170 if (isModEnabled('agenda')) {
14171 // Search histo on actioncomm
14172 if (is_object($objcon) && $objcon->id > 0) {
14173 $sql = "SELECT DISTINCT a.id, a.label as label,";
14174 } else {
14175 $sql = "SELECT a.id, a.label as label,";
14176 }
14177 $sql .= " a.datep as dp,";
14178 $sql .= " a.note as message,";
14179 $sql .= " a.datep2 as dp2,";
14180 $sql .= " a.percent as percent, 'action' as type,";
14181 $sql .= " a.fk_element, a.elementtype,";
14182 $sql .= " a.fk_contact,";
14183 $sql .= " a.email_from as msg_from,";
14184 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
14185 $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";
14186 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14187 $sql .= ", sp.lastname, sp.firstname";
14188 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14189 $sql .= ", m.lastname, m.firstname";
14190 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
14191 $sql .= ", o.ref";
14192 }
14193 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
14194 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
14195 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
14196
14197 $force_filter_contact = $filterobj instanceof User;
14198
14199 if (is_object($objcon) && $objcon->id > 0) {
14200 $force_filter_contact = true;
14201 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
14202 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
14203 }
14204
14205 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
14206 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
14207 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
14208 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
14209 $sql .= " ON er.resource_type = 'dolresource'";
14210 $sql .= " AND er.element_id = a.id";
14211 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
14212 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14213 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
14214 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14215 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
14216 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14217 $sql .= ", ".MAIN_DB_PREFIX."product as o";
14218 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14219 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
14220 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14221 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
14222 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14223 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
14224 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14225 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
14226 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14227 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
14228 }
14229
14230 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
14231 if (!$force_filter_contact) {
14232 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
14233 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
14234 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
14235 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
14236 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14237 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
14238 if ($filterobj->id) {
14239 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14240 }
14241 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
14242 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
14243 if ($filterobj->id) {
14244 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14245 }
14246 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14247 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
14248 if ($filterobj->id) {
14249 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14250 }
14251 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14252 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
14253 if ($filterobj->id) {
14254 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14255 }
14256 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14257 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
14258 if ($filterobj->id) {
14259 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14260 }
14261 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14262 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
14263 if ($filterobj->id) {
14264 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14265 }
14266 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14267 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
14268 if ($filterobj->id) {
14269 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14270 }
14271 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
14272 $sql .= " AND a.fk_contact = sp.rowid";
14273 if ($filterobj->id) {
14274 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
14275 }
14276 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14277 $sql .= " AND a.fk_element = o.rowid";
14278 if ($filterobj->id) {
14279 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
14280 }
14281 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14282 $sql .= " AND a.fk_element = o.rowid";
14283 if ($filterobj->id) {
14284 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
14285 }
14286 }
14287 } else {
14288 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
14289 }
14290
14291 // Condition on actioncode
14292 if (!empty($actioncode) && $actioncode != '-1') {
14293 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14294 if ($actioncode == 'AC_NON_AUTO') {
14295 $sql .= " AND c.type != 'systemauto'";
14296 } elseif ($actioncode == 'AC_ALL_AUTO') {
14297 $sql .= " AND c.type = 'systemauto'";
14298 } else {
14299 if ($actioncode == 'AC_OTH') {
14300 $sql .= " AND c.type != 'systemauto'";
14301 } elseif ($actioncode == 'AC_OTH_AUTO') {
14302 $sql .= " AND c.type = 'systemauto'";
14303 }
14304 }
14305 } else {
14306 if ($actioncode == 'AC_NON_AUTO') {
14307 $sql .= " AND c.type != 'systemauto'";
14308 } elseif ($actioncode == 'AC_ALL_AUTO') {
14309 $sql .= " AND c.type = 'systemauto'";
14310 } else {
14311 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14312 }
14313 }
14314 }
14315 if ($donetodo == 'todo') {
14316 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14317 } elseif ($donetodo == 'done') {
14318 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14319 }
14320 if (is_array($filters) && $filters['search_agenda_label']) {
14321 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14322 }
14323 }
14324
14325 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14326 if (isModEnabled('mailing') && !empty($objcon->email)
14327 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14328 $langs->load("mails");
14329
14330 $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";
14331 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14332 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14333 $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
14334 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14335 $sql2 .= ", '' as lastname, '' as firstname";
14336 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14337 $sql2 .= ", '' as lastname, '' as firstname";
14338 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14339 $sql2 .= ", '' as ref";
14340 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14341 $sql2 .= ", '' as ref";
14342 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14343 $sql2 .= ", '' as ref";
14344 }
14345 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14346 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14347 $sql2 .= " AND mc.statut = 1";
14348 $sql2 .= " AND u.rowid = m.fk_user_valid";
14349 $sql2 .= " AND mc.fk_mailing=m.rowid";
14350 }
14351
14352 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14353 if (!empty($sql) && !empty($sql2)) {
14354 $sql = $sql." UNION ".$sql2;
14355 } elseif (empty($sql) && !empty($sql2)) {
14356 $sql = $sql2;
14357 }
14358
14359 //TODO Add navigation with this limits...
14360 $offset = 0;
14361 $limit = 1000;
14362
14363 // Complete request and execute it with limit
14364 $sql .= $db->order($sortfield_new, $sortorder);
14365 if ($limit) {
14366 $sql .= $db->plimit($limit + 1, $offset);
14367 }
14368
14369 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14370 $resql = $db->query($sql);
14371 if ($resql) {
14372 $i = 0;
14373 $num = $db->num_rows($resql);
14374
14375 $imaxinloop = ($limit ? min($num, $limit) : $num);
14376 while ($i < $imaxinloop) {
14377 $obj = $db->fetch_object($resql);
14378
14379 if ($obj->type == 'action') {
14380 $contactaction = new ActionComm($db);
14381 $contactaction->id = $obj->id;
14382 $result = $contactaction->fetchResources();
14383 if ($result < 0) {
14384 dol_print_error($db);
14385 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14386 }
14387
14388 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14389 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14390 $tododone = '';
14391 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14392 $tododone = 'todo';
14393 }
14394
14395 $histo[$numaction] = array(
14396 'type' => $obj->type,
14397 'tododone' => $tododone,
14398 'id' => $obj->id,
14399 'datestart' => $db->jdate($obj->dp),
14400 'dateend' => $db->jdate($obj->dp2),
14401 'note' => $obj->label,
14402 'message' => dol_htmlentitiesbr($obj->message),
14403 'percent' => $obj->percent,
14404
14405 'userid' => $obj->user_id,
14406 'login' => $obj->user_login,
14407 'userfirstname' => $obj->user_firstname,
14408 'userlastname' => $obj->user_lastname,
14409 'userphoto' => $obj->user_photo,
14410 'msg_from' => $obj->msg_from,
14411
14412 'contact_id' => $obj->fk_contact,
14413 'socpeopleassigned' => $contactaction->socpeopleassigned,
14414 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14415 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14416 'fk_element' => $obj->fk_element,
14417 'elementtype' => $obj->elementtype,
14418 // Type of event
14419 'acode' => $obj->acode,
14420 'alabel' => $obj->alabel,
14421 'libelle' => $obj->alabel, // deprecated
14422 'apicto' => $obj->apicto
14423 );
14424 } else {
14425 $histo[$numaction] = array(
14426 'type' => $obj->type,
14427 'tododone' => 'done',
14428 'id' => $obj->id,
14429 'datestart' => $db->jdate($obj->dp),
14430 'dateend' => $db->jdate($obj->dp2),
14431 'note' => $obj->label,
14432 'message' => dol_htmlentitiesbr($obj->message),
14433 'percent' => $obj->percent,
14434 'acode' => $obj->acode,
14435
14436 'userid' => $obj->user_id,
14437 'login' => $obj->user_login,
14438 'userfirstname' => $obj->user_firstname,
14439 'userlastname' => $obj->user_lastname,
14440 'userphoto' => $obj->user_photo
14441 );
14442 }
14443
14444 $numaction++;
14445 $i++;
14446 }
14447 } else {
14448 dol_print_error($db);
14449 }
14450 }
14451
14452 // Set $out to show events
14453 $out = '';
14454
14455 if (!isModEnabled('agenda')) {
14456 $langs->loadLangs(array("admin", "errors"));
14457 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14458 }
14459
14460 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14461 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
14462
14463 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14464 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14465 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14466 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14467
14468 $formactions = new FormActions($db);
14469
14470 $actionstatic = new ActionComm($db);
14471 $userstatic = new User($db);
14472 $contactstatic = new Contact($db);
14473 $userGetNomUrlCache = array();
14474 $contactGetNomUrlCache = array();
14475
14476 $out .= '<div class="filters-container" >';
14477 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14478 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14479
14480 if ($objcon && get_class($objcon) == 'Contact' &&
14481 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14482 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14483 } else {
14484 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14485 }
14486 if (($filterobj && get_class($filterobj) == 'Societe')) {
14487 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14488 } else {
14489 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14490 }
14491
14492 $out .= "\n";
14493
14494 $out .= '<div class="div-table-responsive-no-min">';
14495 $out .= '<table class="noborder borderbottom centpercent">';
14496
14497 $out .= '<tr class="liste_titre">';
14498
14499 // Action column
14500 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14501 $out .= '<th class="liste_titre width50 middle">';
14502 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14503 $out .= $searchpicto;
14504 $out .= '</th>';
14505 }
14506
14507 // Date
14508 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
14509
14510 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14511 if ($donetodo) {
14512 $out .= '<th class="liste_titre"></th>';
14513 }
14514 // Type of event
14515 $out .= '<th class="liste_titre">';
14516 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14517 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
14518 $out .= '</th>';
14519 // Label
14520 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14521 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14522 $out .= '</th>';
14523
14524 // Action column
14525 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14526 $out .= '<th class="liste_titre width50 middle">';
14527 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14528 $out .= $searchpicto;
14529 $out .= '</th>';
14530 }
14531
14532 $out .= '</tr>';
14533
14534 $out .= '</table>';
14535
14536 $out .= '</form>';
14537 $out .= '</div>';
14538
14539 $out .= "\n";
14540
14541 $out .= '<ul class="timeline">';
14542
14543 if ($donetodo) {
14544 $tmp = '';
14545 if ($filterobj instanceof Societe) {
14546 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14547 }
14548 if ($filterobj instanceof User) {
14549 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14550 }
14551 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14552 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14553 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14554 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14555 if ($filterobj instanceof Societe) {
14556 $tmp .= '</a>';
14557 }
14558 if ($filterobj instanceof User) {
14559 $tmp .= '</a>';
14560 }
14561 $out .= getTitleFieldOfList($tmp);
14562 }
14563
14564 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14565 $caction = new CActionComm($db);
14566 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14567
14568 $actualCycleDate = false;
14569
14570 // Loop on each event to show it
14571 foreach ($histo as $key => $value) {
14572 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14573
14574 $actionstatic->type_picto = $histo[$key]['apicto'];
14575 $actionstatic->type_code = $histo[$key]['acode'];
14576
14577 $labeltype = $actionstatic->type_code;
14578 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14579 $labeltype = 'AC_OTH';
14580 }
14581 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14582 $labeltype = $langs->trans("Message");
14583 } else {
14584 if (!empty($arraylist[$labeltype])) {
14585 $labeltype = $arraylist[$labeltype];
14586 }
14587 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14588 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14589 }
14590 }
14591
14592 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14593
14594 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14595
14596 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14597 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14598 $out .= '<!-- timeline time label -->';
14599 $out .= '<li class="time-label">';
14600 $out .= '<span class="timeline-badge-date">';
14601 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14602 $out .= '</span>';
14603 $out .= '</li>';
14604 $out .= '<!-- /.timeline-label -->';
14605 }
14606
14607
14608 $out .= '<!-- timeline item -->'."\n";
14609 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
14610
14611 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14612 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14613 //$out .= $timelineicon;
14614 //var_dump($timelineicon);
14615 $out .= $typeicon;
14616
14617 $out .= '<div class="timeline-item">'."\n";
14618
14619 $out .= '<span class="time timeline-header-action2">';
14620
14621 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14622 $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").' ';
14623 $out .= $histo[$key]['id'];
14624 $out .= '</a> ';
14625 } else {
14626 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14627 }
14628
14629 if ($user->hasRight('agenda', 'allactions', 'create') ||
14630 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14631 $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).'">';
14632 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14633 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14634 $out .= '</a>';
14635 }
14636
14637 $out .= '</span>';
14638
14639 // Date
14640 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14641 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14642 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14643 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14644 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14645 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14646 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14647 } else {
14648 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14649 }
14650 }
14651 $late = 0;
14652 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14653 $late = 1;
14654 }
14655 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14656 $late = 1;
14657 }
14658 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14659 $late = 1;
14660 }
14661 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14662 $late = 1;
14663 }
14664 if ($late) {
14665 $out .= img_warning($langs->trans("Late")).' ';
14666 }
14667 $out .= "</span></span>\n";
14668
14669 // Ref
14670 $out .= '<h3 class="timeline-header">';
14671
14672 // Author of event
14673 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14674 if ($histo[$key]['userid'] > 0) {
14675 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14676 $userstatic->fetch($histo[$key]['userid']);
14677 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14678 }
14679 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14680 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14681 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14682 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14683 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14684 } else {
14685 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14686 }
14687 }
14688 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14689 }
14690 $out .= '</div>';
14691
14692 // Title
14693 $out .= ' <div class="messaging-title inline-block">';
14694 //$out .= $actionstatic->getTypePicto();
14695 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14696 $out .= $labeltype.' - ';
14697 }
14698
14699 $libelle = '';
14700 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14701 $out .= $langs->trans('TicketNewMessage');
14702 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14703 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14704 } elseif (isset($histo[$key]['type'])) {
14705 if ($histo[$key]['type'] == 'action') {
14706 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14707 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14708 $libelle = $histo[$key]['note'];
14709 $actionstatic->id = $histo[$key]['id'];
14710 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14711 } elseif ($histo[$key]['type'] == 'mailing') {
14712 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14713 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14714 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14715 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14716 } else {
14717 $libelle .= $histo[$key]['note'];
14718 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14719 }
14720 }
14721
14722 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14723 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14724 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14725 } else {
14726 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14727 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14728 }
14729 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14730 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14731 }
14732 if ($link) {
14733 $out .= ' - '.$link;
14734 }
14735 }
14736
14737 $out .= '</div>';
14738
14739 $out .= '</h3>';
14740
14741 // Message
14742 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14743 && $actionstatic->code != 'AC_TICKET_CREATE'
14744 && $actionstatic->code != 'AC_TICKET_MODIFY'
14745 ) {
14746 $out .= '<div class="timeline-body wordbreak small">';
14747 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14748 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14749 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14750 $out .= '<div class="readmore-block --closed" >';
14751 $out .= ' <div class="readmore-block__excerpt">';
14752 $out .= dolPrintHTML($truncatedText);
14753 $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>';
14754 $out .= ' </div>';
14755 $out .= ' <div class="readmore-block__full-text" >';
14756 $out .= dolPrintHTML($histo[$key]['message']);
14757 $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>';
14758 $out .= ' </div>';
14759 $out .= '</div>';
14760 } else {
14761 $out .= dolPrintHTML($histo[$key]['message']);
14762 }
14763
14764 $out .= '</div>';
14765 }
14766
14767 // Timeline footer
14768 $footer = '';
14769
14770 // Contact for this action
14771 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14772 $contactList = '';
14773 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14774 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14775 $contact = new Contact($db);
14776 $contact->fetch($cid);
14777 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14778 } else {
14779 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14780 }
14781
14782 if ($contact) {
14783 $contactList .= !empty($contactList) ? ', ' : '';
14784 $contactList .= $contact->getNomUrl(1);
14785 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14786 if (!empty($contact->phone_pro)) {
14787 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14788 }
14789 }
14790 }
14791 }
14792
14793 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14794 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14795 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14796 $contact = new Contact($db);
14797 $result = $contact->fetch($histo[$key]['contact_id']);
14798 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14799 } else {
14800 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14801 $result = ($contact instanceof Contact) ? $contact->id : 0;
14802 }
14803
14804 if ($result > 0) {
14805 $footer .= $contact->getNomUrl(1);
14806 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14807 if (!empty($contact->phone_pro)) {
14808 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14809 }
14810 }
14811 }
14812 }
14813
14814 $documents = getActionCommEcmList($actionstatic);
14815 if (!empty($documents)) {
14816 $footer .= '<div class="timeline-documents-container">';
14817 foreach ($documents as $doc) {
14818 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14819 $footer .= ' data-id="'.$doc->id.'" ';
14820 $footer .= ' data-path="'.$doc->filepath.'"';
14821 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14822 $footer .= '>';
14823
14824 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14825 $mime = dol_mimetype($filePath);
14826 $file = $actionstatic->id.'/'.$doc->filename;
14827 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14828 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14829 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14830
14831 $mimeAttr = ' mime="'.$mime.'" ';
14832 $class = '';
14833 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14834 $class .= ' documentpreview';
14835 }
14836
14837 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14838 $footer .= img_mime($filePath).' '.$doc->filename;
14839 $footer .= '</a>';
14840
14841 $footer .= '</span>';
14842 }
14843 $footer .= '</div>';
14844 }
14845
14846 if (!empty($footer)) {
14847 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14848 }
14849
14850 $out .= '</div>'."\n"; // end timeline-item
14851
14852 $out .= '</li>';
14853 $out .= '<!-- END timeline item -->';
14854 }
14855
14856 $out .= "</ul>\n";
14857
14858 $out .= '<script>
14859 jQuery(document).ready(function () {
14860 $(document).on("click", "[data-read-more-action]", function(e){
14861 let readMoreBloc = $(this).closest(".readmore-block");
14862 if(readMoreBloc.length > 0){
14863 e.preventDefault();
14864 if($(this).attr("data-read-more-action") == "close"){
14865 readMoreBloc.addClass("--closed").removeClass("--open");
14866 $("html, body").animate({
14867 scrollTop: readMoreBloc.offset().top - 200
14868 }, 100);
14869 }else{
14870 readMoreBloc.addClass("--open").removeClass("--closed");
14871 }
14872 }
14873 });
14874 });
14875 </script>';
14876
14877
14878 if (empty($histo)) {
14879 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14880 }
14881 }
14882
14883 if ($noprint) {
14884 return $out;
14885 } else {
14886 print $out;
14887 return null;
14888 }
14889}
14890
14902function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14903{
14904 if ($timestamp === null) {
14905 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14906 }
14907 $TParam = array(
14908 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14909 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14910 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14911 );
14912 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
14913 $TParam = array_merge($TParam, array(
14914 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
14915 $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
14916 $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
14917 ));
14918 }
14919
14920 return '&' . http_build_query($TParam);
14921}
14922
14941function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
14942{
14943 global $conf, $db, $langs, $hookmanager;
14944 global $action, $object;
14945
14946 if (!is_object($langs)) {
14947 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
14948 $langs = new Translate('', $conf);
14949 $langs->setDefaultLang();
14950 }
14951
14952 $langs->load("errors");
14953
14954 if ($printheader) {
14955 if (function_exists("llxHeader")) {
14956 llxHeader('');
14957 } elseif (function_exists("llxHeaderVierge")) {
14958 llxHeaderVierge('');
14959 }
14960 }
14961
14962 print '<div class="error">';
14963 if (empty($message)) {
14964 print $langs->trans("ErrorRecordNotFound");
14965 } else {
14966 print $langs->trans($message);
14967 }
14968 print '</div>';
14969 print '<br>';
14970
14971 if (empty($showonlymessage)) {
14972 if (empty($hookmanager)) {
14973 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14974 $hookmanager = new HookManager($db);
14975 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
14976 $hookmanager->initHooks(array('main'));
14977 }
14978
14979 $parameters = array('message' => $message, 'params' => $params);
14980 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14981 print $hookmanager->resPrint;
14982 }
14983
14984 if ($printfooter && function_exists("llxFooter")) {
14985 llxFooter();
14986 }
14987 exit(0);
14988}
$id
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
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:459
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:750
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:71
$object ref
Definition info.php:89
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
llxFooter()
Footer empty.
Definition document.php:107
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.
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
finishSimpleTable($addLineBreak=false)
Add the correct HTML close tags for "startSimpleTable(...)" (use after the last table line)
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.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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.
dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
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.
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_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
dol_print_ip($ip, $mode=0)
Return an IP formatted to be shown on screen.
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.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
Function to test if an entry is enabled or not.
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_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0)
Clean a string to keep only desirable HTML tags.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
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.
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 '.
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).
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
roundUpToNextMultiple($n, $x=5)
Round to next multiple.
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...
currentToken()
Return the value of token currently saved into session with name 'token'.
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.
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...
GETPOSTDATE($prefix, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
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.
dolGetBadge($label, $html='', $type='primary', $mode='', $url='', $params=array())
Function dolGetBadge.
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.
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 '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getElementProperties($elementType)
Get an array with properties of an element.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
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.
dolPrintHTMLForAttribute($s)
Return a string ready to be output on an HTML attribute (alt, title, data-html, .....
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).
dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot=true)
An function to complete dropdown url in dolGetButtonAction.
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.
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.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
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.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
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.
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_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
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...
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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...
print_date_range($date_start, $date_end, $format='', $outputlangs=null)
Format output for start and end date.
getArrayOfSocialNetworks()
Get array of social network dictionary.
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
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, ...)
colorIsLight($stringcolor)
Return true if the color is light.
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.
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
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_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
fetchObjectByElement($element_id, $element_type, $element_ref='', $useCache=0, $maxCacheByType=10)
Fetch an object from its id and element_type Inclusion of classes is automatic.
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 =...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
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.
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
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.
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...
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
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 link.
jsonOrUnserialize($stringtodecode)
Decode an encode string.
addSummaryTableLine($tableColumnCount, $num, $nbofloop=0, $total=0, $noneWord="None", $extraRightColumn=false)
Add a summary line to the current open table ("None", "XMoreLines" or "Total xxx")
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.
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_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
getUserRemoteIP()
Return the IP of remote user.
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.
dolPrintLabel($s)
Return a string label (so on 1 line only and that should not contains any HTML) ready to be output on...
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).
startSimpleTable($header, $link="", $arguments="", $emptyColumns=0, $number=-1, $pictofulllist='')
Start a table with headers and a optional clickable number (don't forget to use "finishSimpleTable()"...
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,...
getFieldErrorIcon($fieldValidationErrorMsg)
get field error icon
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.
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.
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
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition inc.php:420
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition main.inc.php:86
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)
Save data into a memory area shared by all users, all sessions on server.
dol_getcache($memoryid)
Read a memory area shared by all users, all sessions on server.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:149
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:152
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length for cryptographic purposes.