dolibarr 21.0.0-alpha
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-2022 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')) {
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['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || 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
1153function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
1154{
1155 $m = array();
1156 if ($hourTime === 'getpost') {
1157 $hour = GETPOSTINT($prefix . 'hour');
1158 $minute = GETPOSTINT($prefix . 'minute');
1159 $second = GETPOSTINT($prefix . 'second');
1160 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1161 $hour = intval($m[1]);
1162 $minute = intval($m[2]);
1163 $second = intval($m[3]);
1164 } else {
1165 $hour = $minute = $second = 0;
1166 }
1167 // normalize out of range values
1168 $hour = min($hour, 23);
1169 $minute = min($minute, 59);
1170 $second = min($second, 59);
1171 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
1172}
1173
1174
1185function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1186{
1187 return sanitizeVal($out, $check, $filter, $options);
1188}
1189
1199function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1200{
1201 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1202 // Check is done after replacement
1203 switch ($check) {
1204 case 'none':
1205 case 'password':
1206 break;
1207 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1208 if (!is_numeric($out)) {
1209 $out = '';
1210 }
1211 break;
1212 case 'intcomma':
1213 if (is_array($out)) {
1214 $out = implode(',', $out);
1215 }
1216 if (preg_match('/[^0-9,-]+/i', $out)) {
1217 $out = '';
1218 }
1219 break;
1220 case 'san_alpha':
1221 $out = filter_var($out, FILTER_SANITIZE_STRING);
1222 break;
1223 case 'email':
1224 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1225 break;
1226 case 'aZ':
1227 if (!is_array($out)) {
1228 $out = trim($out);
1229 if (preg_match('/[^a-z]+/i', $out)) {
1230 $out = '';
1231 }
1232 }
1233 break;
1234 case 'aZ09':
1235 if (!is_array($out)) {
1236 $out = trim($out);
1237 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1238 $out = '';
1239 }
1240 }
1241 break;
1242 case 'aZ09arobase': // great to sanitize $objecttype parameter
1243 if (!is_array($out)) {
1244 $out = trim($out);
1245 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1246 $out = '';
1247 }
1248 }
1249 break;
1250 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1251 if (!is_array($out)) {
1252 $out = trim($out);
1253 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1254 $out = '';
1255 }
1256 }
1257 break;
1258 case 'alpha': // No html and no ../ and "
1259 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1260 if (!is_array($out)) {
1261 $out = trim($out);
1262 do {
1263 $oldstringtoclean = $out;
1264 // Remove html tags
1265 $out = dol_string_nohtmltag($out, 0);
1266 // 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).
1267 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1268 // Remove also other dangerous string sequences
1269 // '../' or '..\' is dangerous because it allows dir transversals
1270 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1271 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1272 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1273 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1274 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1275 } while ($oldstringtoclean != $out);
1276 // keep lines feed
1277 }
1278 break;
1279 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'
1280 if (!is_array($out)) {
1281 $out = trim($out);
1282 do {
1283 $oldstringtoclean = $out;
1284 // Decode html entities
1285 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1286 // 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).
1287 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1288 // Remove also other dangerous string sequences
1289 // '../' or '..\' is dangerous because it allows dir transversals
1290 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1291 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1292 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1293 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1294 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1295 } while ($oldstringtoclean != $out);
1296 }
1297 break;
1298 case 'nohtml': // No html
1299 $out = dol_string_nohtmltag($out, 0);
1300 break;
1301 case 'restricthtmlnolink':
1302 case 'restricthtml': // Recommended for most html textarea
1303 case 'restricthtmlallowclass':
1304 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1305 case 'restricthtmlallowunvalid':
1306 $out = dol_htmlwithnojs($out, 1, $check);
1307 break;
1308 case 'custom':
1309 if (!empty($out)) {
1310 if (empty($filter)) {
1311 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1312 }
1313 if (is_null($options)) {
1314 $options = 0;
1315 }
1316 $out = filter_var($out, $filter, $options);
1317 }
1318 break;
1319 default:
1320 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1321 $out = GETPOST($out, 'alphanohtml');
1322 break;
1323 }
1324
1325 return $out;
1326}
1327
1328
1329if (!function_exists('dol_getprefix')) {
1340 function dol_getprefix($mode = '')
1341 {
1342 // If prefix is for email (we need to have $conf already loaded for this case)
1343 if ($mode == 'email') {
1344 global $conf;
1345
1346 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1347 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1348 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1349 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1350 return $_SERVER["SERVER_NAME"];
1351 }
1352 }
1353
1354 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1355 if (!empty($conf->file->instance_unique_id)) {
1356 return sha1('dolibarr'.$conf->file->instance_unique_id);
1357 }
1358
1359 // For backward compatibility when instance_unique_id is not set
1360 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1361 }
1362
1363 // If prefix is for session (no need to have $conf loaded)
1364 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1365 $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
1366
1367 // The recommended value (may be not defined for old versions)
1368 if (!empty($tmp_instance_unique_id)) {
1369 return sha1('dolibarr'.$tmp_instance_unique_id);
1370 }
1371
1372 // For backward compatibility when instance_unique_id is not set
1373 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1374 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1375 } else {
1376 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1377 }
1378 }
1379}
1380
1391function dol_include_once($relpath, $classname = '')
1392{
1393 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']
1394
1395 $fullpath = dol_buildpath($relpath);
1396
1397 if (!file_exists($fullpath)) {
1398 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1399 return false;
1400 }
1401
1402 if (!empty($classname) && !class_exists($classname)) {
1403 return include $fullpath;
1404 } else {
1405 return include_once $fullpath;
1406 }
1407}
1408
1409
1423function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1424{
1425 global $conf;
1426
1427 $path = preg_replace('/^\//', '', $path);
1428
1429 if (empty($type)) { // For a filesystem path
1430 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1431 if (is_array($conf->file->dol_document_root)) {
1432 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1433 if ($key == 'main') {
1434 continue;
1435 }
1436 // if (@file_exists($dirroot.'/'.$path)) {
1437 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1438 $res = $dirroot.'/'.$path;
1439 return $res;
1440 }
1441 }
1442 }
1443 if ($returnemptyifnotfound) {
1444 // Not found into alternate dir
1445 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1446 return '';
1447 }
1448 }
1449 } else {
1450 // For an url path
1451 // We try to get local path of file on filesystem from url
1452 // Note that trying to know if a file on disk exist by forging path on disk from url
1453 // works only for some web server and some setup. This is bugged when
1454 // using proxy, rewriting, virtual path, etc...
1455 $res = '';
1456 if ($type == 1) {
1457 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1458 }
1459 if ($type == 2) {
1460 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1461 }
1462 if ($type == 3) {
1463 $res = DOL_URL_ROOT.'/'.$path;
1464 }
1465
1466 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1467 if ($key == 'main') {
1468 if ($type == 3) {
1469 /*global $dolibarr_main_url_root;*/
1470
1471 // Define $urlwithroot
1472 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1473 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1474 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1475
1476 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1477 }
1478 continue;
1479 }
1480 $regs = array();
1481 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1482 if (!empty($regs[1])) {
1483 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1484 //if (file_exists($dirroot.'/'.$regs[1])) {
1485 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1486 if ($type == 1) {
1487 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1488 } elseif ($type == 2) {
1489 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1490 } elseif ($type == 3) {
1491 /*global $dolibarr_main_url_root;*/
1492
1493 // Define $urlwithroot
1494 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1495 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1496 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1497
1498 $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
1499 }
1500 break;
1501 }
1502 }
1503 }
1504 }
1505
1506 return $res;
1507}
1508
1519function dol_get_object_properties($obj, $properties = [])
1520{
1521 // Get real properties using get_object_vars() if $properties is empty
1522 if (empty($properties)) {
1523 return get_object_vars($obj);
1524 }
1525
1526 $existingProperties = [];
1527 $realProperties = get_object_vars($obj);
1528
1529 // Get the real or magic property values
1530 foreach ($properties as $property) {
1531 if (array_key_exists($property, $realProperties)) {
1532 // Real property, add the value
1533 $existingProperties[$property] = $obj->{$property};
1534 } elseif (property_exists($obj, $property)) {
1535 // Magic property
1536 $existingProperties[$property] = $obj->{$property};
1537 }
1538 }
1539
1540 return $existingProperties;
1541}
1542
1543
1558function dol_clone($object, $native = 0)
1559{
1560 if ($native == 0) {
1561 // deprecated method, use the method with native = 2 instead
1562 $tmpsavdb = null;
1563 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1564 $tmpsavdb = $object->db;
1565 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1566 }
1567
1568 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1569
1570 if (!empty($tmpsavdb)) {
1571 $object->db = $tmpsavdb;
1572 }
1573 } elseif ($native == 2) {
1574 // recommended method to have a full isolated cloned object
1575 $myclone = new stdClass();
1576 $tmparray = get_object_vars($object); // return only public properties
1577
1578 if (is_array($tmparray)) {
1579 foreach ($tmparray as $propertykey => $propertyval) {
1580 if (is_scalar($propertyval) || is_array($propertyval)) {
1581 $myclone->$propertykey = $propertyval;
1582 }
1583 }
1584 }
1585 } else {
1586 $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)
1587 }
1588
1589 return $myclone;
1590}
1591
1601function dol_size($size, $type = '')
1602{
1603 global $conf;
1604 if (empty($conf->dol_optimize_smallscreen)) {
1605 return $size;
1606 }
1607 if ($type == 'width' && $size > 250) {
1608 return 250;
1609 } else {
1610 return 10;
1611 }
1612}
1613
1614
1626function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1627{
1628 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1629 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1630 // Char '/' and '\' are file delimiters.
1631 // 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
1632 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1633 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1634 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1635 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1636 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1637 $tmp = str_replace('..', '', $tmp);
1638 return $tmp;
1639}
1640
1641
1653function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1654{
1655 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1656 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1657 // 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
1658 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1659 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1660 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1661 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1662 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1663 $tmp = str_replace('..', '', $tmp);
1664 return $tmp;
1665}
1666
1674function dol_sanitizeUrl($stringtoclean, $type = 1)
1675{
1676 // 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)
1677 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1678 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1679 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1680 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1681
1682 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1683 if ($type == 1) {
1684 // removing : should disable links to external url like http:aaa)
1685 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1686 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1687 }
1688
1689 do {
1690 $oldstringtoclean = $stringtoclean;
1691 // removing '&colon' should disable links to external url like http:aaa)
1692 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1693 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1694 } while ($oldstringtoclean != $stringtoclean);
1695
1696 if ($type == 1) {
1697 // removing '//' should disable links to external url like //aaa or http//)
1698 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1699 }
1700
1701 return $stringtoclean;
1702}
1703
1710function dol_sanitizeEmail($stringtoclean)
1711{
1712 do {
1713 $oldstringtoclean = $stringtoclean;
1714 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1715 } while ($oldstringtoclean != $stringtoclean);
1716
1717 return $stringtoclean;
1718}
1719
1728function dol_sanitizeKeyCode($str)
1729{
1730 return preg_replace('/[^\w]+/', '', $str);
1731}
1732
1733
1742function dol_string_unaccent($str)
1743{
1744 if (is_null($str)) {
1745 return '';
1746 }
1747
1748 if (utf8_check($str)) {
1749 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1750 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1751 return $transliterator->transliterate($str);
1752 }
1753 // See http://www.utf8-chartable.de/
1754 $string = rawurlencode($str);
1755 $replacements = array(
1756 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1757 '%C3%87' => 'C',
1758 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1759 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1760 '%C3%91' => 'N',
1761 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1762 '%C5%A0' => 'S',
1763 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1764 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1765 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1766 '%C3%A7' => 'c',
1767 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1768 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1769 '%C3%B1' => 'n',
1770 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1771 '%C5%A1' => 's',
1772 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1773 '%C3%BD' => 'y', '%C3%BF' => 'y'
1774 );
1775 $string = strtr($string, $replacements);
1776 return rawurldecode($string);
1777 } else {
1778 // See http://www.ascii-code.com/
1779 $string = strtr(
1780 $str,
1781 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1782 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1783 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1784 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1785 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1786 \xF9\xFA\xFB\xFC\xFD\xFF",
1787 "AAAAAAC
1788 EEEEIIIIDN
1789 OOOOOUUUY
1790 aaaaaaceeee
1791 iiiidnooooo
1792 uuuuyy"
1793 );
1794 $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"));
1795 return $string;
1796 }
1797}
1798
1812function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1813{
1814 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1815 if (empty($keepspaces)) {
1816 $forbidden_chars_to_replace[] = " ";
1817 }
1818 $forbidden_chars_to_remove = array();
1819 //$forbidden_chars_to_remove=array("(",")");
1820
1821 if (is_array($badcharstoreplace)) {
1822 $forbidden_chars_to_replace = $badcharstoreplace;
1823 }
1824 if (is_array($badcharstoremove)) {
1825 $forbidden_chars_to_remove = $badcharstoremove;
1826 }
1827
1828 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1829 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1830}
1831
1832
1846function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1847{
1848 if ($removetabcrlf) {
1849 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1850 } else {
1851 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
1852 }
1853}
1854
1863function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1864{
1865 if (is_null($stringtoescape)) {
1866 return '';
1867 }
1868
1869 // escape quotes and backslashes, newlines, etc.
1870 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1871 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1872 if (empty($noescapebackslashn)) {
1873 $substitjs["\n"] = '\\n';
1874 $substitjs['\\'] = '\\\\';
1875 }
1876 if (empty($mode)) {
1877 $substitjs["'"] = "\\'";
1878 $substitjs['"'] = "\\'";
1879 } elseif ($mode == 1) {
1880 $substitjs["'"] = "\\'";
1881 } elseif ($mode == 2) {
1882 $substitjs['"'] = '\\"';
1883 } elseif ($mode == 3) {
1884 $substitjs["'"] = "\\'";
1885 $substitjs['"'] = "\\\"";
1886 }
1887 return strtr($stringtoescape, $substitjs);
1888}
1889
1896function dol_escape_json($stringtoescape)
1897{
1898 return str_replace('"', '\"', $stringtoescape);
1899}
1900
1908function dol_escape_php($stringtoescape, $stringforquotes = 2)
1909{
1910 if (is_null($stringtoescape)) {
1911 return '';
1912 }
1913
1914 if ($stringforquotes == 2) {
1915 return str_replace('"', "'", $stringtoescape);
1916 } elseif ($stringforquotes == 1) {
1917 // We remove the \ char.
1918 // If we allow the \ char, we can have $stringtoescape =
1919 // abc\';phpcodedanger; so the escapement will become
1920 // abc\\';phpcodedanger; and injecting this into
1921 // $a='...' will give $ac='abc\\';phpcodedanger;
1922 $stringtoescape = str_replace('\\', '', $stringtoescape);
1923 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1924 }
1925
1926 return 'Bad parameter for stringforquotes in dol_escape_php';
1927}
1928
1935function dol_escape_all($stringtoescape)
1936{
1937 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1938}
1939
1946function dol_escape_xml($stringtoescape)
1947{
1948 return $stringtoescape;
1949}
1950
1958function dolPrintLabel($s)
1959{
1960 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1961}
1962
1970function dolPrintText($s)
1971{
1972 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
1973}
1974
1984function dolPrintHTML($s, $allowiframe = 0)
1985{
1986 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1987}
1988
1996function dolPrintHTMLForAttribute($s)
1997{
1998 // The dol_htmlentitiesbr will convert simple text into html
1999 // The dol_escape_htmltag will escape html chars.
2000 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
2001}
2002
2012function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2013{
2014 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2015}
2016
2023function dolPrintPassword($s)
2024{
2025 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
2026}
2027
2028
2045function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2046{
2047 if ($noescapetags == 'common') {
2048 $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';
2049 // Add also html5 tags
2050 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2051 }
2052 if ($cleanalsojavascript) {
2053 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2054 }
2055
2056 // escape quotes and backslashes, newlines, etc.
2057 if ($escapeonlyhtmltags) {
2058 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2059 } else {
2060 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
2061 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
2062 }
2063 if (!$keepb) {
2064 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2065 }
2066 if (!$keepn) {
2067 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2068 } elseif ($keepn == -1) {
2069 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2070 }
2071
2072 if ($escapeonlyhtmltags) {
2073 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2074 } else {
2075 // Escape tags to keep
2076 $tmparrayoftags = array();
2077 if ($noescapetags) {
2078 $tmparrayoftags = explode(',', $noescapetags);
2079 }
2080 if (count($tmparrayoftags)) {
2081 $reg = array();
2082 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2083
2084 foreach ($tmparrayoftags as $tagtoreplace) {
2085 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2086 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2087 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2088
2089 // For case of tag with attribute
2090 do {
2091 $tmpold = $tmp;
2092
2093 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', $tmp, $reg)) {
2094 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must never have [ ] inside the attribute string
2095 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes);
2096 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2097 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2098 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2099 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2100 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2101 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2102 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2103 }
2104 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)\s+\/>/', $tmp, $reg)) {
2105 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
2106 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2107 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2108 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2109 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2110 }
2111
2112 $diff = strcmp($tmpold, $tmp);
2113 } while ($diff);
2114 }
2115 }
2116
2117 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2118
2119 //print $result;
2120
2121 if (count($tmparrayoftags)) {
2122 foreach ($tmparrayoftags as $tagtoreplace) {
2123 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2124 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2125 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2126 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2127 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2128 }
2129
2130 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2131 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2132 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2133 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2134 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2135 }
2136
2137 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2138
2139 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2140
2141 return $result;
2142 }
2143}
2144
2152function dol_strtolower($string, $encoding = "UTF-8")
2153{
2154 if (function_exists('mb_strtolower')) {
2155 return mb_strtolower($string, $encoding);
2156 } else {
2157 return strtolower($string);
2158 }
2159}
2160
2169function dol_strtoupper($string, $encoding = "UTF-8")
2170{
2171 if (function_exists('mb_strtoupper')) {
2172 return mb_strtoupper($string, $encoding);
2173 } else {
2174 return strtoupper($string);
2175 }
2176}
2177
2186function dol_ucfirst($string, $encoding = "UTF-8")
2187{
2188 if (function_exists('mb_substr')) {
2189 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2190 } else {
2191 return ucfirst($string);
2192 }
2193}
2194
2203function dol_ucwords($string, $encoding = "UTF-8")
2204{
2205 if (function_exists('mb_convert_case')) {
2206 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2207 } else {
2208 return ucwords($string);
2209 }
2210}
2211
2212
2218function getCallerInfoString()
2219{
2220 $backtrace = debug_backtrace();
2221 $msg = "";
2222 if (count($backtrace) >= 2) {
2223 $trace = $backtrace[1];
2224 if (isset($trace['file'], $trace['line'])) {
2225 $msg = " From {$trace['file']}:{$trace['line']}.";
2226 }
2227 }
2228 return $msg;
2229}
2230
2253function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2254{
2255 global $conf, $user, $debugbar;
2256
2257 // If syslog module enabled
2258 if (!isModEnabled('syslog')) {
2259 return;
2260 }
2261
2262 // Check if we are into execution of code of a website
2263 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2264 global $website, $websitekey;
2265 if (is_object($website) && !empty($website->ref)) {
2266 $suffixinfilename .= '_website_'.$website->ref;
2267 } elseif (!empty($websitekey)) {
2268 $suffixinfilename .= '_website_'.$websitekey;
2269 }
2270 }
2271
2272 // Check if we have a forced suffix
2273 if (defined('USESUFFIXINLOG')) {
2274 $suffixinfilename .= constant('USESUFFIXINLOG');
2275 }
2276
2277 if ($ident < 0) {
2278 foreach ($conf->loghandlers as $loghandlerinstance) {
2279 $loghandlerinstance->setIdent($ident);
2280 }
2281 }
2282
2283 if (!empty($message)) {
2284 // Test log level @phan-suppress-next-line PhanPluginDuplicateArrayKey
2285 $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');
2286 if (!array_key_exists($level, $logLevels)) {
2287 throw new Exception('Incorrect log level');
2288 }
2289 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2290 return;
2291 }
2292
2293 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2294 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2295 }
2296
2297 // If adding log inside HTML page is required
2298 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2299 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2300 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2301 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2302
2303 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2304 }
2305
2306 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2307 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2308 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2309 print "\n\n<!-- Log start\n";
2310 print dol_escape_htmltag($message)."\n";
2311 print "Log end -->\n";
2312 }
2313
2314 $data = array(
2315 'message' => $message,
2316 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2317 'level' => $level,
2318 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2319 'ip' => false,
2320 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2321 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2322 );
2323
2324 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2325 if (!empty($remoteip)) {
2326 $data['ip'] = $remoteip;
2327 // This is when server run behind a reverse proxy
2328 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2329 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2330 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2331 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2332 }
2333 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2334 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2335 $data['ip'] = $_SERVER['SERVER_ADDR'];
2336 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2337 // 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).
2338 $data['ip'] = $_SERVER['COMPUTERNAME'];
2339 } else {
2340 $data['ip'] = '???';
2341 }
2342
2343 if (!empty($_SERVER['USERNAME'])) {
2344 // 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).
2345 $data['osuser'] = $_SERVER['USERNAME'];
2346 } elseif (!empty($_SERVER['LOGNAME'])) {
2347 // 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).
2348 $data['osuser'] = $_SERVER['LOGNAME'];
2349 }
2350
2351 // Loop on each log handler and send output
2352 foreach ($conf->loghandlers as $loghandlerinstance) {
2353 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2354 continue;
2355 }
2356 $loghandlerinstance->export($data, $suffixinfilename);
2357 }
2358 unset($data);
2359 }
2360
2361 if ($ident > 0) {
2362 foreach ($conf->loghandlers as $loghandlerinstance) {
2363 $loghandlerinstance->setIdent($ident);
2364 }
2365 }
2366}
2367
2379function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2380{
2381 global $langs, $db;
2382
2383 $form = new Form($db);
2384
2385 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2386 if (empty($templatenameforexport)) {
2387 $templatenameforexport = 'website_'.$website->ref;
2388 }
2389
2390 $out = '';
2391 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2392
2393 // for generate popup
2394 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2395 $out .= 'jQuery(document).ready(function () {';
2396 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2397 $out .= ' var dialogHtml = \'';
2398
2399 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2400 $dialogcontent .= ' <div style="margin-top: 20px;">';
2401 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2402 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2403 $dialogcontent .= ' </div>';
2404 $dialogcontent .= ' <br>';
2405 $dialogcontent .= ' <div style="margin-top: 20px;">';
2406 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2407 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2408 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2409 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2410 $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>';
2411 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2412 $dialogcontent .= ' </form>';
2413 $dialogcontent .= ' </div>';
2414 $dialogcontent .= ' </div>';
2415
2416 $out .= dol_escape_js($dialogcontent);
2417
2418 $out .= '\';';
2419
2420
2421 // Add the content of the dialog to the body of the page
2422 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2423 $out .= ' if ($dialog.length > 0) {
2424 $dialog.remove();
2425 }
2426 jQuery("body").append(dialogHtml);';
2427
2428 // Configuration of popup
2429 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2430 $out .= ' autoOpen: false,';
2431 $out .= ' modal: true,';
2432 $out .= ' height: 290,';
2433 $out .= ' width: "40%",';
2434 $out .= ' title: "' . dol_escape_js($label) . '",';
2435 $out .= ' });';
2436
2437 // Simulate a click on the original "submit" input to export the site.
2438 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2439 $out .= ' console.log("Clic on exportsite.");';
2440 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2441 $out .= ' console.log("element founded:", target.length > 0);';
2442 $out .= ' if (target.length > 0) { target.click(); }';
2443 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2444 $out .= ' });';
2445
2446 // open popup
2447 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2448 $out .= ' return false;';
2449 $out .= ' });';
2450 $out .= '});';
2451 $out .= '</script>';
2452
2453 return $out;
2454}
2455
2456
2473function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2474{
2475 global $conf;
2476
2477 if (strpos($url, '?') > 0) {
2478 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2479 } else {
2480 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2481 }
2482
2483 $out = '';
2484
2485 $backtopagejsfieldsid = '';
2486 $backtopagejsfieldslabel = '';
2487 if ($backtopagejsfields) {
2488 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2489 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2490 $backtopagejsfields = $name.":".$backtopagejsfields;
2491 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2492 } else {
2493 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2494 }
2495 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2496 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2497 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2498 }
2499
2500 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2501 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2502 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2503 if (empty($conf->use_javascript_ajax)) {
2504 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2505 } elseif ($jsonopen) {
2506 $out .= ' href="#" onclick="'.$jsonopen.'"';
2507 } else {
2508 $out .= ' href="#"';
2509 }
2510 $out .= '>'.$buttonstring.'</a>';
2511
2512 if (!empty($conf->use_javascript_ajax)) {
2513 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2514 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2515 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2516 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2517 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2518
2519 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2520 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2521 jQuery(document).ready(function () {
2522 jQuery(".button_'.$name.'").click(function () {
2523 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2524 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2525 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2526 $tmpdialog.dialog({
2527 autoOpen: false,
2528 modal: true,
2529 height: (window.innerHeight - 150),
2530 width: \'80%\',
2531 title: \''.dol_escape_js($label).'\',
2532 open: function (event, ui) {
2533 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2534 },
2535 close: function (event, ui) {
2536 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2537 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2538 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2539 if (returnedid != "" && returnedid != "div for returned id") {
2540 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2541 }
2542 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2543 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2544 }
2545 }
2546 });
2547
2548 $tmpdialog.dialog(\'open\');
2549 return false;
2550 });
2551 });
2552 </script>';
2553 }
2554 return $out;
2555}
2556
2573function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2574{
2575 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2576}
2577
2594function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2595{
2596 global $conf, $langs, $hookmanager;
2597
2598 // Show title
2599 $showtitle = 1;
2600 if (!empty($conf->dol_optimize_smallscreen)) {
2601 $showtitle = 0;
2602 }
2603
2604 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2605
2606 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2607 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2608 }
2609
2610 // Show right part
2611 if ($morehtmlright) {
2612 $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.
2613 }
2614
2615 // Show title
2616 /*
2617 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2618 $limittitle = 30;
2619 $out .= '<a class="tabTitle">';
2620 if ($picto) {
2621 $noprefix = $pictoisfullpath;
2622 if (strpos($picto, 'fontawesome_') !== false) {
2623 $noprefix = 1;
2624 }
2625 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2626 }
2627 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2628 $out .= '</a>';
2629 }
2630 */
2631
2632 // Show tabs
2633
2634 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2635 $maxkey = -1;
2636 if (is_array($links) && !empty($links)) {
2637 $keys = array_keys($links);
2638 if (count($keys)) {
2639 $maxkey = max($keys);
2640 }
2641 }
2642
2643 // Show tabs
2644 // if =0 we don't use the feature
2645 if (empty($limittoshow)) {
2646 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2647 }
2648 if (!empty($conf->dol_optimize_smallscreen)) {
2649 $limittoshow = 2;
2650 }
2651
2652 $displaytab = 0;
2653 $nbintab = 0;
2654 $popuptab = 0;
2655 $outmore = '';
2656 for ($i = 0; $i <= $maxkey; $i++) {
2657 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2658 // If active tab is already present
2659 if ($i >= $limittoshow) {
2660 $limittoshow--;
2661 }
2662 }
2663 }
2664
2665 for ($i = 0; $i <= $maxkey; $i++) {
2666 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2667 $isactive = true;
2668 } else {
2669 $isactive = false;
2670 }
2671
2672 if ($i < $limittoshow || $isactive) {
2673 // Output entry with a visible tab
2674 $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])).' -->';
2675
2676 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2677 if (!empty($links[$i][0])) {
2678 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2679 } else {
2680 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2681 }
2682 } elseif (!empty($links[$i][1])) {
2683 //print "x $i $active ".$links[$i][2]." z";
2684 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2685
2686 if (!empty($links[$i][0])) {
2687 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2688 $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).'">';
2689 }
2690
2691 if ($displaytab == 0 && $picto) {
2692 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2693 }
2694
2695 $out .= $links[$i][1];
2696 if (!empty($links[$i][0])) {
2697 $out .= '</a>'."\n";
2698 }
2699 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2700 $out .= '</div>';
2701 }
2702
2703 $out .= '</div>';
2704 } else {
2705 // Add entry into the combo popup with the other tabs
2706 if (!$popuptab) {
2707 $popuptab = 1;
2708 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2709 }
2710 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2711 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2712 if (!empty($links[$i][0])) {
2713 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2714 } else {
2715 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2716 }
2717 } elseif (!empty($links[$i][1])) {
2718 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2719 $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.
2720 $outmore .= '</a>'."\n";
2721 }
2722 $outmore .= '</div>';
2723
2724 $nbintab++;
2725 }
2726
2727 $displaytab = $i + 1;
2728 }
2729 if ($popuptab) {
2730 $outmore .= '</div>';
2731 }
2732
2733 if ($popuptab) { // If there is some tabs not shown
2734 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2735 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2736 $widthofpopup = 200;
2737
2738 $tabsname = $moretabssuffix;
2739 if (empty($tabsname)) {
2740 $tabsname = str_replace("@", "", $picto);
2741 }
2742 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2743 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2744 $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".
2745 }
2746 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2747 $out .= $outmore;
2748 $out .= '</div>';
2749 $out .= '<div></div>';
2750 $out .= "</div>\n";
2751
2752 $out .= '<script nonce="'.getNonce().'">';
2753 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2754 var x = this.offsetLeft, y = this.offsetTop;
2755 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2756 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2757 $('#moretabsList".$tabsname."').css('".$right."','8px');
2758 }
2759 $('#moretabsList".$tabsname."').css('".$left."','auto');
2760 });
2761 ";
2762 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2763 $out .= "</script>";
2764 }
2765
2766 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2767 $out .= "</div>\n";
2768 }
2769
2770 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
2771 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
2772 $out .= '">'."\n";
2773 }
2774 if (!empty($dragdropfile)) {
2775 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2776 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2777 }
2778 $parameters = array('tabname' => $active, 'out' => $out);
2779 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2780 if ($reshook > 0) {
2781 $out = $hookmanager->resPrint;
2782 }
2783
2784 return $out;
2785}
2786
2794function dol_fiche_end($notab = 0)
2795{
2796 print dol_get_fiche_end($notab);
2797}
2798
2805function dol_get_fiche_end($notab = 0)
2806{
2807 if (!$notab || $notab == -1) {
2808 return "\n</div>\n";
2809 } else {
2810 return '';
2811 }
2812}
2813
2833function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2834{
2835 global $conf, $form, $user, $langs, $hookmanager, $action;
2836
2837 $error = 0;
2838
2839 $maxvisiblephotos = 1;
2840 $showimage = 1;
2841 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2842 // @phan-suppress-next-line PhanUndeclaredMethod
2843 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2844 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2845 $showbarcode = 0;
2846 }
2847 $modulepart = 'unknown';
2848
2849 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2850 $modulepart = $object->element;
2851 } elseif ($object->element == 'member') {
2852 $modulepart = 'memberphoto';
2853 } elseif ($object->element == 'user') {
2854 $modulepart = 'userphoto';
2855 }
2856
2857 if (class_exists("Imagick")) {
2858 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2859 $modulepart = $object->element;
2860 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2861 $modulepart = 'ficheinter';
2862 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2863 $modulepart = 'contract';
2864 } elseif ($object->element == 'order_supplier') {
2865 $modulepart = 'supplier_order';
2866 } elseif ($object->element == 'invoice_supplier') {
2867 $modulepart = 'supplier_invoice';
2868 }
2869 }
2870
2871 if ($object->element == 'product') {
2873 '@phan-var-force Product $object';
2874 $width = 80;
2875 $cssclass = 'photowithmargin photoref';
2876 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2877 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2878 if ($conf->browser->layout == 'phone') {
2879 $maxvisiblephotos = 1;
2880 }
2881 if ($showimage) {
2882 $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>';
2883 } else {
2884 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2885 $nophoto = '';
2886 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2887 } else { // Show no photo link
2888 $nophoto = '/public/theme/common/nophoto.png';
2889 $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>';
2890 }
2891 }
2892 } elseif ($object->element == 'category') {
2894 '@phan-var-force Categorie $object';
2895 $width = 80;
2896 $cssclass = 'photowithmargin photoref';
2897 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2898 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2899 if ($conf->browser->layout == 'phone') {
2900 $maxvisiblephotos = 1;
2901 }
2902 if ($showimage) {
2903 $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>';
2904 } else {
2905 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2906 $nophoto = '';
2907 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2908 } else { // Show no photo link
2909 $nophoto = '/public/theme/common/nophoto.png';
2910 $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>';
2911 }
2912 }
2913 } elseif ($object->element == 'bom') {
2915 '@phan-var-force Bom $object';
2916 $width = 80;
2917 $cssclass = 'photowithmargin photoref';
2918 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2919 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2920 if ($conf->browser->layout == 'phone') {
2921 $maxvisiblephotos = 1;
2922 }
2923 if ($showimage) {
2924 $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>';
2925 } else {
2926 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2927 $nophoto = '';
2928 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2929 } else { // Show no photo link
2930 $nophoto = '/public/theme/common/nophoto.png';
2931 $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>';
2932 }
2933 }
2934 } elseif ($object->element == 'ticket') {
2935 $width = 80;
2936 $cssclass = 'photoref';
2938 '@phan-var-force Ticket $object';
2939 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2940 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2941 if ($conf->browser->layout == 'phone') {
2942 $maxvisiblephotos = 1;
2943 }
2944
2945 if ($showimage) {
2946 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2947 if ($object->nbphoto > 0) {
2948 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2949 } else {
2950 $showimage = 0;
2951 }
2952 }
2953 if (!$showimage) {
2954 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2955 $nophoto = '';
2956 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2957 } else { // Show no photo link
2958 $nophoto = img_picto('No photo', 'object_ticket');
2959 $morehtmlleft .= '<!-- No photo to show -->';
2960 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2961 $morehtmlleft .= $nophoto;
2962 $morehtmlleft .= '</div></div>';
2963 }
2964 }
2965 } else {
2966 if ($showimage) {
2967 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2968 $phototoshow = '';
2969 // Check if a preview file is available
2970 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2971 $objectref = dol_sanitizeFileName($object->ref);
2972 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2973 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2974 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2975 $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
2976 } else {
2977 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2978 }
2979 if (empty($subdir)) {
2980 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2981 }
2982
2983 $filepath = $dir_output.$subdir."/";
2984
2985 $filepdf = $filepath.$objectref.".pdf";
2986 $relativepath = $subdir.'/'.$objectref.'.pdf';
2987
2988 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2989 $fileimage = $filepdf.'_preview.png';
2990 $relativepathimage = $relativepath.'_preview.png';
2991
2992 $pdfexists = file_exists($filepdf);
2993
2994 // If PDF file exists
2995 if ($pdfexists) {
2996 // Conversion du PDF en image png si fichier png non existent
2997 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2998 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2999 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3000 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3001 if ($ret < 0) {
3002 $error++;
3003 }
3004 }
3005 }
3006 }
3007
3008 if ($pdfexists && !$error) {
3009 $heightforphotref = 80;
3010 if (!empty($conf->dol_optimize_smallscreen)) {
3011 $heightforphotref = 60;
3012 }
3013 // If the preview file is found
3014 if (file_exists($fileimage)) {
3015 $phototoshow = '<div class="photoref">';
3016 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3017 $phototoshow .= '</div>';
3018 }
3019 }
3020 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3021 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
3022 }
3023
3024 if ($phototoshow) {
3025 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3026 $morehtmlleft .= $phototoshow;
3027 $morehtmlleft .= '</div>';
3028 }
3029 }
3030
3031 if (empty($phototoshow)) { // Show No photo link (picto of object)
3032 if ($object->element == 'action') {
3033 $width = 80;
3034 $cssclass = 'photorefcenter';
3035 $nophoto = img_picto('No photo', 'title_agenda');
3036 } else {
3037 $width = 14;
3038 $cssclass = 'photorefcenter';
3039 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3040 $prefix = 'object_';
3041 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3042 $picto = 'project'; // instead of projectpub
3043 }
3044 if (strpos($picto, 'fontawesome_') !== false) {
3045 $prefix = '';
3046 }
3047 $nophoto = img_picto('No photo', $prefix.$picto);
3048 }
3049 $morehtmlleft .= '<!-- No photo to show -->';
3050 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3051 $morehtmlleft .= $nophoto;
3052 $morehtmlleft .= '</div></div>';
3053 }
3054 }
3055 }
3056
3057 // Show barcode
3058 if ($showbarcode) {
3059 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3060 }
3061
3062 if ($object->element == 'societe') {
3063 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3064 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3065 } else {
3066 $morehtmlstatus .= $object->getLibStatut(6);
3067 }
3068 } elseif ($object->element == 'product') {
3069 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3070 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3071 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3072 } else {
3073 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3074 }
3075 $morehtmlstatus .= ' &nbsp; ';
3076 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3077 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3078 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3079 } else {
3080 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3081 }
3082 } elseif (in_array($object->element, array('salary'))) {
3083 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3084 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3085 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3086 }
3087 $morehtmlstatus .= $tmptxt;
3088 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3089 $totalallpayments = $object->getSommePaiement(0);
3090 $totalallpayments += $object->getSumCreditNotesUsed(0);
3091 $totalallpayments += $object->getSumDepositsUsed(0);
3092 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3093 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3094 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3095 }
3096 $morehtmlstatus .= $tmptxt;
3097 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3098 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3099 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3100 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3101 }
3102 $morehtmlstatus .= $tmptxt;
3103 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3104 if ($object->statut == 0) {
3105 $morehtmlstatus .= $object->getLibStatut(5);
3106 } else {
3107 $morehtmlstatus .= $object->getLibStatut(4);
3108 }
3109 } elseif ($object->element == 'facturerec') {
3110 '@phan-var-force FactureRec $object';
3111 if ($object->frequency == 0) {
3112 $morehtmlstatus .= $object->getLibStatut(2);
3113 } else {
3114 $morehtmlstatus .= $object->getLibStatut(5);
3115 }
3116 } elseif ($object->element == 'project_task') {
3117 $object->fk_statut = 1;
3118 $object->status = 1;
3119 if ($object->progress > 0) {
3120 $object->fk_statut = 2;
3121 $object->status = 2;
3122 }
3123 if ($object->progress >= 100) {
3124 $object->fk_statut = 3;
3125 $object->status = 3;
3126 }
3127 $tmptxt = $object->getLibStatut(5);
3128 $morehtmlstatus .= $tmptxt; // No status on task
3129 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3130 $tmptxt = $object->getLibStatut(6);
3131 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3132 $tmptxt = $object->getLibStatut(5);
3133 }
3134 $morehtmlstatus .= $tmptxt;
3135 }
3136
3137 // Add if object was dispatched "into accountancy"
3138 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3139 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3140 if (method_exists($object, 'getVentilExportCompta')) {
3141 $accounted = $object->getVentilExportCompta();
3142 $langs->load("accountancy");
3143 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3144 }
3145 }
3146
3147 // Add alias for thirdparty
3148 if (!empty($object->name_alias)) {
3149 '@phan-var-force Societe $object';
3150 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3151 }
3152
3153 // Add label
3154 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3155 if (!empty($object->label)) {
3156 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3157 }
3158 }
3159 // Show address and email
3160 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3161 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3162 if ($moreaddress) {
3163 $morehtmlref .= '<div class="refidno refaddress">';
3164 $morehtmlref .= $moreaddress;
3165 $morehtmlref .= '</div>';
3166 }
3167 }
3168 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)) {
3169 $morehtmlref .= '<div style="clear: both;"></div>';
3170 $morehtmlref .= '<div class="refidno opacitymedium">';
3171 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3172 $morehtmlref .= '</div>';
3173 }
3174
3175 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3176 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3177 if ($reshook < 0) {
3178 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3179 } elseif (empty($reshook)) {
3180 $morehtmlref .= $hookmanager->resPrint;
3181 } elseif ($reshook > 0) {
3182 $morehtmlref = $hookmanager->resPrint;
3183 }
3184
3185 // $morehtml is the right part (link "Back to list")
3186 // $morehtmlleft is the picto or photo of banner
3187 // $morehtmlstatus is part under the status
3188 // $morehtmlright is part of htmlright
3189
3190 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3191 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3192 print '</div>';
3193 print '<div class="underrefbanner clearboth"></div>';
3194}
3195
3205function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3206{
3207 global $langs;
3208 $ret = '';
3209 if ($fieldrequired) {
3210 $ret .= '<span class="fieldrequired">';
3211 }
3212 $ret .= '<label for="'.$fieldkey.'">';
3213 $ret .= $langs->trans($langkey);
3214 $ret .= '</label>';
3215 if ($fieldrequired) {
3216 $ret .= '</span>';
3217 }
3218 return $ret;
3219}
3220
3234function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3235{
3236 global $langs, $hookmanager;
3237
3238 $ret = '';
3239 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3240
3241 // See format of addresses on https://en.wikipedia.org/wiki/Address
3242 // Address
3243 if (empty($mode)) {
3244 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3245 }
3246 // Zip/Town/State
3247 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3248 // US: title firstname name \n address lines \n town, state, zip \n country
3249 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3250 $ret .= (($ret && $town) ? $sep : '').$town;
3251
3252 if (!empty($object->state)) {
3253 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3254 }
3255 if (!empty($object->zip)) {
3256 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3257 }
3258 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3259 // UK: title firstname name \n address lines \n town state \n zip \n country
3260 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3261 $ret .= ($ret ? $sep : '').$town;
3262 if (!empty($object->state)) {
3263 $ret .= ($ret ? ", " : '').$object->state;
3264 }
3265 if (!empty($object->zip)) {
3266 $ret .= ($ret ? $sep : '').$object->zip;
3267 }
3268 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3269 // ES: title firstname name \n address lines \n zip town \n state \n country
3270 $ret .= ($ret ? $sep : '').$object->zip;
3271 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3272 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3273 if (!empty($object->state)) {
3274 $ret .= $sep.$object->state;
3275 }
3276 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3277 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3278 // See https://www.sljfaq.org/afaq/addresses.html
3279 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3280 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3281 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3282 // IT: title firstname name\n address lines \n zip town state_code \n country
3283 $ret .= ($ret ? $sep : '').$object->zip;
3284 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3285 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3286 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3287 } else {
3288 // Other: title firstname name \n address lines \n zip town[, state] \n country
3289 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3290 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3291 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3292 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3293 $ret .= ($ret ? ", " : '').$object->state;
3294 }
3295 }
3296
3297 if (!is_object($outputlangs)) {
3298 $outputlangs = $langs;
3299 }
3300 if ($withcountry) {
3301 $langs->load("dict");
3302 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3303 }
3304 if ($hookmanager) {
3305 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3306 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3307 if ($reshook > 0) {
3308 $ret = '';
3309 }
3310 $ret .= $hookmanager->resPrint;
3311 }
3312
3313 return $ret;
3314}
3315
3316
3317
3327function dol_strftime($fmt, $ts = false, $is_gmt = false)
3328{
3329 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3330 return dol_print_date($ts, $fmt, $is_gmt);
3331 } else {
3332 return 'Error date outside supported range';
3333 }
3334}
3335
3357function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3358{
3359 global $conf, $langs;
3360
3361 // If date undefined or "", we return ""
3362 if (dol_strlen($time) == 0) {
3363 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3364 }
3365
3366 if ($tzoutput === 'auto') {
3367 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3368 }
3369
3370 // Clean parameters
3371 $to_gmt = false;
3372 $offsettz = $offsetdst = 0;
3373 if ($tzoutput) {
3374 $to_gmt = true; // For backward compatibility
3375 if (is_string($tzoutput)) {
3376 if ($tzoutput == 'tzserver') {
3377 $to_gmt = false;
3378 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3379 // @phan-suppress-next-line PhanPluginRedundantAssignment
3380 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3381 // @phan-suppress-next-line PhanPluginRedundantAssignment
3382 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3383 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3384 $to_gmt = true;
3385 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3386
3387 if (class_exists('DateTimeZone')) {
3388 $user_date_tz = new DateTimeZone($offsettzstring);
3389 $user_dt = new DateTime();
3390 $user_dt->setTimezone($user_date_tz);
3391 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3392 $offsettz = $user_dt->getOffset(); // should include dst ?
3393 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3394 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3395 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3396 }
3397 }
3398 }
3399 }
3400 if (!is_object($outputlangs)) {
3401 $outputlangs = $langs;
3402 }
3403 if (!$format) {
3404 $format = 'daytextshort';
3405 }
3406
3407 // Do we have to reduce the length of date (year on 2 chars) to save space.
3408 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3409 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3410 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3411 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3412 if ($formatwithoutreduce != $format) {
3413 $format = $formatwithoutreduce;
3414 $reduceformat = 1;
3415 } // so format 'dayreduceformat' is processed like day
3416
3417 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3418 // TODO Add format daysmallyear and dayhoursmallyear
3419 if ($format == 'day') {
3420 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3421 } elseif ($format == 'hour') {
3422 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3423 } elseif ($format == 'hourduration') {
3424 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3425 } elseif ($format == 'daytext') {
3426 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3427 } elseif ($format == 'daytextshort') {
3428 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3429 } elseif ($format == 'dayhour') {
3430 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3431 } elseif ($format == 'dayhoursec') {
3432 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3433 } elseif ($format == 'dayhourtext') {
3434 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3435 } elseif ($format == 'dayhourtextshort') {
3436 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3437 } elseif ($format == 'dayhourlog') {
3438 // Format not sensitive to language
3439 $format = '%Y%m%d%H%M%S';
3440 } elseif ($format == 'dayhourlogsmall') {
3441 // Format not sensitive to language
3442 $format = '%y%m%d%H%M';
3443 } elseif ($format == 'dayhourldap') {
3444 $format = '%Y%m%d%H%M%SZ';
3445 } elseif ($format == 'dayhourxcard') {
3446 $format = '%Y%m%dT%H%M%SZ';
3447 } elseif ($format == 'dayxcard') {
3448 $format = '%Y%m%d';
3449 } elseif ($format == 'dayrfc') {
3450 $format = '%Y-%m-%d'; // DATE_RFC3339
3451 } elseif ($format == 'dayhourrfc') {
3452 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3453 } elseif ($format == 'standard') {
3454 $format = '%Y-%m-%d %H:%M:%S';
3455 }
3456
3457 if ($reduceformat) {
3458 $format = str_replace('%Y', '%y', $format);
3459 $format = str_replace('yyyy', 'yy', $format);
3460 }
3461
3462 // Clean format
3463 if (preg_match('/%b/i', $format)) { // There is some text to translate
3464 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3465 $format = str_replace('%b', '__b__', $format);
3466 $format = str_replace('%B', '__B__', $format);
3467 }
3468 if (preg_match('/%a/i', $format)) { // There is some text to translate
3469 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3470 $format = str_replace('%a', '__a__', $format);
3471 $format = str_replace('%A', '__A__', $format);
3472 }
3473
3474 // Analyze date
3475 $reg = array();
3476 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
3477 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"]));
3478 return '';
3479 } 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
3480 // This part of code should not be used anymore.
3481 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);
3482 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3483 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3484 $syear = (!empty($reg[1]) ? $reg[1] : '');
3485 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3486 $sday = (!empty($reg[3]) ? $reg[3] : '');
3487 $shour = (!empty($reg[4]) ? $reg[4] : '');
3488 $smin = (!empty($reg[5]) ? $reg[5] : '');
3489 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3490
3491 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3492
3493 if ($to_gmt) {
3494 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3495 } else {
3496 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3497 }
3498 $dtts = new DateTime();
3499 $dtts->setTimestamp($time);
3500 $dtts->setTimezone($tzo);
3501 $newformat = str_replace(
3502 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3503 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3504 $format
3505 );
3506 $ret = $dtts->format($newformat);
3507 $ret = str_replace(
3508 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3509 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3510 $ret
3511 );
3512 } else {
3513 // Date is a timestamps
3514 if ($time < 100000000000) { // Protection against bad date values
3515 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3516
3517 if ($to_gmt) {
3518 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3519 } else {
3520 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3521 }
3522 $dtts = new DateTime();
3523 $dtts->setTimestamp($timetouse);
3524 $dtts->setTimezone($tzo);
3525 $newformat = str_replace(
3526 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3527 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3528 $format
3529 );
3530 $ret = $dtts->format($newformat);
3531 $ret = str_replace(
3532 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3533 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3534 $ret
3535 );
3536 //var_dump($ret);exit;
3537 } else {
3538 $ret = 'Bad value '.$time.' for date';
3539 }
3540 }
3541
3542 if (preg_match('/__b__/i', $format)) {
3543 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3544
3545 if ($to_gmt) {
3546 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3547 } else {
3548 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3549 }
3550 $dtts = new DateTime();
3551 $dtts->setTimestamp($timetouse);
3552 $dtts->setTimezone($tzo);
3553 $month = (int) $dtts->format("m");
3554 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3555 if ($encodetooutput) {
3556 $monthtext = $outputlangs->transnoentities('Month'.$month);
3557 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3558 } else {
3559 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3560 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3561 }
3562 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3563 $ret = str_replace('__b__', $monthtextshort, $ret);
3564 $ret = str_replace('__B__', $monthtext, $ret);
3565 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3566 //return $ret;
3567 }
3568 if (preg_match('/__a__/i', $format)) {
3569 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3570 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3571
3572 if ($to_gmt) {
3573 $tzo = new DateTimeZone('UTC');
3574 } else {
3575 $tzo = new DateTimeZone(date_default_timezone_get());
3576 }
3577 $dtts = new DateTime();
3578 $dtts->setTimestamp($timetouse);
3579 $dtts->setTimezone($tzo);
3580 $w = $dtts->format("w");
3581 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3582
3583 $ret = str_replace('__A__', $dayweek, $ret);
3584 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3585 }
3586
3587 return $ret;
3588}
3589
3590
3611function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3612{
3613 if ($timestamp === '') {
3614 return array();
3615 }
3616
3617 $datetimeobj = new DateTime();
3618 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3619 if ($forcetimezone) {
3620 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3621 }
3622 $arrayinfo = array(
3623 'year' => ((int) date_format($datetimeobj, 'Y')),
3624 'mon' => ((int) date_format($datetimeobj, 'm')),
3625 'mday' => ((int) date_format($datetimeobj, 'd')),
3626 'wday' => ((int) date_format($datetimeobj, 'w')),
3627 'yday' => ((int) date_format($datetimeobj, 'z')),
3628 'hours' => ((int) date_format($datetimeobj, 'H')),
3629 'minutes' => ((int) date_format($datetimeobj, 'i')),
3630 'seconds' => ((int) date_format($datetimeobj, 's')),
3631 '0' => $timestamp
3632 );
3633
3634 return $arrayinfo;
3635}
3636
3658function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3659{
3660 global $conf;
3661 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3662
3663 if ($gm === 'auto') {
3664 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3665 }
3666 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3667
3668 // Clean parameters
3669 if ($hour == -1 || empty($hour)) {
3670 $hour = 0;
3671 }
3672 if ($minute == -1 || empty($minute)) {
3673 $minute = 0;
3674 }
3675 if ($second == -1 || empty($second)) {
3676 $second = 0;
3677 }
3678
3679 // Check parameters
3680 if ($check) {
3681 if (!$month || !$day) {
3682 return '';
3683 }
3684 if ($day > 31) {
3685 return '';
3686 }
3687 if ($month > 12) {
3688 return '';
3689 }
3690 if ($hour < 0 || $hour > 24) {
3691 return '';
3692 }
3693 if ($minute < 0 || $minute > 60) {
3694 return '';
3695 }
3696 if ($second < 0 || $second > 60) {
3697 return '';
3698 }
3699 }
3700
3701 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3702 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3703 $localtz = new DateTimeZone($default_timezone);
3704 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3705 // We use dol_tz_string first because it is more reliable.
3706 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3707 try {
3708 $localtz = new DateTimeZone($default_timezone);
3709 } catch (Exception $e) {
3710 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3711 $default_timezone = @date_default_timezone_get();
3712 }
3713 } elseif (strrpos($gm, "tz,") !== false) {
3714 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3715 try {
3716 $localtz = new DateTimeZone($timezone);
3717 } catch (Exception $e) {
3718 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3719 }
3720 }
3721
3722 if (empty($localtz)) {
3723 $localtz = new DateTimeZone('UTC');
3724 }
3725 //var_dump($localtz);
3726 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3727 $dt = new DateTime('now', $localtz);
3728 $dt->setDate((int) $year, (int) $month, (int) $day);
3729 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3730 $date = $dt->getTimestamp(); // should include daylight saving time
3731 //var_dump($date);
3732 return $date;
3733}
3734
3735
3746function dol_now($mode = 'auto')
3747{
3748 $ret = 0;
3749
3750 if ($mode === 'auto') {
3751 $mode = 'gmt';
3752 }
3753
3754 if ($mode == 'gmt') {
3755 $ret = time(); // Time for now at greenwich.
3756 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3757 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3758 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3759 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3760 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3761 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3762 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3763 // $ret=dol_now('gmt')+($tzsecond*3600);
3764 //}
3765 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3766 // Time for now with user timezone added
3767 //print 'time: '.time();
3768 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3769 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3770 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3771 }
3772
3773 return $ret;
3774}
3775
3776
3785function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3786{
3787 global $conf, $langs;
3788 $level = 1024;
3789
3790 if (!empty($conf->dol_optimize_smallscreen)) {
3791 $shortunit = 1;
3792 }
3793
3794 // Set value text
3795 if (empty($shortvalue) || $size < ($level * 10)) {
3796 $ret = $size;
3797 $textunitshort = $langs->trans("b");
3798 $textunitlong = $langs->trans("Bytes");
3799 } else {
3800 $ret = round($size / $level, 0);
3801 $textunitshort = $langs->trans("Kb");
3802 $textunitlong = $langs->trans("KiloBytes");
3803 }
3804 // Use long or short text unit
3805 if (empty($shortunit)) {
3806 $ret .= ' '.$textunitlong;
3807 } else {
3808 $ret .= ' '.$textunitshort;
3809 }
3810
3811 return $ret;
3812}
3813
3824function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3825{
3826 global $langs;
3827
3828 if (empty($url)) {
3829 return '';
3830 }
3831
3832 $linkstart = '<a href="';
3833 if (!preg_match('/^http/i', $url)) {
3834 $linkstart .= 'http://';
3835 }
3836 $linkstart .= $url;
3837 $linkstart .= '"';
3838 if ($target) {
3839 $linkstart .= ' target="'.$target.'"';
3840 }
3841 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3842 $linkstart .= '>';
3843
3844 $link = '';
3845 if (!preg_match('/^http/i', $url)) {
3846 $link .= 'http://';
3847 }
3848 $link .= dol_trunc($url, $max);
3849
3850 $linkend = '</a>';
3851
3852 if ($morecss == 'float') { // deprecated
3853 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3854 } else {
3855 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3856 }
3857}
3858
3872function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
3873{
3874 global $user, $langs, $hookmanager;
3875
3876 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3877 //$showinvalid = 1; $email = 'rrrrr';
3878
3879 $newemail = dol_escape_htmltag($email);
3880
3881 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3882 $withpicto = 0;
3883 }
3884
3885 if (empty($email)) {
3886 return '&nbsp;';
3887 }
3888
3889 if ($addlink == 1) {
3890 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
3891 if (!preg_match('/^mailto:/i', $email)) {
3892 $newemail .= 'mailto:';
3893 }
3894 $newemail .= $email;
3895 $newemail .= '" target="_blank">';
3896
3897 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3898
3899 if ($max > 0) {
3900 $newemail .= dol_trunc($email, $max);
3901 } else {
3902 $newemail .= $email;
3903 }
3904 $newemail .= '</a>';
3905 if ($showinvalid && !isValidEmail($email)) {
3906 $langs->load("errors");
3907 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3908 }
3909
3910 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3911 $type = 'AC_EMAIL';
3912 $linktoaddaction = '';
3913 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3914 $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>';
3915 }
3916 if ($linktoaddaction) {
3917 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3918 }
3919 }
3920 } elseif ($addlink === 'thirdparty') {
3921 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
3922 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3923 if ($withpicto == 1) {
3924 $tmpnewemail .= $newemail;
3925 }
3926 $tmpnewemail .= '</a>';
3927
3928 $newemail = $tmpnewemail;
3929 } else {
3930 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3931
3932 if ($showinvalid && !isValidEmail($email)) {
3933 $langs->load("errors");
3934 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3935 }
3936 }
3937
3938 //$rep = '<div class="nospan" style="margin-right: 10px">';
3939 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3940 //$rep .= '</div>';
3941 $rep = $newemail;
3942
3943 if ($hookmanager) {
3944 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3945
3946 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3947 if ($reshook > 0) {
3948 $rep = '';
3949 }
3950 $rep .= $hookmanager->resPrint;
3951 }
3952
3953 return $rep;
3954}
3955
3961function getArrayOfSocialNetworks()
3962{
3963 global $conf, $db;
3964
3965 $socialnetworks = array();
3966 // Enable caching of array
3967 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3968 $cachekey = 'socialnetworks_' . $conf->entity;
3969 $dataretrieved = dol_getcache($cachekey);
3970 if (!is_null($dataretrieved)) {
3971 $socialnetworks = $dataretrieved;
3972 } else {
3973 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3974 $sql .= " WHERE entity=".$conf->entity;
3975 $resql = $db->query($sql);
3976 if ($resql) {
3977 while ($obj = $db->fetch_object($resql)) {
3978 $socialnetworks[$obj->code] = array(
3979 'rowid' => $obj->rowid,
3980 'label' => $obj->label,
3981 'url' => $obj->url,
3982 'icon' => $obj->icon,
3983 'active' => $obj->active,
3984 );
3985 }
3986 }
3987 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3988 }
3989 return $socialnetworks;
3990}
3991
4002function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
4003{
4004 global $hookmanager, $langs, $user;
4005
4006 $htmllink = $value;
4007
4008 if (empty($value)) {
4009 return '&nbsp;';
4010 }
4011
4012 if (!empty($type)) {
4013 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4014 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4015 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
4016 if ($type == 'skype') {
4017 $htmllink .= dol_escape_htmltag($value);
4018 $htmllink .= '&nbsp; <a href="skype:';
4019 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4020 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
4021 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
4022 $htmllink .= '</a><a href="skype:';
4023 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4024 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
4025 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
4026 $htmllink .= '</a>';
4027 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4028 $addlink = 'AC_SKYPE';
4029 $link = '';
4030 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4031 $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>';
4032 }
4033 $htmllink .= ($link ? ' '.$link : '');
4034 }
4035 } else {
4036 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
4037 if (getDolGlobalString($networkconstname)) {
4038 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
4039 if (preg_match('/^https?:\/\//i', $link)) {
4040 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4041 } else {
4042 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4043 }
4044 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
4045 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4046 if ($tmpvirginurl) {
4047 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4048 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4049
4050 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4051 if ($tmpvirginurl3) {
4052 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4053 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4054 }
4055
4056 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4057 if ($tmpvirginurl2) {
4058 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4059 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4060 }
4061 }
4062 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4063 if (preg_match('/^https?:\/\//i', $link)) {
4064 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4065 } else {
4066 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4067 }
4068 } else {
4069 $htmllink .= dol_escape_htmltag($value);
4070 }
4071 }
4072 $htmllink .= '</div>';
4073 } else {
4074 $langs->load("errors");
4075 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4076 }
4077
4078 if ($hookmanager) {
4079 $parameters = array(
4080 'value' => $value,
4081 'cid' => $cid,
4082 'socid' => $socid,
4083 'type' => $type,
4084 'dictsocialnetworks' => $dictsocialnetworks,
4085 );
4086
4087 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4088 if ($reshook > 0) {
4089 $htmllink = '';
4090 }
4091 $htmllink .= $hookmanager->resPrint;
4092 }
4093
4094 return $htmllink;
4095}
4096
4106function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4107{
4108 global $mysoc;
4109
4110 if (empty($profID) || empty($profIDtype)) {
4111 return '';
4112 }
4113 if (empty($countrycode)) {
4114 $countrycode = $mysoc->country_code;
4115 }
4116 $newProfID = $profID;
4117 $id = substr($profIDtype, -1);
4118 $ret = '';
4119 if (strtoupper($countrycode) == 'FR') {
4120 // France
4121 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4122
4123 if ($id == 1 && dol_strlen($newProfID) == 9) {
4124 // SIREN (ex: 123 123 123)
4125 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4126 }
4127 if ($id == 2 && dol_strlen($newProfID) == 14) {
4128 // SIRET (ex: 123 123 123 12345)
4129 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4130 }
4131 if ($id == 3 && dol_strlen($newProfID) == 5) {
4132 // NAF/APE (ex: 69.20Z)
4133 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4134 }
4135 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4136 // TVA intracommunautaire (ex: FR12 123 123 123)
4137 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4138 }
4139 }
4140 if (!empty($addcpButton)) {
4141 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4142 } else {
4143 $ret = $newProfID;
4144 }
4145 return $ret;
4146}
4147
4163function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4164{
4165 global $conf, $user, $langs, $mysoc, $hookmanager;
4166
4167 // Clean phone parameter
4168 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4169 if (empty($phone)) {
4170 return '';
4171 }
4172 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4173 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4174 }
4175 if (empty($countrycode) && is_object($mysoc)) {
4176 $countrycode = $mysoc->country_code;
4177 }
4178
4179 // Short format for small screens
4180 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4181 $separ = '';
4182 }
4183
4184 $newphone = $phone;
4185 $newphonewa = $phone;
4186 if (strtoupper($countrycode) == "FR") {
4187 // France
4188 if (dol_strlen($phone) == 10) {
4189 $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);
4190 } elseif (dol_strlen($phone) == 7) {
4191 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4192 } elseif (dol_strlen($phone) == 9) {
4193 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4194 } elseif (dol_strlen($phone) == 11) {
4195 $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);
4196 } elseif (dol_strlen($phone) == 12) {
4197 $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);
4198 } elseif (dol_strlen($phone) == 13) {
4199 $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);
4200 }
4201 } elseif (strtoupper($countrycode) == "CA") {
4202 if (dol_strlen($phone) == 10) {
4203 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4204 }
4205 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4206 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4207 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4208 }
4209 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4210 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4211 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4212 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4213 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4214 }
4215 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4216 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4217 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4218 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4219 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4220 }
4221 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4222 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4223 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4224 }
4225 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4226 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4227 $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);
4228 }
4229 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4230 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4231 $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);
4232 }
4233 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4234 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4235 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4236 }
4237 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4238 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4239 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4240 }
4241 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4242 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4243 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4244 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4245 $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);
4246 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4247 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4248 }
4249 } elseif (strtoupper($countrycode) == "ML") {//Mali
4250 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4251 $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);
4252 }
4253 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4254 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4255 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4256 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4257 $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);
4258 }
4259 } elseif (strtoupper($countrycode) == "MU") {
4260 //Maurice
4261 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4262 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4263 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4264 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4265 }
4266 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4267 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4268 $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);
4269 }
4270 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4271 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4272 $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);
4273 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
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, 3);
4275 }
4276 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4277 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4278 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4279 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4280 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4281 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4282 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4283 }
4284 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4285 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4286 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4287 }
4288 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4289 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4290 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4291 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4292 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4293 }
4294 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4295 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4296 $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);
4297 }
4298 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4299 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4300 $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);
4301 }
4302 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4303 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4304 $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);
4305 }
4306 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4307 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4308 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4309 }
4310 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4311 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4312 $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);
4313 }
4314 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4315 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4316 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4317 }
4318 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4319 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4320 $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);
4321 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4322 $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);
4323 }
4324 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4325 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4326 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4327 }
4328 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4329 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4330 $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);
4331 }
4332 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4333 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4334 $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);
4335 }
4336 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4337 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4338 $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);
4339 }
4340 } elseif (strtoupper($countrycode) == "IT") {//Italie
4341 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4342 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4343 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4344 $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);
4345 }
4346 } elseif (strtoupper($countrycode) == "AU") {
4347 //Australie
4348 if (dol_strlen($phone) == 12) {
4349 //ex: +61_A_BCDE_FGHI
4350 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4351 }
4352 } elseif (strtoupper($countrycode) == "LU") {
4353 // Luxembourg
4354 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4355 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4356 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4357 $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);
4358 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
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, 2);
4360 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4361 $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);
4362 }
4363 } elseif (strtoupper($countrycode) == "PE") {
4364 // Peru
4365 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4366 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4367 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4368 $newphonewa = '+51'.$newphone;
4369 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4370 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4371 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4372 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4373 $newphonewa = $newphone;
4374 $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);
4375 }
4376 }
4377
4378 $newphoneastart = $newphoneaend = '';
4379 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4380 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
4381 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4382 $newphoneaend .= '</a>';
4383 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4384 if (empty($user->clicktodial_loaded)) {
4385 $user->fetch_clicktodial();
4386 }
4387
4388 // Define urlmask
4389 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4390 if (!empty($user->clicktodial_url)) {
4391 $urlmask = $user->clicktodial_url;
4392 }
4393
4394 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4395 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4396 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4397 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4398 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4399 // Those lines are for substitution
4400 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4401 '__PHONETO__' => urlencode($phone),
4402 '__LOGIN__' => $clicktodial_login,
4403 '__PASS__' => $clicktodial_password);
4404 $url = make_substitutions($url, $substitarray);
4405 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4406 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4407 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4408 $newphoneaend = '</a>';
4409 } else {
4410 // Old method
4411 $newphoneastart = '<a href="'.$url.'"';
4412 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4413 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4414 }
4415 $newphoneastart .= '>';
4416 $newphoneaend .= '</a>';
4417 }
4418 }
4419
4420 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4421 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4422 $type = 'AC_TEL';
4423 $addlinktoagenda = '';
4424 if ($addlink == 'AC_FAX') {
4425 $type = 'AC_FAX';
4426 }
4427 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4428 $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>';
4429 }
4430 if ($addlinktoagenda) {
4431 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4432 }
4433 }
4434 }
4435
4436 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4437 // Link to Whatsapp
4438 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4439 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4440 }
4441
4442 if (empty($titlealt)) {
4443 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4444 }
4445 $rep = '';
4446
4447 if ($hookmanager) {
4448 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4449 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4450 $rep .= $hookmanager->resPrint;
4451 }
4452 if (empty($reshook)) {
4453 $picto = '';
4454 if ($withpicto) {
4455 if ($withpicto == 'fax') {
4456 $picto = 'phoning_fax';
4457 } elseif ($withpicto == 'phone') {
4458 $picto = 'phoning';
4459 } elseif ($withpicto == 'mobile') {
4460 $picto = 'phoning_mobile';
4461 } else {
4462 $picto = '';
4463 }
4464 }
4465 if ($adddivfloat == 1) {
4466 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4467 } elseif (empty($adddivfloat)) {
4468 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4469 }
4470
4471 $rep .= $newphoneastart;
4472 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4473 if ($separ != 'hidenum') {
4474 $rep .= ($withpicto ? ' ' : '').$newphone;
4475 }
4476 $rep .= $newphoneaend;
4477
4478 if ($adddivfloat == 1) {
4479 $rep .= '</div>';
4480 } elseif (empty($adddivfloat)) {
4481 $rep .= '</span>';
4482 }
4483 }
4484
4485 return $rep;
4486}
4487
4495function dol_print_ip($ip, $mode = 0)
4496{
4497 global $langs;
4498
4499 $ret = '';
4500
4501 if (empty($mode)) {
4502 $ret .= $ip;
4503 }
4504
4505 if ($mode != 2) {
4506 $countrycode = dolGetCountryCodeFromIp($ip);
4507 if ($countrycode) { // If success, countrycode is us, fr, ...
4508 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4509 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4510 } else {
4511 $ret .= ' ('.$countrycode.')';
4512 }
4513 } else {
4514 // Nothing
4515 }
4516 }
4517
4518 return $ret;
4519}
4520
4529function getUserRemoteIP()
4530{
4531 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4532 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4533 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4534 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4535 } else {
4536 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4537 }
4538 } else {
4539 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4540 }
4541 } else {
4542 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4543 }
4544 return $ip;
4545}
4546
4555function isHTTPS()
4556{
4557 $isSecure = false;
4558 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4559 $isSecure = true;
4560 } 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') {
4561 $isSecure = true;
4562 }
4563 return $isSecure;
4564}
4565
4572function dolGetCountryCodeFromIp($ip)
4573{
4574 global $conf;
4575
4576 $countrycode = '';
4577
4578 if (isModEnabled('geoipmaxmind')) {
4579 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4580 //$ip='24.24.24.24';
4581 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4582 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4583 $geoip = new DolGeoIP('country', $datafile);
4584 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4585 $countrycode = $geoip->getCountryCodeFromIP($ip);
4586 }
4587
4588 return $countrycode;
4589}
4590
4591
4598function dol_user_country()
4599{
4600 global $conf, $langs, $user;
4601
4602 //$ret=$user->xxx;
4603 $ret = '';
4604 if (isModEnabled('geoipmaxmind')) {
4605 $ip = getUserRemoteIP();
4606 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4607 //$ip='24.24.24.24';
4608 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4609 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4610 $geoip = new DolGeoIP('country', $datafile);
4611 $countrycode = $geoip->getCountryCodeFromIP($ip);
4612 $ret = $countrycode;
4613 }
4614 return $ret;
4615}
4616
4629function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4630{
4631 global $conf, $user, $langs, $hookmanager;
4632
4633 $out = '';
4634
4635 if ($address) {
4636 if ($hookmanager) {
4637 $parameters = array('element' => $element, 'id' => $id);
4638 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4639 $out .= $hookmanager->resPrint;
4640 }
4641 if (empty($reshook)) {
4642 if (empty($charfornl)) {
4643 $out .= nl2br($address);
4644 } else {
4645 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4646 }
4647
4648 // TODO Remove this block, we can add this using the hook now
4649 $showgmap = $showomap = 0;
4650 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4651 $showgmap = 1;
4652 }
4653 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4654 $showgmap = 1;
4655 }
4656 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4657 $showgmap = 1;
4658 }
4659 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4660 $showgmap = 1;
4661 }
4662 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4663 $showomap = 1;
4664 }
4665 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4666 $showomap = 1;
4667 }
4668 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4669 $showomap = 1;
4670 }
4671 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4672 $showomap = 1;
4673 }
4674 if ($showgmap) {
4675 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4676 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4677 }
4678 if ($showomap) {
4679 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4680 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4681 }
4682 }
4683 }
4684 if ($noprint) {
4685 return $out;
4686 } else {
4687 print $out;
4688 return null;
4689 }
4690}
4691
4692
4702function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4703{
4704 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4705 return true;
4706 }
4707 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4708 return true;
4709 }
4710 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4711 return true;
4712 }
4713
4714 return false;
4715}
4716
4726function isValidMXRecord($domain)
4727{
4728 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4729 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4730 return 0;
4731 }
4732 if (function_exists('getmxrr')) {
4733 $mxhosts = array();
4734 $weight = array();
4735 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4736 if (count($mxhosts) > 1) {
4737 return 1;
4738 }
4739 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4740 return 1;
4741 }
4742
4743 return 0;
4744 }
4745 }
4746
4747 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4748 return -1;
4749}
4750
4758function isValidPhone($phone)
4759{
4760 return true;
4761}
4762
4763
4773function dolGetFirstLetters($s, $nbofchar = 1)
4774{
4775 $ret = '';
4776 $tmparray = explode(' ', $s);
4777 foreach ($tmparray as $tmps) {
4778 $ret .= dol_substr($tmps, 0, $nbofchar);
4779 }
4780
4781 return $ret;
4782}
4783
4784
4792function dol_strlen($string, $stringencoding = 'UTF-8')
4793{
4794 if (is_null($string)) {
4795 return 0;
4796 }
4797
4798 if (function_exists('mb_strlen')) {
4799 return mb_strlen($string, $stringencoding);
4800 } else {
4801 return strlen($string);
4802 }
4803}
4804
4815function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4816{
4817 global $langs;
4818
4819 if (empty($stringencoding)) {
4820 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4821 }
4822
4823 $ret = '';
4824 if (empty($trunconbytes)) {
4825 if (function_exists('mb_substr')) {
4826 $ret = mb_substr($string, $start, $length, $stringencoding);
4827 } else {
4828 $ret = substr($string, $start, $length);
4829 }
4830 } else {
4831 if (function_exists('mb_strcut')) {
4832 $ret = mb_strcut($string, $start, $length, $stringencoding);
4833 } else {
4834 $ret = substr($string, $start, $length);
4835 }
4836 }
4837 return $ret;
4838}
4839
4840
4854function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4855{
4856 global $conf;
4857
4858 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4859 return $string;
4860 }
4861
4862 if (empty($stringencoding)) {
4863 $stringencoding = 'UTF-8';
4864 }
4865 // reduce for small screen
4866 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4867 $size = round($size / 3);
4868 }
4869
4870 // We go always here
4871 if ($trunc == 'right') {
4872 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4873 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4874 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4875 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4876 } else {
4877 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4878 return $string;
4879 }
4880 } elseif ($trunc == 'middle') {
4881 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4882 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4883 $size1 = (int) round($size / 2);
4884 $size2 = (int) round($size / 2);
4885 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4886 } else {
4887 return $string;
4888 }
4889 } elseif ($trunc == 'left') {
4890 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4891 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4892 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4893 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4894 } else {
4895 return $string;
4896 }
4897 } elseif ($trunc == 'wrap') {
4898 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4899 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4900 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4901 } else {
4902 return $string;
4903 }
4904 } else {
4905 return 'BadParam3CallingDolTrunc';
4906 }
4907}
4908
4916function getPictoForType($key, $morecss = '')
4917{
4918 // Set array with type -> picto
4919 $type2picto = array(
4920 'varchar' => 'font',
4921 'text' => 'font',
4922 'html' => 'code',
4923 'int' => 'sort-numeric-down',
4924 'double' => 'sort-numeric-down',
4925 'price' => 'currency',
4926 'pricecy' => 'multicurrency',
4927 'password' => 'key',
4928 'boolean' => 'check-square',
4929 'date' => 'calendar',
4930 'datetime' => 'calendar',
4931 'duration' => 'hourglass',
4932 'phone' => 'phone',
4933 'mail' => 'email',
4934 'url' => 'url',
4935 'ip' => 'country',
4936 'select' => 'list',
4937 'sellist' => 'list',
4938 'stars' => 'fontawesome_star_fas',
4939 'radio' => 'check-circle',
4940 'checkbox' => 'list',
4941 'chkbxlst' => 'list',
4942 'link' => 'link',
4943 'icon' => "question",
4944 'point' => "country",
4945 'multipts' => 'country',
4946 'linestrg' => "country",
4947 'polygon' => "country",
4948 'separate' => 'minus'
4949 );
4950
4951 if (!empty($type2picto[$key])) {
4952 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4953 }
4954
4955 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4956}
4957
4958
4980function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4981{
4982 global $conf;
4983
4984 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4985 $url = DOL_URL_ROOT;
4986 $theme = isset($conf->theme) ? $conf->theme : null;
4987 $path = 'theme/'.$theme;
4988 if (empty($picto)) {
4989 $picto = 'generic';
4990 }
4991
4992 // Define fullpathpicto to use into src
4993 if ($pictoisfullpath) {
4994 // Clean parameters
4995 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4996 $picto .= '.png';
4997 }
4998 $fullpathpicto = $picto;
4999 $reg = array();
5000 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5001 $morecss .= ($morecss ? ' ' : '').$reg[1];
5002 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5003 }
5004 } else {
5005 // $picto can not be null since replaced with 'generic' in that case
5006 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5007 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5008 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5009 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5010
5011 // Fix some values of $pictowithouttext
5012 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5013 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5014 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5015 }
5016
5017 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5018 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5019 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5020 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5021
5022 // Compatibility with old fontawesome versions
5023 if ($pictowithouttext == 'file-o') {
5024 $pictowithouttext = 'file';
5025 }
5026
5027 $pictowithouttextarray = explode('_', $pictowithouttext);
5028 $marginleftonlyshort = 0;
5029
5030 if (!empty($pictowithouttextarray[1])) {
5031 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5032 $fakey = 'fa-'.$pictowithouttextarray[0];
5033 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5034 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5035 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5036 } else {
5037 $fakey = 'fa-'.$pictowithouttext;
5038 $faprefix = 'fas';
5039 $facolor = '';
5040 $fasize = '';
5041 }
5042
5043 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5044 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5045 $morestyle = '';
5046 $reg = array();
5047 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5048 $morecss .= ($morecss ? ' ' : '').$reg[1];
5049 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5050 }
5051 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5052 $morestyle = $reg[1];
5053 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5054 }
5055 $moreatt = trim($moreatt);
5056
5057 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5058 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5059 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5060 $enabledisablehtml .= $titlealt;
5061 }*/
5062 $enabledisablehtml .= '</span>';
5063
5064 return $enabledisablehtml;
5065 }
5066
5067 if (empty($srconly) && in_array($pictowithouttext, array(
5068 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5069 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5070 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5071 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5072 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5073 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5074 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
5075 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5076 'commercial', 'companies',
5077 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5078 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5079 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5080 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5081 'hands-helping', 'help', 'holiday',
5082 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5083 'key', 'knowledgemanagement',
5084 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5085 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5086 'off', 'on', 'order',
5087 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5088 'stock', 'resize', 'service', 'stats',
5089 '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',
5090 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
5091 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5092 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5093 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5094 'technic', 'ticket',
5095 'error', 'warning',
5096 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
5097 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5098 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5099 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5100 'conferenceorbooth', 'eventorganization',
5101 'stamp', 'signature',
5102 'webportal'
5103 ))) {
5104 $fakey = $pictowithouttext;
5105 $facolor = '';
5106 $fasize = '';
5107 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5108 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'))) {
5109 $fa = 'far';
5110 }
5111 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
5112 $fa = 'fab';
5113 }
5114
5115 $arrayconvpictotofa = array(
5116 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5117 'asset' => 'money-check-alt', 'autofill' => 'fill',
5118 'back' => 'arrow-left', 'bank_account' => 'university',
5119 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5120 'bookcal' => 'calendar-check',
5121 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5122 'bom' => 'shapes',
5123 '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',
5124 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5125 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5126 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5127 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5128 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5129 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5130 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5131 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5132 'generic' => 'file', 'holiday' => 'umbrella-beach',
5133 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5134 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5135 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5136 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5137 'sign-out' => 'sign-out-alt',
5138 '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',
5139 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5140 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5141 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5142 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5143 'other' => 'square',
5144 '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',
5145 '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',
5146 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5147 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5148 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5149 'service' => 'concierge-bell',
5150 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5151 'status' => 'stop-circle',
5152 'stripe' => 'stripe-s', 'supplier' => 'building',
5153 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5154 'title_agenda' => 'calendar-alt',
5155 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5156 'jabber' => 'comment-o',
5157 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5158 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5159 'webportal' => 'door-open'
5160 );
5161 if ($conf->currency == 'EUR') {
5162 $arrayconvpictotofa['currency'] = 'euro-sign';
5163 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5164 } else {
5165 $arrayconvpictotofa['currency'] = 'dollar-sign';
5166 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5167 }
5168 if ($pictowithouttext == 'off') {
5169 $fakey = 'fa-square';
5170 $fasize = '1.3em';
5171 } elseif ($pictowithouttext == 'on') {
5172 $fakey = 'fa-check-square';
5173 $fasize = '1.3em';
5174 } elseif ($pictowithouttext == 'listlight') {
5175 $fakey = 'fa-download';
5176 $marginleftonlyshort = 1;
5177 } elseif ($pictowithouttext == 'printer') {
5178 $fakey = 'fa-print';
5179 $fasize = '1.2em';
5180 } elseif ($pictowithouttext == 'note') {
5181 $fakey = 'fa-sticky-note';
5182 $marginleftonlyshort = 1;
5183 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5184 $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');
5185 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5186 if (preg_match('/selected/', $pictowithouttext)) {
5187 $facolor = '#888';
5188 }
5189 $marginleftonlyshort = 1;
5190 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5191 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5192 } else {
5193 $fakey = 'fa-'.$pictowithouttext;
5194 }
5195
5196 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5197 $morecss .= ' em092';
5198 }
5199 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5200 $morecss .= ' em088';
5201 }
5202 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5203 $morecss .= ' em080';
5204 }
5205
5206 // Define $marginleftonlyshort
5207 $arrayconvpictotomarginleftonly = array(
5208 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5209 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5210 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5211 );
5212 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5213 $marginleftonlyshort = 0;
5214 }
5215
5216 // Add CSS
5217 $arrayconvpictotomorcess = array(
5218 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5219 'bank_account' => 'infobox-bank_account',
5220 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5221 'bookcal' => 'infobox-action',
5222 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5223 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5224 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5225 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5226 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5227 'incoterm' => 'infobox-supplier_proposal',
5228 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5229 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5230 'order' => 'infobox-commande',
5231 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5232 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5233 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5234 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5235 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5236 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5237 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5238 'resource' => 'infobox-action',
5239 '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',
5240 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5241 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5242 'vat' => 'infobox-bank_account',
5243 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5244 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5245 );
5246 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5247 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5248 }
5249
5250 // Define $color
5251 $arrayconvpictotocolor = array(
5252 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5253 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5254 'dynamicprice' => '#a69944',
5255 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5256 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5257 'lock' => '#ddd', 'lot' => '#a69944',
5258 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5259 'other' => '#ddd', 'world' => '#986c6a',
5260 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5261 //'shipment'=>'#a69944',
5262 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5263 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5264 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5265 'website' => '#304', 'workstation' => '#a69944'
5266 );
5267 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5268 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5269 }
5270
5271 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5272 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5273 $morestyle = '';
5274 $reg = array();
5275 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5276 $morecss .= ($morecss ? ' ' : '').$reg[1];
5277 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5278 }
5279 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5280 $morestyle = $reg[1];
5281 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5282 }
5283 $moreatt = trim($moreatt);
5284
5285 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5286 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5287 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5288 $enabledisablehtml .= $titlealt;
5289 }*/
5290 $enabledisablehtml .= '</span>';
5291
5292 return $enabledisablehtml;
5293 }
5294
5295 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5296 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5297 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5298 $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
5299 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5300 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5301 }
5302
5303 // If we ask an image into $url/$mymodule/img (instead of default path)
5304 $regs = array();
5305 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5306 $picto = $regs[1];
5307 $path = $regs[2]; // $path is $mymodule
5308 }
5309
5310 // Clean parameters
5311 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5312 $picto .= '.png';
5313 }
5314 // If alt path are defined, define url where img file is, according to physical path
5315 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5316 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5317 if ($type == 'main') {
5318 continue;
5319 }
5320 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5321 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5322 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5323 break;
5324 }
5325 }
5326
5327 // $url is '' or '/custom', $path is current theme or
5328 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5329 }
5330
5331 if ($srconly) {
5332 return $fullpathpicto;
5333 }
5334
5335 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5336 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
5337}
5338
5352function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5353{
5354 if (strpos($picto, '^') === 0) {
5355 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5356 } else {
5357 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5358 }
5359}
5360
5372function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5373{
5374 global $conf;
5375
5376 if (is_numeric($picto)) {
5377 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5378 //$picto = $leveltopicto[$picto];
5379 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5380 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5381 $picto .= '.png';
5382 }
5383
5384 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5385
5386 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5387}
5388
5400function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5401{
5402 global $conf;
5403
5404 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5405 $picto .= '.png';
5406 }
5407
5408 if ($pictoisfullpath) {
5409 $path = $picto;
5410 } else {
5411 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5412
5413 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5414 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5415
5416 if (file_exists($themepath)) {
5417 $path = $themepath;
5418 }
5419 }
5420 }
5421
5422 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5423}
5424
5438function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5439{
5440 global $langs;
5441
5442 if (empty($titlealt) || $titlealt == 'default') {
5443 if ($numaction == '-1' || $numaction == 'ST_NO') {
5444 $numaction = -1;
5445 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5446 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5447 $numaction = 0;
5448 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5449 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5450 $numaction = 1;
5451 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5452 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5453 $numaction = 2;
5454 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5455 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5456 $numaction = 3;
5457 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5458 } else {
5459 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5460 $numaction = 0;
5461 }
5462 }
5463 if (!is_numeric($numaction)) {
5464 $numaction = 0;
5465 }
5466
5467 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5468}
5469
5477function img_pdf($titlealt = 'default', $size = 3)
5478{
5479 global $langs;
5480
5481 if ($titlealt == 'default') {
5482 $titlealt = $langs->trans('Show');
5483 }
5484
5485 return img_picto($titlealt, 'pdf'.$size.'.png');
5486}
5487
5495function img_edit_add($titlealt = 'default', $other = '')
5496{
5497 global $langs;
5498
5499 if ($titlealt == 'default') {
5500 $titlealt = $langs->trans('Add');
5501 }
5502
5503 return img_picto($titlealt, 'edit_add.png', $other);
5504}
5512function img_edit_remove($titlealt = 'default', $other = '')
5513{
5514 global $langs;
5515
5516 if ($titlealt == 'default') {
5517 $titlealt = $langs->trans('Remove');
5518 }
5519
5520 return img_picto($titlealt, 'edit_remove.png', $other);
5521}
5522
5531function img_edit($titlealt = 'default', $float = 0, $other = '')
5532{
5533 global $langs;
5534
5535 if ($titlealt == 'default') {
5536 $titlealt = $langs->trans('Modify');
5537 }
5538
5539 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5540}
5541
5550function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5551{
5552 global $langs;
5553
5554 if ($titlealt == 'default') {
5555 $titlealt = $langs->trans('View');
5556 }
5557
5558 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5559
5560 return img_picto($titlealt, 'eye', $moreatt);
5561}
5562
5571function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5572{
5573 global $langs;
5574
5575 if ($titlealt == 'default') {
5576 $titlealt = $langs->trans('Delete');
5577 }
5578
5579 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5580}
5581
5589function img_printer($titlealt = "default", $other = '')
5590{
5591 global $langs;
5592 if ($titlealt == "default") {
5593 $titlealt = $langs->trans("Print");
5594 }
5595 return img_picto($titlealt, 'printer.png', $other);
5596}
5597
5605function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5606{
5607 global $langs;
5608
5609 if ($titlealt == 'default') {
5610 $titlealt = $langs->trans('Split');
5611 }
5612
5613 return img_picto($titlealt, 'split.png', $other);
5614}
5615
5623function img_help($usehelpcursor = 1, $usealttitle = 1)
5624{
5625 global $langs;
5626
5627 if ($usealttitle) {
5628 if (is_string($usealttitle)) {
5629 $usealttitle = dol_escape_htmltag($usealttitle);
5630 } else {
5631 $usealttitle = $langs->trans('Info');
5632 }
5633 }
5634
5635 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5636}
5637
5644function img_info($titlealt = 'default')
5645{
5646 global $langs;
5647
5648 if ($titlealt == 'default') {
5649 $titlealt = $langs->trans('Informations');
5650 }
5651
5652 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5653}
5654
5663function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5664{
5665 global $langs;
5666
5667 if ($titlealt == 'default') {
5668 $titlealt = $langs->trans('Warning');
5669 }
5670
5671 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5672 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5673}
5674
5681function img_error($titlealt = 'default')
5682{
5683 global $langs;
5684
5685 if ($titlealt == 'default') {
5686 $titlealt = $langs->trans('Error');
5687 }
5688
5689 return img_picto($titlealt, 'error.png');
5690}
5691
5699function img_next($titlealt = 'default', $moreatt = '')
5700{
5701 global $langs;
5702
5703 if ($titlealt == 'default') {
5704 $titlealt = $langs->trans('Next');
5705 }
5706
5707 //return img_picto($titlealt, 'next.png', $moreatt);
5708 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5709}
5710
5718function img_previous($titlealt = 'default', $moreatt = '')
5719{
5720 global $langs;
5721
5722 if ($titlealt == 'default') {
5723 $titlealt = $langs->trans('Previous');
5724 }
5725
5726 //return img_picto($titlealt, 'previous.png', $moreatt);
5727 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5728}
5729
5738function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5739{
5740 global $langs;
5741
5742 if ($titlealt == 'default') {
5743 $titlealt = $langs->trans('Down');
5744 }
5745
5746 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5747}
5748
5757function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5758{
5759 global $langs;
5760
5761 if ($titlealt == 'default') {
5762 $titlealt = $langs->trans('Up');
5763 }
5764
5765 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5766}
5767
5776function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5777{
5778 global $langs;
5779
5780 if ($titlealt == 'default') {
5781 $titlealt = $langs->trans('Left');
5782 }
5783
5784 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5785}
5786
5795function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5796{
5797 global $langs;
5798
5799 if ($titlealt == 'default') {
5800 $titlealt = $langs->trans('Right');
5801 }
5802
5803 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5804}
5805
5813function img_allow($allow, $titlealt = 'default')
5814{
5815 global $langs;
5816
5817 if ($titlealt == 'default') {
5818 $titlealt = $langs->trans('Active');
5819 }
5820
5821 if ($allow == 1) {
5822 return img_picto($titlealt, 'tick.png');
5823 }
5824
5825 return '-';
5826}
5827
5835function img_credit_card($brand, $morecss = null)
5836{
5837 if (is_null($morecss)) {
5838 $morecss = 'fa-2x';
5839 }
5840
5841 if ($brand == 'visa' || $brand == 'Visa') {
5842 $brand = 'cc-visa';
5843 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5844 $brand = 'cc-mastercard';
5845 } elseif ($brand == 'amex' || $brand == 'American Express') {
5846 $brand = 'cc-amex';
5847 } elseif ($brand == 'discover' || $brand == 'Discover') {
5848 $brand = 'cc-discover';
5849 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5850 $brand = 'cc-jcb';
5851 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5852 $brand = 'cc-diners-club';
5853 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5854 $brand = 'credit-card';
5855 }
5856
5857 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5858}
5859
5868function img_mime($file, $titlealt = '', $morecss = '')
5869{
5870 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5871
5872 $mimetype = dol_mimetype($file, '', 1);
5873 $mimeimg = dol_mimetype($file, '', 2);
5874 $mimefa = dol_mimetype($file, '', 4);
5875
5876 if (empty($titlealt)) {
5877 $titlealt = 'Mime type: '.$mimetype;
5878 }
5879
5880 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5881 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5882}
5883
5884
5892function img_search($titlealt = 'default', $other = '')
5893{
5894 global $langs;
5895
5896 if ($titlealt == 'default') {
5897 $titlealt = $langs->trans('Search');
5898 }
5899
5900 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5901
5902 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5903 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5904
5905 return $input;
5906}
5907
5915function img_searchclear($titlealt = 'default', $other = '')
5916{
5917 global $langs;
5918
5919 if ($titlealt == 'default') {
5920 $titlealt = $langs->trans('Search');
5921 }
5922
5923 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5924
5925 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5926 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5927
5928 return $input;
5929}
5930
5943function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5944{
5945 global $conf, $langs;
5946
5947 if ($infoonimgalt) {
5948 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5949 } else {
5950 if (empty($conf->use_javascript_ajax)) {
5951 $textfordropdown = '';
5952 }
5953
5954 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5955 $fa = 'info-circle';
5956 if ($picto == 'warning') {
5957 $fa = 'exclamation-triangle';
5958 }
5959 $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> ';
5960 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5961 $result .= ($nodiv ? '' : '</div>');
5962
5963 if ($textfordropdown) {
5964 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5965 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5966 jQuery(document).ready(function() {
5967 jQuery(".'.$class.'text").click(function() {
5968 console.log("toggle text");
5969 jQuery(".'.$class.'").toggle();
5970 });
5971 });
5972 </script>';
5973
5974 $result = $tmpresult.$result;
5975 }
5976 }
5977
5978 return $result;
5979}
5980
5981
5993function dol_print_error($db = null, $error = '', $errors = null)
5994{
5995 global $conf, $langs, $user, $argv;
5996 global $dolibarr_main_prod;
5997
5998 $out = '';
5999 $syslog = '';
6000
6001 // If error occurs before the $lang object was loaded
6002 if (!$langs) {
6003 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6004 $langs = new Translate('', $conf);
6005 $langs->load("main");
6006 }
6007
6008 // Load translation files required by the error messages
6009 $langs->loadLangs(array('main', 'errors'));
6010
6011 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6012 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6013 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6014 $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";
6015 }
6016 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6017
6018 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6019 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6020 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6021 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6022 }
6023 if ($user instanceof User) {
6024 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6025 }
6026 if (function_exists("phpversion")) {
6027 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6028 }
6029 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6030 if (function_exists("php_uname")) {
6031 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6032 }
6033 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6034 $out .= "<br>\n";
6035 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
6036 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
6037 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
6038 $out .= "<br>\n";
6039 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
6040 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
6041 } else { // Mode CLI
6042 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6043 $syslog .= "pid=".dol_getmypid();
6044 }
6045
6046 if (!empty($conf->modules)) {
6047 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6048 }
6049
6050 if (is_object($db)) {
6051 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6052 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6053 $lastqueryerror = $db->lastqueryerror();
6054 if (!utf8_check($lastqueryerror)) {
6055 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6056 }
6057 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6058 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6059 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6060 $out .= "<br>\n";
6061 } else { // Mode CLI
6062 // No dol_escape_htmltag for output, we are in CLI mode
6063 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6064 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6065 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6066 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6067 }
6068 $syslog .= ", sql=".$db->lastquery();
6069 $syslog .= ", db_error=".$db->lasterror();
6070 }
6071
6072 if ($error || $errors) {
6073 // Merge all into $errors array
6074 if (is_array($error) && is_array($errors)) {
6075 $errors = array_merge($error, $errors);
6076 } elseif (is_array($error)) { // deprecated, use second parameters
6077 $errors = $error;
6078 } elseif (is_array($errors) && !empty($error)) {
6079 $errors = array_merge(array($error), $errors);
6080 } elseif (!empty($error)) {
6081 $errors = array_merge(array($error), array($errors));
6082 }
6083
6084 $langs->load("errors");
6085
6086 foreach ($errors as $msg) {
6087 if (empty($msg)) {
6088 continue;
6089 }
6090 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6091 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6092 } else { // Mode CLI
6093 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6094 }
6095 $syslog .= ", msg=".$msg;
6096 }
6097 }
6098 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6099 xdebug_print_function_stack();
6100 $out .= '<b>XDebug information:</b>'."<br>\n";
6101 $out .= 'File: '.xdebug_call_file()."<br>\n";
6102 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6103 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6104 $out .= "<br>\n";
6105 }
6106
6107 // Return a http header with error code if possible
6108 if (!headers_sent()) {
6109 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6110 top_httphead();
6111 }
6112 //http_response_code(500); // If we use 500, message is not output with some command line tools
6113 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
6114 }
6115
6116 if (empty($dolibarr_main_prod)) {
6117 print $out;
6118 } else {
6119 if (empty($langs->defaultlang)) {
6120 $langs->setDefaultLang();
6121 }
6122 $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.
6123 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6124 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";
6125 print $langs->trans("DolibarrHasDetectedError").'. ';
6126 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6127 if (!defined("MAIN_CORE_ERROR")) {
6128 define("MAIN_CORE_ERROR", 1);
6129 }
6130 }
6131
6132 dol_syslog("Error ".$syslog, LOG_ERR);
6133}
6134
6145function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6146{
6147 global $langs;
6148
6149 if (empty($email)) {
6150 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6151 }
6152
6153 $langs->load("errors");
6154 $now = dol_now();
6155
6156 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6157 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6158 if ($errormessage) {
6159 print '<br><br>'.$errormessage;
6160 }
6161 if (is_array($errormessages) && count($errormessages)) {
6162 foreach ($errormessages as $mesgtoshow) {
6163 print '<br><br>'.$mesgtoshow;
6164 }
6165 }
6166 print '</div></div>';
6167}
6168
6185function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6186{
6187 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6188}
6189
6208function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6209{
6210 global $langs, $form;
6211 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6212
6213 if ($moreattrib == 'class="right"') {
6214 $prefix .= 'right '; // For backward compatibility
6215 }
6216
6217 $sortorder = strtoupper($sortorder);
6218 $out = '';
6219 $sortimg = '';
6220
6221 $tag = 'th';
6222 if ($thead == 2) {
6223 $tag = 'div';
6224 }
6225
6226 $tmpsortfield = explode(',', $sortfield);
6227 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6228 $tmpfield = explode(',', $field);
6229 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6230
6231 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6232 $prefix = 'wrapcolumntitle '.$prefix;
6233 }
6234
6235 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6236 // If field is used as sort criteria we use a specific css class liste_titre_sel
6237 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6238 $liste_titre = 'liste_titre';
6239 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6240 $liste_titre = 'liste_titre_sel';
6241 }
6242
6243 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6244 //$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)).'"' : '');
6245 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6246 $tagstart .= '>';
6247
6248 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6249 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6250 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6251 $options = preg_replace('/&+/i', '&', $options);
6252 if (!preg_match('/^&/', $options)) {
6253 $options = '&'.$options;
6254 }
6255
6256 $sortordertouseinlink = '';
6257 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6258 if (preg_match('/^DESC/i', $sortorder)) {
6259 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6260 } else { // We reverse the var $sortordertouseinlink
6261 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6262 }
6263 } else { // We are on field that is the first current sorting criteria
6264 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6265 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6266 } else {
6267 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6268 }
6269 }
6270 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6271 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6272 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6273 $out .= '>';
6274 }
6275 if ($tooltip) {
6276 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6277 if (preg_match('/:\w+$/', $tooltip)) {
6278 $tmptooltip = explode(':', $tooltip);
6279 } else {
6280 $tmptooltip = array($tooltip);
6281 }
6282 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6283 } else {
6284 $out .= $langs->trans($name);
6285 }
6286
6287 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6288 $out .= '</a>';
6289 }
6290
6291 if (empty($thead) && $field) { // If this is a sort field
6292 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6293 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6294 $options = preg_replace('/&+/i', '&', $options);
6295 if (!preg_match('/^&/', $options)) {
6296 $options = '&'.$options;
6297 }
6298
6299 if (!$sortorder || ($field1 != $sortfield1)) {
6300 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6301 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6302 } else {
6303 if (preg_match('/^DESC/', $sortorder)) {
6304 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6305 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6306 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6307 }
6308 if (preg_match('/^ASC/', $sortorder)) {
6309 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6310 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6311 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6312 }
6313 }
6314 }
6315
6316 $tagend = '</'.$tag.'>';
6317
6318 $out = $tagstart.$sortimg.$out.$tagend;
6319
6320 return $out;
6321}
6322
6331function print_titre($title)
6332{
6333 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6334
6335 print '<div class="titre">'.$title.'</div>';
6336}
6337
6349function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6350{
6351 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6352}
6353
6367function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6368{
6369 $return = '';
6370
6371 if ($picto == 'setup') {
6372 $picto = 'generic';
6373 }
6374
6375 $return .= "\n";
6376 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6377 $return .= '<tr class="toptitle">';
6378 if ($picto) {
6379 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6380 }
6381 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6382 $return .= '<div class="titre inline-block">';
6383 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6384 $return .= '</div>';
6385 $return .= '</td>';
6386 if (dol_strlen($morehtmlcenter)) {
6387 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6388 }
6389 if (dol_strlen($morehtmlright)) {
6390 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6391 }
6392 $return .= '</tr></table>'."\n";
6393
6394 return $return;
6395}
6396
6420function 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 = '')
6421{
6422 global $conf, $langs;
6423
6424 $savlimit = $limit;
6425 $savtotalnboflines = $totalnboflines;
6426 if (is_numeric($totalnboflines)) {
6427 $totalnboflines = abs($totalnboflines);
6428 }
6429
6430 // Detect if there is a subtitle
6431 $subtitle = '';
6432 $tmparray = preg_split('/<br>/i', $title, 2);
6433 if (!empty($tmparray[1])) {
6434 $title = $tmparray[0];
6435 $subtitle = $tmparray[1];
6436 }
6437
6438 $page = (int) $page;
6439
6440 if ($picto == 'setup') {
6441 $picto = 'title_setup.png';
6442 }
6443 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6444 $picto = 'title.gif';
6445 }
6446 if ($limit < 0) {
6447 $limit = $conf->liste_limit;
6448 }
6449
6450 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6451 $nextpage = 1;
6452 } else {
6453 $nextpage = 0;
6454 }
6455 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6456
6457 print "\n";
6458 print "<!-- Begin print_barre_liste -->\n";
6459 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6460 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6461
6462 // Left
6463
6464 if ($picto && $title) {
6465 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6466 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6467 print '</td>';
6468 }
6469
6470 print '<td class="nobordernopadding valignmiddle col-title">';
6471 print '<div class="titre inline-block">';
6472 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()
6473 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6474 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6475 }
6476 print '</div>';
6477 if (!empty($subtitle)) {
6478 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6479 }
6480 print '</td>';
6481
6482 // Center
6483 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6484 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6485 }
6486
6487 // Right
6488 print '<td class="nobordernopadding valignmiddle right col-right">';
6489 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6490 if ($sortfield) {
6491 $options .= "&sortfield=".urlencode($sortfield);
6492 }
6493 if ($sortorder) {
6494 $options .= "&sortorder=".urlencode($sortorder);
6495 }
6496 // Show navigation bar
6497 $pagelist = '';
6498 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6499 if ($totalnboflines) { // If we know total nb of lines
6500 // Define nb of extra page links before and after selected page + ... + first or last
6501 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6502
6503 if ($limit > 0) {
6504 $nbpages = ceil($totalnboflines / $limit);
6505 } else {
6506 $nbpages = 1;
6507 }
6508 $cpt = ($page - $maxnbofpage);
6509 if ($cpt < 0) {
6510 $cpt = 0;
6511 }
6512
6513 if ($cpt >= 1) {
6514 if (empty($pagenavastextinput)) {
6515 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6516 if ($cpt > 2) {
6517 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6518 } elseif ($cpt == 2) {
6519 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6520 }
6521 }
6522 }
6523
6524 do {
6525 if ($pagenavastextinput) {
6526 if ($cpt == $page) {
6527 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6528 $pagelist .= '/';
6529 }
6530 } else {
6531 if ($cpt == $page) {
6532 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6533 } else {
6534 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6535 }
6536 }
6537 $cpt++;
6538 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6539
6540 if (empty($pagenavastextinput)) {
6541 if ($cpt < $nbpages) {
6542 if ($cpt < $nbpages - 2) {
6543 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6544 } elseif ($cpt == $nbpages - 2) {
6545 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6546 }
6547 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6548 }
6549 } else {
6550 //var_dump($page.' '.$cpt.' '.$nbpages);
6551 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6552 }
6553 } else {
6554 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6555 }
6556 }
6557
6558 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6559 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
6560 }
6561
6562 // js to autoselect page field on focus
6563 if ($pagenavastextinput) {
6564 print ajax_autoselect('.pageplusone');
6565 }
6566
6567 print '</td>';
6568 print '</tr>';
6569
6570 print '</table>'."\n";
6571
6572 // Center
6573 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6574 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6575 }
6576
6577 print "<!-- End title -->\n\n";
6578}
6579
6596function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6597{
6598 global $conf, $langs;
6599
6600 print '<div class="pagination"><ul>';
6601 if ($beforearrows) {
6602 print '<li class="paginationbeforearrows">';
6603 print $beforearrows;
6604 print '</li>';
6605 }
6606
6607 if (empty($hidenavigation)) {
6608 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6609 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6610 $pagesizechoices .= ',5000:5000';
6611 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6612 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6613 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6614 //$pagesizechoices .= ',2:2';
6615 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6616 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6617 }
6618
6619 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6620 print '<li class="pagination">';
6621 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.'">';
6622 print '<datalist id="limitlist">';
6623 } else {
6624 print '<li class="paginationcombolimit valignmiddle">';
6625 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")).'">';
6626 }
6627 $tmpchoice = explode(',', $pagesizechoices);
6628 $tmpkey = $limit.':'.$limit;
6629 if (!in_array($tmpkey, $tmpchoice)) {
6630 $tmpchoice[$tmpkey] = $tmpkey;
6631 }
6632 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6633 if (!in_array($tmpkey, $tmpchoice)) {
6634 $tmpchoice[$tmpkey] = $tmpkey;
6635 }
6636 asort($tmpchoice, SORT_NUMERIC);
6637 foreach ($tmpchoice as $val) {
6638 $selected = '';
6639 $tmp = explode(':', $val);
6640 $key = $tmp[0];
6641 $val = $tmp[1];
6642 if ($key != '' && $val != '') {
6643 if ((int) $key == (int) $limit) {
6644 $selected = ' selected="selected"';
6645 }
6646 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6647 }
6648 }
6649 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6650 print '</datalist>';
6651 } else {
6652 print '</select>';
6653 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
6654 //print ajax_combobox("limit");
6655 }
6656
6657 if ($conf->use_javascript_ajax) {
6658 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6659 <script>
6660 jQuery(document).ready(function () {
6661 jQuery(".selectlimit").change(function() {
6662 console.log("We change limit so we submit the form");
6663 $(this).parents(\'form:first\').submit();
6664 });
6665 });
6666 </script>
6667 ';
6668 }
6669 print '</li>';
6670 }
6671 if ($page > 0) {
6672 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>';
6673 }
6674 if ($betweenarrows) {
6675 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6676 print $betweenarrows;
6677 print '<!--</div>-->';
6678 }
6679 if ($nextpage > 0) {
6680 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>';
6681 }
6682 if ($afterarrows) {
6683 print '<li class="paginationafterarrows">';
6684 print $afterarrows;
6685 print '</li>';
6686 }
6687 }
6688 print '</ul></div>'."\n";
6689}
6690
6691
6703function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6704{
6705 $morelabel = '';
6706
6707 if (preg_match('/%/', $rate)) {
6708 $rate = str_replace('%', '', $rate);
6709 $addpercent = true;
6710 }
6711 $reg = array();
6712 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6713 $morelabel = ' ('.$reg[1].')';
6714 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6715 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6716 }
6717 if (preg_match('/\*/', $rate)) {
6718 $rate = str_replace('*', '', $rate);
6719 $info_bits |= 1;
6720 }
6721
6722 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6723 if (!preg_match('/\//', $rate)) {
6724 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6725 } else {
6726 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6727 $ret = $rate.($addpercent ? '%' : '');
6728 }
6729 if (($info_bits & 1) && $usestarfornpr >= 0) {
6730 $ret .= ' *';
6731 }
6732 $ret .= $morelabel;
6733 return $ret;
6734}
6735
6736
6752function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6753{
6754 global $langs, $conf;
6755
6756 // Clean parameters
6757 if (empty($amount)) {
6758 $amount = 0; // To have a numeric value if amount not defined or = ''
6759 }
6760 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6761 if ($rounding == -1) {
6762 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6763 }
6764 $nbdecimal = $rounding;
6765
6766 if ($outlangs === 'none') {
6767 // Use international separators
6768 $dec = '.';
6769 $thousand = '';
6770 } else {
6771 // Output separators by default (french)
6772 $dec = ',';
6773 $thousand = ' ';
6774
6775 // If $outlangs not forced, we use use language
6776 if (!($outlangs instanceof Translate)) {
6777 $outlangs = $langs;
6778 }
6779
6780 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6781 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6782 }
6783 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6784 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6785 }
6786 if ($thousand == 'None') {
6787 $thousand = '';
6788 } elseif ($thousand == 'Space') {
6789 $thousand = ' ';
6790 }
6791 }
6792 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6793
6794 //print "amount=".$amount."-";
6795 $amount = str_replace(',', '.', $amount); // should be useless
6796 //print $amount."-";
6797 $data = explode('.', $amount);
6798 $decpart = isset($data[1]) ? $data[1] : '';
6799 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6800 //print "decpart=".$decpart."<br>";
6801 $end = '';
6802
6803 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6804 if (dol_strlen($decpart) > $nbdecimal) {
6805 $nbdecimal = dol_strlen($decpart);
6806 }
6807
6808 // If nbdecimal is higher than max to show
6809 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6810 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6811 $nbdecimal = $nbdecimalmaxshown;
6812 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6813 // If output is truncated, we show ...
6814 $end = '...';
6815 }
6816 }
6817
6818 // If force rounding
6819 if ((string) $forcerounding != '-1') {
6820 if ($forcerounding === 'MU') {
6821 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6822 } elseif ($forcerounding === 'MT') {
6823 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
6824 } elseif ($forcerounding >= 0) {
6825 $nbdecimal = $forcerounding;
6826 }
6827 }
6828
6829 // Format number
6830 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6831 if ($form) {
6832 $output = preg_replace('/\s/', '&nbsp;', $output);
6833 $output = preg_replace('/\'/', '&#039;', $output);
6834 }
6835 // Add symbol of currency if requested
6836 $cursymbolbefore = $cursymbolafter = '';
6837 if ($currency_code && is_object($outlangs)) {
6838 if ($currency_code == 'auto') {
6839 $currency_code = $conf->currency;
6840 }
6841
6842 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6843 $listoflanguagesbefore = array('nl_NL');
6844 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6845 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6846 } else {
6847 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6848 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6849 }
6850 }
6851 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6852
6853 return $output;
6854}
6855
6880function price2num($amount, $rounding = '', $option = 0)
6881{
6882 global $langs, $conf;
6883
6884 // Clean parameters
6885 if (is_null($amount)) {
6886 $amount = '';
6887 }
6888
6889 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6890 // Numbers must be '1234.56'
6891 // Decimal delimiter for PHP and database SQL requests must be '.'
6892 $dec = ',';
6893 $thousand = ' ';
6894 if (is_null($langs)) { // $langs is not defined, we use english values.
6895 $dec = '.';
6896 $thousand = ',';
6897 } else {
6898 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6899 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6900 }
6901 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6902 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6903 }
6904 }
6905 if ($thousand == 'None') {
6906 $thousand = '';
6907 } elseif ($thousand == 'Space') {
6908 $thousand = ' ';
6909 }
6910 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6911
6912 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6913 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6914 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6915 if (!is_numeric($amount)) {
6916 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6917 }
6918
6919 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
6920 $amount = str_replace($thousand, '', $amount);
6921 }
6922
6923 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6924 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6925 // So if number was already a good number, it is converted into local Dolibarr setup.
6926 if (is_numeric($amount)) {
6927 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6928 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6929 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6930 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6931 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6932 }
6933 //print "QQ".$amount."<br>\n";
6934
6935 // Now make replace (the main goal of function)
6936 if ($thousand != ',' && $thousand != '.') {
6937 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6938 }
6939
6940 $amount = str_replace(' ', '', $amount); // To avoid spaces
6941 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6942 $amount = str_replace($dec, '.', $amount);
6943
6944 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6945 }
6946 //print ' XX'.$amount.' '.$rounding;
6947
6948 // Now, $amount is a real PHP float number. We make a rounding if required.
6949 if ($rounding) {
6950 $nbofdectoround = '';
6951 if ($rounding == 'MU') {
6952 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6953 } elseif ($rounding == 'MT') {
6954 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6955 } elseif ($rounding == 'MS') {
6956 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6957 } elseif ($rounding == 'CU') {
6958 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6959 } elseif ($rounding == 'CT') {
6960 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6961 } elseif (is_numeric($rounding)) {
6962 $nbofdectoround = (int) $rounding;
6963 }
6964
6965 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6966 if (dol_strlen($nbofdectoround)) {
6967 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6968 } else {
6969 return 'ErrorBadParameterProvidedToFunction';
6970 }
6971 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6972
6973 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6974 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6975 if (is_numeric($amount)) {
6976 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6977 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6978 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6979 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6980 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6981 }
6982 //print "TT".$amount.'<br>';
6983
6984 // Always make replace because each math function (like round) replace
6985 // with local values and we want a number that has a SQL string format x.y
6986 if ($thousand != ',' && $thousand != '.') {
6987 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6988 }
6989
6990 $amount = str_replace(' ', '', $amount); // To avoid spaces
6991 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6992 $amount = str_replace($dec, '.', $amount);
6993
6994 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6995 }
6996
6997 return $amount;
6998}
6999
7012function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7013{
7014 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7015
7016 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7017 $dimension *= 1000000;
7018 $unit -= 6;
7019 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7020 $dimension *= 1000;
7021 $unit -= 3;
7022 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7023 $dimension /= 1000000;
7024 $unit += 6;
7025 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7026 $dimension /= 1000;
7027 $unit += 3;
7028 }
7029 // Special case when we want output unit into pound or ounce
7030 /* TODO
7031 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7032 {
7033 $dimension = // convert dimension from standard unit into ounce or pound
7034 $unit = $forceunitoutput;
7035 }
7036 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7037 {
7038 $dimension = // convert dimension from standard unit into ounce or pound
7039 $unit = $forceunitoutput;
7040 }*/
7041
7042 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7043 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7044 $ret .= ' '.measuringUnitString(0, $type, (string) $unit, $use_short_label, $outputlangs);
7045
7046 return $ret;
7047}
7048
7049
7062function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7063{
7064 global $db, $conf, $mysoc;
7065
7066 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7067 $thirdparty_seller = $mysoc;
7068 }
7069
7070 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);
7071
7072 $vatratecleaned = $vatrate;
7073 $reg = array();
7074 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7075 $vatratecleaned = trim($reg[1]);
7076 $vatratecode = $reg[2];
7077 }
7078
7079 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7080 {
7081 return 0;
7082 }*/
7083
7084 // Some test to guess with no need to make database access
7085 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7086 if ($local == 1) {
7087 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7088 return 0;
7089 }
7090 if ($thirdparty_seller->id == $mysoc->id) {
7091 if (!$thirdparty_buyer->localtax1_assuj) {
7092 return 0;
7093 }
7094 } else {
7095 if (!$thirdparty_seller->localtax1_assuj) {
7096 return 0;
7097 }
7098 }
7099 }
7100
7101 if ($local == 2) {
7102 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7103 if (!$mysoc->localtax2_assuj) {
7104 return 0; // If main vat is 0, IRPF may be different than 0.
7105 }
7106 if ($thirdparty_seller->id == $mysoc->id) {
7107 if (!$thirdparty_buyer->localtax2_assuj) {
7108 return 0;
7109 }
7110 } else {
7111 if (!$thirdparty_seller->localtax2_assuj) {
7112 return 0;
7113 }
7114 }
7115 }
7116 } else {
7117 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7118 return 0;
7119 }
7120 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7121 return 0;
7122 }
7123 }
7124
7125 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7126 if (in_array($mysoc->country_code, array('ES'))) {
7127 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7128 }
7129
7130 // Search local taxes
7131 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7132 if ($local == 1) {
7133 if ($thirdparty_seller != $mysoc) {
7134 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7135 return $thirdparty_seller->localtax1_value;
7136 }
7137 } else { // i am the seller
7138 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7139 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7140 }
7141 }
7142 }
7143 if ($local == 2) {
7144 if ($thirdparty_seller != $mysoc) {
7145 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7146 // TODO We should also return value defined on thirdparty only if defined
7147 return $thirdparty_seller->localtax2_value;
7148 }
7149 } else { // i am the seller
7150 if (in_array($mysoc->country_code, array('ES'))) {
7151 return $thirdparty_buyer->localtax2_value;
7152 } else {
7153 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7154 }
7155 }
7156 }
7157 }
7158
7159 // By default, search value of local tax on line of common tax
7160 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7161 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7162 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7163 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7164 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7165 if (!empty($vatratecode)) {
7166 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7167 } else {
7168 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7169 }
7170
7171 $resql = $db->query($sql);
7172
7173 if ($resql) {
7174 $obj = $db->fetch_object($resql);
7175 if ($obj) {
7176 if ($local == 1) {
7177 return $obj->localtax1;
7178 } elseif ($local == 2) {
7179 return $obj->localtax2;
7180 }
7181 }
7182 }
7183
7184 return 0;
7185}
7186
7187
7196function isOnlyOneLocalTax($local)
7197{
7198 $tax = get_localtax_by_third($local);
7199
7200 $valors = explode(":", $tax);
7201
7202 if (count($valors) > 1) {
7203 return false;
7204 } else {
7205 return true;
7206 }
7207}
7208
7215function get_localtax_by_third($local)
7216{
7217 global $db, $mysoc;
7218
7219 $sql = " SELECT t.localtax".$local." as localtax";
7220 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7221 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7222 $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";
7223 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7224 $sql .= " AND t.localtax".$local."_type <> '0'";
7225 $sql .= " ORDER BY t.rowid DESC";
7226
7227 $resql = $db->query($sql);
7228 if ($resql) {
7229 $obj = $db->fetch_object($resql);
7230 if ($obj) {
7231 return $obj->localtax;
7232 } else {
7233 return '0';
7234 }
7235 }
7236
7237 return 'Error';
7238}
7239
7240
7252function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7253{
7254 global $db;
7255
7256 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7257
7258 // Search local taxes
7259 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7260 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7261 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7262 if ($firstparamisid) {
7263 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7264 } else {
7265 $vatratecleaned = $vatrate;
7266 $vatratecode = '';
7267 $reg = array();
7268 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7269 $vatratecleaned = $reg[1];
7270 $vatratecode = $reg[2];
7271 }
7272
7273 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7274 /*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 ??
7275 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7276 $sql .= " WHERE t.fk_pays = c.rowid";
7277 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7278 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7279 } else {
7280 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7281 }
7282 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7283 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7284 if ($vatratecode) {
7285 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7286 }
7287 }
7288
7289 $resql = $db->query($sql);
7290 if ($resql) {
7291 $obj = $db->fetch_object($resql);
7292 if ($obj) {
7293 return array(
7294 'rowid' => $obj->rowid,
7295 'code' => $obj->code,
7296 'rate' => $obj->rate,
7297 'localtax1' => $obj->localtax1,
7298 'localtax1_type' => $obj->localtax1_type,
7299 'localtax2' => $obj->localtax2,
7300 'localtax2_type' => $obj->localtax2_type,
7301 'npr' => $obj->npr,
7302 'accountancy_code_sell' => $obj->accountancy_code_sell,
7303 'accountancy_code_buy' => $obj->accountancy_code_buy
7304 );
7305 } else {
7306 return array();
7307 }
7308 } else {
7309 dol_print_error($db);
7310 }
7311
7312 return array();
7313}
7314
7331function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7332{
7333 global $db, $mysoc;
7334
7335 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7336
7337 // Search local taxes
7338 $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";
7339 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7340 if ($firstparamisid) {
7341 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7342 } else {
7343 $vatratecleaned = $vatrate;
7344 $vatratecode = '';
7345 $reg = array();
7346 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7347 $vatratecleaned = $reg[1];
7348 $vatratecode = $reg[2];
7349 }
7350
7351 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7352 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7353 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7354 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7355 } else {
7356 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7357 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7358 }
7359 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7360 if ($vatratecode) {
7361 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7362 }
7363 }
7364
7365 $resql = $db->query($sql);
7366 if ($resql) {
7367 $obj = $db->fetch_object($resql);
7368
7369 if ($obj) {
7370 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7371
7372 if ($local == 1) {
7373 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7374 } elseif ($local == 2) {
7375 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7376 } else {
7377 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);
7378 }
7379 }
7380 }
7381
7382 return array();
7383}
7384
7395function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7396{
7397 global $db, $mysoc;
7398
7399 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7400
7401 $ret = 0;
7402 $found = 0;
7403
7404 if ($idprod > 0) {
7405 // Load product
7406 $product = new Product($db);
7407 $product->fetch($idprod);
7408
7409 if (($mysoc->country_code == $thirdpartytouse->country_code)
7410 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7411 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7412 ) {
7413 // If country of thirdparty to consider is ours
7414 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7415 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7416 if ($result > 0) {
7417 $ret = $product->vatrate_supplier;
7418 if ($product->default_vat_code_supplier) {
7419 $ret .= ' ('.$product->default_vat_code_supplier.')';
7420 }
7421 $found = 1;
7422 }
7423 }
7424 if (!$found) {
7425 $ret = $product->tva_tx; // Default sales vat of product
7426 if ($product->default_vat_code) {
7427 $ret .= ' ('.$product->default_vat_code.')';
7428 }
7429 $found = 1;
7430 }
7431 } else {
7432 // TODO Read default product vat according to product and an other countrycode.
7433 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7434 }
7435 }
7436
7437 if (!$found) {
7438 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7439 // 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).
7440 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7441 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7442 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7443 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7444 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7445 $sql .= $db->plimit(1);
7446
7447 $resql = $db->query($sql);
7448 if ($resql) {
7449 $obj = $db->fetch_object($resql);
7450 if ($obj) {
7451 $ret = $obj->vat_rate;
7452 if ($obj->default_vat_code) {
7453 $ret .= ' ('.$obj->default_vat_code.')';
7454 }
7455 }
7456 $db->free($resql);
7457 } else {
7458 dol_print_error($db);
7459 }
7460 } else {
7461 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7462 // '1.23'
7463 // or '1.23 (CODE)'
7464 $defaulttx = '';
7465 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7466 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7467 }
7468 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7469 $defaultcode = $reg[1];
7470 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7471 }*/
7472
7473 $ret = $defaulttx;
7474 }
7475 }
7476
7477 dol_syslog("get_product_vat_for_country: ret=".$ret);
7478 return $ret;
7479}
7480
7490function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7491{
7492 global $db, $mysoc;
7493
7494 if (!class_exists('Product')) {
7495 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7496 }
7497
7498 $ret = 0;
7499 $found = 0;
7500
7501 if ($idprod > 0) {
7502 // Load product
7503 $product = new Product($db);
7504 $result = $product->fetch($idprod);
7505
7506 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7507 /* Not defined yet, so we don't use this
7508 if ($local==1) $ret=$product->localtax1_tx;
7509 elseif ($local==2) $ret=$product->localtax2_tx;
7510 $found=1;
7511 */
7512 } else {
7513 // TODO Read default product vat according to product and another countrycode.
7514 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7515 }
7516 }
7517
7518 if (!$found) {
7519 // If vat of product for the country not found or not defined, we return higher vat of country.
7520 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7521 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7522 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7523 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7524 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7525 $sql .= $db->plimit(1);
7526
7527 $resql = $db->query($sql);
7528 if ($resql) {
7529 $obj = $db->fetch_object($resql);
7530 if ($obj) {
7531 if ($local == 1) {
7532 $ret = $obj->localtax1;
7533 } elseif ($local == 2) {
7534 $ret = $obj->localtax2;
7535 }
7536 }
7537 } else {
7538 dol_print_error($db);
7539 }
7540 }
7541
7542 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7543 return $ret;
7544}
7545
7562function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7563{
7564 global $conf;
7565
7566 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7567
7568 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7569 $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;
7570
7571 $seller_country_code = $thirdparty_seller->country_code;
7572 $seller_in_cee = isInEEC($thirdparty_seller);
7573
7574 $buyer_country_code = $thirdparty_buyer->country_code;
7575 $buyer_in_cee = isInEEC($thirdparty_buyer);
7576
7577 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('SERVICES_ARE_ECOMMERCE_200238EC') ? $conf->global->SERVICES_ARE_ECOMMERCE_200238EC : ''));
7578
7579 // 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)
7580 // we use the buyer VAT.
7581 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7582 if ($seller_in_cee && $buyer_in_cee) {
7583 $isacompany = $thirdparty_buyer->isACompany();
7584 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7585 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7586 if (!isValidVATID($thirdparty_buyer)) {
7587 $isacompany = 0;
7588 }
7589 }
7590
7591 if (!$isacompany) {
7592 //print 'VATRULE 0';
7593 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7594 }
7595 }
7596 }
7597
7598 // If seller does not use VAT, default VAT is 0. End of rule.
7599 if (!$seller_use_vat) {
7600 //print 'VATRULE 1';
7601 return 0;
7602 }
7603
7604 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7605 if (($seller_country_code == $buyer_country_code)
7606 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7607 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7608 ) { // Warning ->country_code not always defined
7609 //print 'VATRULE 2';
7610 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7611
7612 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7613 // Special case for india.
7614 //print 'VATRULE 2b';
7615 $reg = array();
7616 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7617 // we must revert the C+S into I
7618 $tmpvat = str_replace("C+S", "I", $tmpvat);
7619 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7620 // we must revert the I into C+S
7621 $tmpvat = str_replace("I", "C+S", $tmpvat);
7622 }
7623 }
7624
7625 return $tmpvat;
7626 }
7627
7628 // 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.
7629 // 'VATRULE 3' - Not supported
7630
7631 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7632 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7633 if (($seller_in_cee && $buyer_in_cee)) {
7634 $isacompany = $thirdparty_buyer->isACompany();
7635 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7636 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7637 if (!isValidVATID($thirdparty_buyer)) {
7638 $isacompany = 0;
7639 }
7640 }
7641
7642 if (!$isacompany) {
7643 //print 'VATRULE 4';
7644 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7645 } else {
7646 //print 'VATRULE 5';
7647 return 0;
7648 }
7649 }
7650
7651 // 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
7652 // I don't see any use case that need this rule.
7653 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7654 $isacompany = $thirdparty_buyer->isACompany();
7655 if (!$isacompany) {
7656 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7657 //print 'VATRULE extra';
7658 }
7659 }
7660
7661 // Otherwise the VAT proposed by default=0. End of rule.
7662 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7663 //print 'VATRULE 6';
7664 return 0;
7665}
7666
7667
7678function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7679{
7680 global $db;
7681
7682 if ($idprodfournprice > 0) {
7683 if (!class_exists('ProductFournisseur')) {
7684 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7685 }
7686 $prodprice = new ProductFournisseur($db);
7687 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7688 return $prodprice->fourn_tva_npr;
7689 } elseif ($idprod > 0) {
7690 if (!class_exists('Product')) {
7691 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7692 }
7693 $prod = new Product($db);
7694 $prod->fetch($idprod);
7695 return $prod->tva_npr;
7696 }
7697
7698 return 0;
7699}
7700
7714function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7715{
7716 global $mysoc;
7717
7718 if (!is_object($thirdparty_seller)) {
7719 return -1;
7720 }
7721 if (!is_object($thirdparty_buyer)) {
7722 return -1;
7723 }
7724
7725 if ($local == 1) { // Localtax 1
7726 if ($mysoc->country_code == 'ES') {
7727 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7728 return 0;
7729 }
7730 } else {
7731 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7732 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7733 return 0;
7734 }
7735 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7736 return 0;
7737 }
7738 }
7739 } elseif ($local == 2) { //I Localtax 2
7740 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7741 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7742 return 0;
7743 }
7744 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7745 return 0;
7746 }
7747 }
7748
7749 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7750 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7751 }
7752
7753 return 0;
7754}
7755
7764function yn($yesno, $format = 1, $color = 0)
7765{
7766 global $langs;
7767
7768 $result = 'unknown';
7769 $classname = '';
7770 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7771 $result = $langs->trans('yes');
7772 if ($format == 1 || $format == 3) {
7773 $result = $langs->trans("Yes");
7774 }
7775 if ($format == 2) {
7776 $result = '<input type="checkbox" value="1" checked disabled>';
7777 }
7778 if ($format == 3) {
7779 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7780 }
7781 if ($format == 4 || !is_numeric($format)) {
7782 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
7783 }
7784
7785 $classname = 'ok';
7786 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7787 $result = $langs->trans("no");
7788 if ($format == 1 || $format == 3) {
7789 $result = $langs->trans("No");
7790 }
7791 if ($format == 2) {
7792 $result = '<input type="checkbox" value="0" disabled>';
7793 }
7794 if ($format == 3) {
7795 $result = '<input type="checkbox" value="0" disabled> '.$result;
7796 }
7797 if ($format == 4 || !is_numeric($format)) {
7798 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
7799 }
7800
7801 if ($color == 2) {
7802 $classname = 'ok';
7803 } else {
7804 $classname = 'error';
7805 }
7806 }
7807 if ($color) {
7808 return '<span class="'.$classname.'">'.$result.'</span>';
7809 }
7810 return $result;
7811}
7812
7831function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7832{
7833 if (empty($modulepart) && is_object($object)) {
7834 if (!empty($object->module)) {
7835 $modulepart = $object->module;
7836 } elseif (!empty($object->element)) {
7837 $modulepart = $object->element;
7838 }
7839 }
7840
7841 $path = '';
7842
7843 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7844 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7845 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7846 $arrayforoldpath['product'] = 2;
7847 }
7848
7849 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7850 $level = $arrayforoldpath[$modulepart];
7851 }
7852
7853 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7854 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7855 if (empty($num) && is_object($object)) {
7856 $num = $object->id;
7857 }
7858 if (empty($alpha)) {
7859 $num = preg_replace('/([^0-9])/i', '', $num);
7860 } else {
7861 $num = preg_replace('/^.*\-/i', '', $num);
7862 }
7863 $num = substr("000".$num, -$level);
7864 if ($level == 1) {
7865 $path = substr($num, 0, 1);
7866 }
7867 if ($level == 2) {
7868 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7869 }
7870 if ($level == 3) {
7871 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7872 }
7873 } else {
7874 // We will enhance here a common way of forging path for document storage.
7875 // In a future, we may distribute directories on several levels depending on setup and object.
7876 // Here, $object->id, $object->ref and $modulepart are required.
7877 //var_dump($modulepart);
7878 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7879 }
7880
7881 if (empty($withoutslash) && !empty($path)) {
7882 $path .= '/';
7883 }
7884
7885 return $path;
7886}
7887
7896function dol_mkdir($dir, $dataroot = '', $newmask = '')
7897{
7898 global $conf;
7899
7900 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7901
7902 $dir_osencoded = dol_osencode($dir);
7903 if (@is_dir($dir_osencoded)) {
7904 return 0;
7905 }
7906
7907 $nberr = 0;
7908 $nbcreated = 0;
7909
7910 $ccdir = '';
7911 if (!empty($dataroot)) {
7912 // Remove data root from loop
7913 $dir = str_replace($dataroot.'/', '', $dir);
7914 $ccdir = $dataroot.'/';
7915 }
7916
7917 $cdir = explode("/", $dir);
7918 $num = count($cdir);
7919 for ($i = 0; $i < $num; $i++) {
7920 if ($i > 0) {
7921 $ccdir .= '/'.$cdir[$i];
7922 } else {
7923 $ccdir .= $cdir[$i];
7924 }
7925 $regs = array();
7926 if (preg_match("/^.:$/", $ccdir, $regs)) {
7927 continue; // If the Windows path is incomplete, continue with next directory
7928 }
7929
7930 // Attention, is_dir() can fail event if the directory exists
7931 // (i.e. according the open_basedir configuration)
7932 if ($ccdir) {
7933 $ccdir_osencoded = dol_osencode($ccdir);
7934 if (!@is_dir($ccdir_osencoded)) {
7935 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7936
7937 umask(0);
7938 $dirmaskdec = octdec((string) $newmask);
7939 if (empty($newmask)) {
7940 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7941 }
7942 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7943 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7944 // If the is_dir has returned a false information, we arrive here
7945 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7946 $nberr++;
7947 } else {
7948 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7949 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7950 $nbcreated++;
7951 }
7952 } else {
7953 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7954 }
7955 }
7956 }
7957 return ($nberr ? -$nberr : $nbcreated);
7958}
7959
7960
7968function dolChmod($filepath, $newmask = '')
7969{
7970 global $conf;
7971
7972 if (!empty($newmask)) {
7973 @chmod($filepath, octdec($newmask));
7974 } elseif (getDolGlobalString('MAIN_UMASK')) {
7975 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7976 }
7977}
7978
7979
7985function picto_required()
7986{
7987 return '<span class="fieldrequired">*</span>';
7988}
7989
7990
8007function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8008{
8009 if (is_null($stringtoclean)) {
8010 return '';
8011 }
8012
8013 if ($removelinefeed == 2) {
8014 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8015 }
8016 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8017
8018 // 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)
8019 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8020
8021 $temp = str_replace('< ', '__ltspace__', $temp);
8022 $temp = str_replace('<:', '__lttwopoints__', $temp);
8023
8024 if ($strip_tags) {
8025 $temp = strip_tags($temp);
8026 } else {
8027 // 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).
8028 $pattern = "/<[^<>]+>/";
8029 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8030 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8031 // pass 2 - $temp after pass 2: 0000-021
8032 $tempbis = $temp;
8033 do {
8034 $temp = $tempbis;
8035 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8036 $tempbis = preg_replace($pattern, '', $tempbis);
8037 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8038 } while ($tempbis != $temp);
8039
8040 $temp = $tempbis;
8041
8042 // 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).
8043 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8044 }
8045
8046 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8047
8048 // Remove also carriage returns
8049 if ($removelinefeed == 1) {
8050 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8051 }
8052
8053 // And double quotes
8054 if ($removedoublespaces) {
8055 while (strpos($temp, " ")) {
8056 $temp = str_replace(" ", " ", $temp);
8057 }
8058 }
8059
8060 $temp = str_replace('__ltspace__', '< ', $temp);
8061 $temp = str_replace('__lttwopoints__', '<:', $temp);
8062
8063 return trim($temp);
8064}
8065
8083function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0)
8084{
8085 if (empty($allowed_tags)) {
8086 $allowed_tags = array(
8087 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8088 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8089 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8090 );
8091 }
8092 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8093 if ($allowiframe) {
8094 if (!in_array('iframe', $allowed_tags)) {
8095 $allowed_tags[] = "iframe";
8096 }
8097 }
8098 if ($allowlink) {
8099 if (!in_array('link', $allowed_tags)) {
8100 $allowed_tags[] = "link";
8101 }
8102 }
8103 if ($allowscript) {
8104 if (!in_array('script', $allowed_tags)) {
8105 $allowed_tags[] = "script";
8106 }
8107 }
8108 if ($allowstyle) {
8109 if (!in_array('style', $allowed_tags)) {
8110 $allowed_tags[] = "style";
8111 }
8112 }
8113
8114 $allowed_tags_string = implode("><", $allowed_tags);
8115 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8116
8117 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8118
8119 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8120
8121 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8122 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8123
8124 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8125 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8126
8127 // Remove all HTML tags
8128 $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
8129
8130 if ($cleanalsosomestyles) { // Clean for remaining html tags
8131 $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
8132 }
8133 if ($removeclassattribute) { // Clean for remaining html tags
8134 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8135 }
8136
8137 // Remove 'javascript:' that we should not find into a text with
8138 // 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)).
8139 if ($cleanalsojavascript) {
8140 $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);
8141 }
8142
8143 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8144
8145 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8146
8147
8148 return $temp;
8149}
8150
8151
8164function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8165{
8166 if (is_null($allowed_attributes)) {
8167 $allowed_attributes = array(
8168 "allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width",
8169 // HTML5
8170 "header", "footer", "nav", "section", "menu", "menuitem"
8171 );
8172 }
8173
8174 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8175 $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
8176
8177 // Warning: loadHTML does not support HTML5 on old libxml versions.
8178 $dom = new DOMDocument('', 'UTF-8');
8179 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8180 $savwarning = error_reporting();
8181 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8182 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8183 error_reporting($savwarning);
8184
8185 if ($dom instanceof DOMDocument) {
8186 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8187 $el = $els->item($i);
8188 if (!$el instanceof DOMElement) {
8189 continue;
8190 }
8191 $attrs = $el->attributes;
8192 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8193 //var_dump($attrs->item($ii));
8194 if (!empty($attrs->item($ii)->name)) {
8195 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8196 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8197 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8198 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8199 // If attribute is 'style'
8200 $valuetoclean = $attrs->item($ii)->value;
8201
8202 if (isset($valuetoclean)) {
8203 do {
8204 $oldvaluetoclean = $valuetoclean;
8205 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8206 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8207 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8208 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8209 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8210 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8211 }
8212
8213 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8214 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8215 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8216 } while ($oldvaluetoclean != $valuetoclean);
8217 }
8218
8219 $attrs->item($ii)->value = $valuetoclean;
8220 }
8221 }
8222 }
8223 }
8224 }
8225
8226 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8227 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8228
8229 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8230 $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
8231 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
8232 return trim($return);
8233 } else {
8234 return $stringtoclean;
8235 }
8236}
8237
8249function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8250{
8251 $temp = $stringtoclean;
8252 foreach ($disallowed_tags as $tagtoremove) {
8253 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8254 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8255 }
8256
8257 if ($cleanalsosomestyles) {
8258 $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
8259 }
8260
8261 return $temp;
8262}
8263
8264
8274function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8275{
8276 if ($nboflines == 1) {
8277 if (dol_textishtml($text)) {
8278 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8279 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8280 } else {
8281 if (isset($text)) {
8282 $firstline = preg_replace('/[\n\r].*/', '', $text);
8283 } else {
8284 $firstline = '';
8285 }
8286 }
8287 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8288 } else {
8289 $ishtml = 0;
8290 if (dol_textishtml($text)) {
8291 $text = preg_replace('/\n/', '', $text);
8292 $ishtml = 1;
8293 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8294 } else {
8295 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8296 }
8297
8298 $text = strtr($text, $repTable);
8299 if ($charset == 'UTF-8') {
8300 $pattern = '/(<br[^>]*>)/Uu';
8301 } else {
8302 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8303 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8304 }
8305 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8306
8307 $firstline = '';
8308 $i = 0;
8309 $countline = 0;
8310 $lastaddediscontent = 1;
8311 while ($countline < $nboflines && isset($a[$i])) {
8312 if (preg_match('/<br[^>]*>/', $a[$i])) {
8313 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8314 $firstline .= ($ishtml ? "<br>\n" : "\n");
8315 // Is it a br for a new line of after a printed line ?
8316 if (!$lastaddediscontent) {
8317 $countline++;
8318 }
8319 $lastaddediscontent = 0;
8320 }
8321 } else {
8322 $firstline .= $a[$i];
8323 $lastaddediscontent = 1;
8324 $countline++;
8325 }
8326 $i++;
8327 }
8328
8329 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8330 //unset($a);
8331 $ret = $firstline.($adddots ? '...' : '');
8332 //exit;
8333 return $ret;
8334 }
8335}
8336
8337
8349function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8350{
8351 if (is_null($stringtoencode)) {
8352 return '';
8353 }
8354
8355 if (!$nl2brmode) {
8356 return nl2br($stringtoencode, $forxml);
8357 } else {
8358 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8359 return $ret;
8360 }
8361}
8362
8372function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8373{
8374 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8375 // TODO using sandbox on inline html content is not possible yet with current browsers
8376 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8377 //$s .= $stringtoencode;
8378 //$s .= '</body></html></iframe>';
8379 return $stringtoencode;
8380 } else {
8381 $out = $stringtoencode;
8382
8383 // First clean HTML content
8384 do {
8385 $oldstringtoclean = $out;
8386
8387 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8388 try {
8389 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8390 if (LIBXML_VERSION < 20900) {
8391 // Avoid load of external entities (security problem).
8392 // Required only if LIBXML_VERSION < 20900
8393 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8394 libxml_disable_entity_loader(true);
8395 }
8396
8397 $dom = new DOMDocument();
8398 // Add a trick to solve pb with text without parent tag
8399 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8400 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8401
8402 if (dol_textishtml($out)) {
8403 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.$out.'</div>';
8404 } else {
8405 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.dol_nl2br($out).'</div>';
8406 }
8407
8408 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8409 $out = trim($dom->saveHTML());
8410
8411 // Remove the trick added to solve pb with text without parent tag
8412 $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8413 $out = preg_replace('/<\/div>$/', '', $out);
8414 } catch (Exception $e) {
8415 // If error, invalid HTML string with no way to clean it
8416 //print $e->getMessage();
8417 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8418 }
8419 }
8420
8421 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript'))) {
8422 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8423 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8424 try {
8425 // Try cleaning using tidy
8426 if (extension_loaded('tidy') && class_exists("tidy")) {
8427 //print "aaa".$out."\n";
8428
8429 // See options at https://tidy.sourceforge.net/docs/quickref.html
8430 $config = array(
8431 'clean' => false,
8432 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8433 'doctype' => 'strict',
8434 'show-body-only' => true,
8435 "indent-attributes" => false,
8436 "vertical-space" => false,
8437 //'ident' => false, // Not always supported
8438 "wrap" => 0
8439 // HTML5 tags
8440 //'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',
8441 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8442 //'new-empty-tags' => 'command embed keygen source track wbr',
8443 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8444 );
8445
8446 // Tidy
8447 $tidy = new tidy();
8448 $out = $tidy->repairString($out, $config, 'utf8');
8449
8450 //print "xxx".$out;exit;
8451 }
8452 } catch (Exception $e) {
8453 // If error, invalid HTML string with no way to clean it
8454 //print $e->getMessage();
8455 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8456 }
8457 }
8458
8459 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8460 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8461
8462 // Clean some html entities that are useless so text is cleaner
8463 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8464
8465 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8466 // encoded using text entities) so we can then exclude all numeric entities.
8467 $out = preg_replace('/&#39;/i', '&apos;', $out);
8468
8469 // 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).
8470 // 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
8471 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8472 $out = preg_replace_callback(
8473 '/&#(x?[0-9][0-9a-f]+;?)/i',
8478 static function ($m) {
8479 return realCharForNumericEntities($m);
8480 },
8481 $out
8482 );
8483
8484 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8485 $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'.
8486
8487 // Keep only some html tags and remove also some 'javascript:' strings
8488 if ($check == 'restricthtmlallowlinkscript') {
8489 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1);
8490 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8491 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8492 } else {
8493 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8494 }
8495
8496 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8497 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8499 }
8500
8501 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8502 $out = preg_replace('/&apos;/i', "&#39;", $out);
8503
8504 // Now remove js
8505 // 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
8506 $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)>
8507 $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);
8508 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8509 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8510 $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);
8511 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8512 // More not into the previous list
8513 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8514 } while ($oldstringtoclean != $out);
8515
8516 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8517 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8518 // 'url(' to avoid inline style like background: url(http...
8519 // '<link' to avoid <link href="http...">
8520 $reg = array();
8521 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8522 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8523 $nblinks = count($reg[0]);
8524 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8525 $out = 'ErrorTooManyLinksIntoHTMLString';
8526 }
8527
8528 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8529 if ($nblinks > 0) {
8530 $out = 'ErrorHTMLLinksNotAllowed';
8531 }
8532 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8533 $nblinks = 0;
8534 // Loop on each url in src= and url(
8535 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8536
8537 $matches = array();
8538 if (preg_match_all($pattern, $out, $matches)) {
8539 // URLs are into $matches[1]
8540 $urls = $matches[1];
8541
8542 // Affiche les URLs
8543 foreach ($urls as $url) {
8544 $nblinks++;
8545 echo "Found url = ".$url . "\n";
8546 }
8547 if ($nblinks > 0) {
8548 $out = 'ErrorHTMLExternalLinksNotAllowed';
8549 }
8550 }
8551 }
8552
8553 return $out;
8554 }
8555}
8556
8577function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8578{
8579 if (is_null($stringtoencode)) {
8580 return '';
8581 }
8582
8583 $newstring = $stringtoencode;
8584 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8585 $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.
8586 if ($removelasteolbr) {
8587 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8588 }
8589 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8590 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8591 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8592 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8593 } else {
8594 if ($removelasteolbr) {
8595 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8596 }
8597 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8598 }
8599 // Other substitutions that htmlentities does not do
8600 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8601 return $newstring;
8602}
8603
8611function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8612{
8613 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8614 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8615 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8616 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8617 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8618 return $ret;
8619}
8620
8627function dol_htmlcleanlastbr($stringtodecode)
8628{
8629 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8630 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8631 return $ret;
8632}
8633
8643function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8644{
8645 $newstring = $a;
8646 if ($keepsomeentities) {
8647 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8648 }
8649 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8650 if ($keepsomeentities) {
8651 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8652 }
8653 return $newstring;
8654}
8655
8667function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8668{
8669 return htmlentities($string, $flags, $encoding, $double_encode);
8670}
8671
8683function dol_string_is_good_iso($s, $clean = 0)
8684{
8685 $len = dol_strlen($s);
8686 $out = '';
8687 $ok = 1;
8688 for ($scursor = 0; $scursor < $len; $scursor++) {
8689 $ordchar = ord($s[$scursor]);
8690 //print $scursor.'-'.$ordchar.'<br>';
8691 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8692 $ok = 0;
8693 break;
8694 } elseif ($ordchar > 126 && $ordchar < 160) {
8695 $ok = 0;
8696 break;
8697 } elseif ($clean) {
8698 $out .= $s[$scursor];
8699 }
8700 }
8701 if ($clean) {
8702 return $out;
8703 }
8704 return $ok;
8705}
8706
8715function dol_nboflines($s, $maxchar = 0)
8716{
8717 if ($s == '') {
8718 return 0;
8719 }
8720 $arraystring = explode("\n", $s);
8721 $nb = count($arraystring);
8722
8723 return $nb;
8724}
8725
8726
8736function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8737{
8738 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8739 if (dol_textishtml($text)) {
8740 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8741 }
8742
8743 $text = strtr($text, $repTable);
8744 if ($charset == 'UTF-8') {
8745 $pattern = '/(<br[^>]*>)/Uu';
8746 } else {
8747 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8748 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8749 }
8750 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8751
8752 $nblines = (int) floor((count($a) + 1) / 2);
8753 // count possible auto line breaks
8754 if ($maxlinesize) {
8755 foreach ($a as $line) {
8756 if (dol_strlen($line) > $maxlinesize) {
8757 //$line_dec = html_entity_decode(strip_tags($line));
8758 $line_dec = html_entity_decode($line);
8759 if (dol_strlen($line_dec) > $maxlinesize) {
8760 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8761 $nblines += substr_count($line_dec, '\n');
8762 }
8763 }
8764 }
8765 }
8766
8767 unset($a);
8768 return $nblines;
8769}
8770
8779function dol_textishtml($msg, $option = 0)
8780{
8781 if (is_null($msg)) {
8782 return false;
8783 }
8784
8785 if ($option == 1) {
8786 if (preg_match('/<(html|link|script)/i', $msg)) {
8787 return true;
8788 } elseif (preg_match('/<body/i', $msg)) {
8789 return true;
8790 } elseif (preg_match('/<\/textarea/i', $msg)) {
8791 return true;
8792 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8793 return true;
8794 } elseif (preg_match('/<br/i', $msg)) {
8795 return true;
8796 }
8797 return false;
8798 } else {
8799 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8800 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8801 if (preg_match('/<(html|link|script|body)/i', $msg)) {
8802 return true;
8803 } elseif (preg_match('/<\/textarea/i', $msg)) {
8804 return true;
8805 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8806 return true;
8807 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8808 return true;
8809 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8810 return true;
8811 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8812 return true;
8813 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8814 return true; // must accept <img src="http://example.com/aaa.png" />
8815 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8816 return true; // must accept <a href="http://example.com/aaa.png" />
8817 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8818 return true;
8819 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8820 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8821 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8822 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8823 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8824 }
8825
8826 return false;
8827 }
8828}
8829
8844function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8845{
8846 if (!empty($invert)) {
8847 $tmp = $text1;
8848 $text1 = $text2;
8849 $text2 = $tmp;
8850 }
8851
8852 $ret = '';
8853 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8854 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8855 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8856 return $ret;
8857}
8858
8859
8860
8874function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8875{
8876 global $db, $conf, $mysoc, $user, $extrafields;
8877
8878 $substitutionarray = array();
8879
8880 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
8881 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8882 // this will include signature content first and then replace var found into content of signature
8883 //var_dump($onlykey);
8884 $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()
8885 $usersignature = $user->signature;
8886 $substitutionarray = array_merge($substitutionarray, array(
8887 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8888 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8889 ));
8890
8891 if (is_object($user) && ($user instanceof User)) {
8892 $substitutionarray = array_merge($substitutionarray, array(
8893 '__USER_ID__' => (string) $user->id,
8894 '__USER_LOGIN__' => (string) $user->login,
8895 '__USER_EMAIL__' => (string) $user->email,
8896 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8897 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8898 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8899 '__USER_FAX__' => (string) $user->office_fax,
8900 '__USER_LASTNAME__' => (string) $user->lastname,
8901 '__USER_FIRSTNAME__' => (string) $user->firstname,
8902 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8903 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8904 '__USER_JOB__' => (string) $user->job,
8905 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8906 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8907 ));
8908 }
8909 }
8910 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8911 $substitutionarray = array_merge($substitutionarray, array(
8912 '__MYCOMPANY_NAME__' => $mysoc->name,
8913 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8914 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8915 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8916 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8917 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8918 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8919 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8920 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8921 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8922 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8923 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8924 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8925 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8926 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8927 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8928 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8929 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8930 '__MYCOMPANY_TOWN__' => $mysoc->town,
8931 '__MYCOMPANY_STATE__' => $mysoc->state,
8932 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
8933 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
8934 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
8935 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
8936 ));
8937 }
8938
8939 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
8940 if ($onlykey) {
8941 $substitutionarray['__ID__'] = '__ID__';
8942 $substitutionarray['__REF__'] = '__REF__';
8943 $substitutionarray['__NEWREF__'] = '__NEWREF__';
8944 $substitutionarray['__LABEL__'] = '__LABEL__';
8945 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
8946 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
8947 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
8948 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
8949 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
8950
8951 if (isModEnabled("societe")) { // Most objects are concerned
8952 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
8953 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
8954 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
8955 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
8956 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
8957 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
8958 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
8959 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
8960 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
8961 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
8962 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
8963 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
8964 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
8965 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
8966 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
8967 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
8968 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
8969 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
8970 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
8971 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
8972 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
8973 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
8974 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
8975 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
8976 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
8977 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
8978 }
8979 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
8980 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
8981 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
8982 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
8983 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
8984 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
8985 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
8986 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
8987 }
8988 // add substitution variables for ticket
8989 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
8990 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
8991 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
8992 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
8993 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
8994 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
8995 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
8996 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
8997 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
8998 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
8999 }
9000
9001 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9002 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9003 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9004 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9005 }
9006 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9007 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9008 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9009 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9010 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9011 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9012 }
9013 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9014 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9015 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9016 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9017 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9018 }
9019 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9020 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9021 }
9022 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9023 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9024 }
9025 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9026 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9027 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9028 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9029 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9030 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9031 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9032
9033 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9034 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9035 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9036 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9037 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9038
9039 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9040 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9041 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9042 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9043 }
9044 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9045 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9046 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9047 }
9048 } else {
9049 '@phan-var-force Adherent|Delivery $object';
9050 $substitutionarray['__ID__'] = $object->id;
9051 $substitutionarray['__REF__'] = $object->ref;
9052 $substitutionarray['__NEWREF__'] = $object->newref;
9053 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9054 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9055 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9056 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9057 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9058 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9059 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9060 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9061 $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', false, $outputlangs) : '');
9062 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%d") : '');
9063 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%A") : '');
9064 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%m") : '');
9065 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%b") : '');
9066 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%Y") : '');
9067 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%H") : '');
9068 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%M") : '');
9069 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%S") : '');
9070
9071 // For backward compatibility (deprecated)
9072 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9073 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9074 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->delivery_date) ? dol_print_date($object->delivery_date, 'day', false, $outputlangs) : '');
9075 $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 : '')) : '');
9076 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9077
9078 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9079 '@phan-var-force Adherent $object';
9080 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9081
9082 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9083 if (method_exists($object, 'getCivilityLabel')) {
9084 $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
9085 }
9086 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9087 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9088 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9089 if (method_exists($object, 'getFullName')) {
9090 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9091 }
9092 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9093 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9094 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9095 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9096 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9097 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9098 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9099 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9100 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9101 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9102 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9103 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9104 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9105 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9106 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9107 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9108
9109 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9110 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9111 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9112 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9113 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9114 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9115 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9116 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9117 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9118 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9119 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9120 }
9121
9122 if (is_object($object) && $object->element == 'societe') {
9123 '@phan-var-force Societe $object';
9124 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
9125 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
9126 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
9127 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
9128 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
9129 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
9130 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
9131 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
9132 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
9133 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
9134 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
9135 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
9136 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object) ? $object->state : '');
9137 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
9138 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
9139 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
9140 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
9141 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
9142 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
9143 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
9144 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
9145 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
9146 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
9147 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
9148 } elseif (is_object($object->thirdparty)) {
9149 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
9150 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9151 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9152 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9153 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9154 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9155 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9156 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9157 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9158 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9159 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9160 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9161 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object->thirdparty) ? $object->thirdparty->state : '');
9162 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9163 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9164 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9165 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9166 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9167 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9168 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9169 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9170 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9171 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9172 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9173 }
9174
9175 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9176 '@phan-var-force RecruitmentCandidature $object';
9177 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9178 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9179 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9180 }
9181 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9182 '@phan-var-force ConferenceOrBoothAttendee $object';
9183 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9184 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9185 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9186 }
9187
9188 if (is_object($object) && $object->element == 'project') {
9189 '@phan-var-force Project $object';
9190 $substitutionarray['__PROJECT_ID__'] = $object->id;
9191 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9192 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9193 } elseif (is_object($object)) {
9194 $project = null;
9195 if (!empty($object->project)) {
9196 $project = $object->project;
9197 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9198 $project = $object->projet;
9199 }
9200 if (!is_null($project) && is_object($project)) {
9201 $substitutionarray['__PROJECT_ID__'] = $project->id;
9202 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9203 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9204 } else {
9205 // can substitute variables for project : uses lazy load in "make_substitutions" method
9206 $project_id = 0;
9207 if (!empty($object->fk_project) && $object->fk_project > 0) {
9208 $project_id = $object->fk_project;
9209 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9210 $project_id = $object->fk_project;
9211 }
9212 if ($project_id > 0) {
9213 // path:class:method:id
9214 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9215 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9216 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9217 }
9218 }
9219 }
9220
9221 if (is_object($object) && $object->element == 'facture') {
9222 '@phan-var-force Facture $object';
9223 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9224 }
9225 if (is_object($object) && $object->element == 'shipping') {
9226 '@phan-var-force Expedition $object';
9227 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9228 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9229 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9230 }
9231 if (is_object($object) && $object->element == 'reception') {
9232 '@phan-var-force Reception $object';
9233 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9234 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9235 }
9236
9237 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9238 '@phan-var-force Contrat $object';
9239 $dateplannedstart = '';
9240 $datenextexpiration = '';
9241 foreach ($object->lines as $line) {
9242 if ($line->date_start > $dateplannedstart) {
9243 $dateplannedstart = $line->date_start;
9244 }
9245 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9246 $datenextexpiration = $line->date_end;
9247 }
9248 }
9249 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9250 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9251 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9252
9253 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9254 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9255 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9256 }
9257 // add substitution variables for ticket
9258 if (is_object($object) && $object->element == 'ticket') {
9259 '@phan-var-force Ticket $object';
9260 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9261 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9262 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9263 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9264 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9265 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9266 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9267 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9268 $userstat = new User($db);
9269 if ($object->fk_user_assign > 0) {
9270 $userstat->fetch($object->fk_user_assign);
9271 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9272 }
9273
9274 if ($object->fk_user_create > 0) {
9275 $userstat->fetch($object->fk_user_create);
9276 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9277 }
9278 }
9279
9280 // Create dynamic tags for __EXTRAFIELD_FIELD__
9281 if ($object->table_element && $object->id > 0) {
9282 if (!is_object($extrafields)) {
9283 $extrafields = new ExtraFields($db);
9284 }
9285 $extrafields->fetch_name_optionals_label($object->table_element, true);
9286
9287 if ($object->fetch_optionals() > 0) {
9288 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9289 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9290 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9291 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9292 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9293 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9294 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9295 $datetime = $object->array_options['options_'.$key];
9296 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9297 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9298 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9299 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9300 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9301 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9302 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9303 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9304 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9305 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9306 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9307 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9308 }
9309 }
9310 }
9311 }
9312 }
9313
9314 // Complete substitution array with the url to make online payment
9315 if (empty($substitutionarray['__REF__'])) {
9316 $paymenturl = '';
9317 } else {
9318 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9319 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9320 $outputlangs->loadLangs(array('paypal', 'other'));
9321
9322 $amounttouse = 0;
9323 $typeforonlinepayment = 'free';
9324 if (is_object($object) && $object->element == 'commande') {
9325 $typeforonlinepayment = 'order';
9326 }
9327 if (is_object($object) && $object->element == 'facture') {
9328 $typeforonlinepayment = 'invoice';
9329 }
9330 if (is_object($object) && $object->element == 'member') {
9331 $typeforonlinepayment = 'member';
9332 if (!empty($object->last_subscription_amount)) {
9333 $amounttouse = $object->last_subscription_amount;
9334 }
9335 }
9336 if (is_object($object) && $object->element == 'contrat') {
9337 $typeforonlinepayment = 'contract';
9338 }
9339 if (is_object($object) && $object->element == 'fichinter') {
9340 $typeforonlinepayment = 'ficheinter';
9341 }
9342
9343 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9344 $paymenturl = $url;
9345 }
9346
9347 if ($object->id > 0) {
9348 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9349 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9350
9351 // Show structured communication
9352 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9353 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9354 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9355 }
9356
9357 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9358 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9359 } else {
9360 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9361 }
9362 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9363 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9364 } else {
9365 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9366 }
9367 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9368 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9369 } else {
9370 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9371 }
9372 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9373 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9374 } else {
9375 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9376 }
9377 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9378 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9379 } else {
9380 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9381 }
9382 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9383 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9384 } else {
9385 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9386 }
9387
9388 if (is_object($object) && $object->element == 'propal') {
9389 '@phan-var-force Propal $object';
9390 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9391 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9392 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9393 }
9394 if (is_object($object) && $object->element == 'commande') {
9395 '@phan-var-force Commande $object';
9396 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9397 }
9398 if (is_object($object) && $object->element == 'facture') {
9399 '@phan-var-force Facture $object';
9400 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9401 }
9402 if (is_object($object) && $object->element == 'contrat') {
9403 '@phan-var-force Contrat $object';
9404 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9405 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9406 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9407 }
9408 if (is_object($object) && $object->element == 'fichinter') {
9409 '@phan-var-force Fichinter $object';
9410 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9411 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9412 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9413 }
9414 if (is_object($object) && $object->element == 'supplier_proposal') {
9415 '@phan-var-force SupplierProposal $object';
9416 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9417 }
9418 if (is_object($object) && $object->element == 'invoice_supplier') {
9419 '@phan-var-force FactureFournisseur $object';
9420 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9421 }
9422 if (is_object($object) && $object->element == 'shipping') {
9423 '@phan-var-force Expedition $object';
9424 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9425 }
9426 }
9427
9428 if (is_object($object) && $object->element == 'action') {
9429 '@phan-var-force ActionComm $object';
9430 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9431 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9432 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9433 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9434 }
9435 }
9436 }
9437 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9438 '@phan-var-force Facture|FactureRec $object';
9439 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9440
9441 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9442 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9443 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9444 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9445
9446 $already_payed_all = 0;
9447 if (is_object($object) && ($object instanceof Facture)) {
9448 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9449 }
9450
9451 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9452 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9453 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9454
9455 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9456 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9457 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9458
9459 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9460
9461 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9462 $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)) : '';
9463 $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)) : '';
9464
9465 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9466 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9467 }
9468 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9469 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9470 }
9471
9472 // Amount keys formatted in a currency
9473 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9474 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9475 $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) : '';
9476 $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)) : '';
9477 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9478 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9479 }
9480 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9481 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9482 }
9483 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9484 if ($onlykey != 2) {
9485 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9486 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9487 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9488 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9489 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9490 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9491 }
9492 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9493 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9494 }
9495 }
9496
9497 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9498 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9499 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9500 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9501 // TODO Add other keys for foreign multicurrency
9502
9503 // For backward compatibility
9504 if ($onlykey != 2) {
9505 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9506 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9507 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9508 }
9509 }
9510
9511
9512 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9513 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9514
9515 $now = dol_now();
9516
9517 $tmp = dol_getdate($now, true);
9518 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9519 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9520 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9521 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9522
9523 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9524
9525 $substitutionarray = array_merge($substitutionarray, array(
9526 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9527 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9528 '__DAY__' => (string) $tmp['mday'],
9529 '__DAY_TEXT__' => $daytext, // Monday
9530 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9531 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9532 '__MONTH__' => (string) $tmp['mon'],
9533 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9534 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9535 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9536 '__YEAR__' => (string) $tmp['year'],
9537 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9538 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9539 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9540 '__NEXT_DAY__' => (string) $tmp4['day'],
9541 '__NEXT_MONTH__' => (string) $tmp5['month'],
9542 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9543 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9544 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9545 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9546 ));
9547 }
9548
9549 if (isModEnabled('multicompany')) {
9550 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9551 }
9552 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9553 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9554 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9555 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9556 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9557 }
9558
9559 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9560
9561 return $substitutionarray;
9562}
9563
9580function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9581{
9582 global $conf, $db, $langs;
9583
9584 if (!is_array($substitutionarray)) {
9585 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9586 }
9587
9588 if (empty($outputlangs)) {
9589 $outputlangs = $langs;
9590 }
9591
9592 // Is initial text HTML or simple text ?
9593 $msgishtml = 0;
9594 if (dol_textishtml($text, 1)) {
9595 $msgishtml = 1;
9596 }
9597
9598 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9599 if (is_object($outputlangs)) {
9600 $reg = array();
9601 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9602 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9603 $tmp = explode('|', $reg[1]);
9604 if (!empty($tmp[1])) {
9605 $outputlangs->load($tmp[1]);
9606 }
9607
9608 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9609
9610 if (empty($converttextinhtmlifnecessary)) {
9611 // convert $newval into HTML is necessary
9612 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9613 } else {
9614 if (! $msgishtml) {
9615 $valueishtml = dol_textishtml($value, 1);
9616 //var_dump("valueishtml=".$valueishtml);
9617
9618 if ($valueishtml) {
9619 $text = dol_htmlentitiesbr($text);
9620 $msgishtml = 1;
9621 }
9622 } else {
9623 $value = dol_nl2br("$value");
9624 }
9625
9626 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9627 }
9628 }
9629 }
9630
9631 // Make substitution for constant keys.
9632 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9633 $reg = array();
9634 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9635 $keyfound = $reg[1];
9636 if (isASecretKey($keyfound)) {
9637 $value = '*****forbidden*****';
9638 } else {
9639 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9640 }
9641
9642 if (empty($converttextinhtmlifnecessary)) {
9643 // convert $newval into HTML is necessary
9644 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9645 } else {
9646 if (! $msgishtml) {
9647 $valueishtml = dol_textishtml($value, 1);
9648
9649 if ($valueishtml) {
9650 $text = dol_htmlentitiesbr($text);
9651 $msgishtml = 1;
9652 }
9653 } else {
9654 $value = dol_nl2br("$value");
9655 }
9656
9657 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9658 }
9659 }
9660
9661 // Make substitution for array $substitutionarray
9662 foreach ($substitutionarray as $key => $value) {
9663 if (!isset($value)) {
9664 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9665 }
9666
9667 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9668 $value = ''; // Protection
9669 }
9670
9671 if (empty($converttextinhtmlifnecessary)) {
9672 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9673 } else {
9674 if (! $msgishtml) {
9675 $valueishtml = dol_textishtml($value, 1);
9676
9677 if ($valueishtml) {
9678 $text = dol_htmlentitiesbr($text);
9679 $msgishtml = 1;
9680 }
9681 } else {
9682 $value = dol_nl2br("$value");
9683 }
9684 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9685 }
9686 }
9687
9688 // TODO Implement the lazyload substitution
9689 /*
9690 add a loop to scan $substitutionarray:
9691 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.
9692 If no, we don't need to make replacement, so we do nothing.
9693 If yes, we can make the substitution:
9694
9695 include_once $path;
9696 $tmpobj = new $class($db);
9697 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9698 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9699 */
9700 $memory_object_list = array();
9701 foreach ($substitutionarray as $key => $value) {
9702 $lazy_load_arr = array();
9703 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9704 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9705 $key_to_substitute = $lazy_load_arr[1];
9706 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9707 $param_arr = explode(':', $value);
9708 // path:class:method:id
9709 if (count($param_arr) == 4) {
9710 $path = $param_arr[0];
9711 $class = $param_arr[1];
9712 $method = $param_arr[2];
9713 $id = (int) $param_arr[3];
9714
9715 // load class file and init object list in memory
9716 if (!isset($memory_object_list[$class])) {
9717 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9718 require_once DOL_DOCUMENT_ROOT . $path;
9719 if (class_exists($class)) {
9720 $memory_object_list[$class] = array(
9721 'list' => array(),
9722 );
9723 }
9724 }
9725 }
9726
9727 // fetch object and set substitution
9728 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9729 if (method_exists($class, $method)) {
9730 if (!isset($memory_object_list[$class]['list'][$id])) {
9731 $tmpobj = new $class($db);
9732 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9733 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9734 $memory_object_list[$class]['list'][$id] = $tmpobj;
9735 } else {
9736 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9737 $tmpobj = $memory_object_list[$class]['list'][$id];
9738 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9739 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9740 }
9741
9742 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9743 }
9744 }
9745 }
9746 }
9747 }
9748 }
9749 }
9750
9751 return $text;
9752}
9753
9766function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9767{
9768 global $conf, $user;
9769
9770 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9771
9772 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9773
9774 // Check if there is external substitution to do, requested by plugins
9775 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9776
9777 foreach ($dirsubstitutions as $reldir) {
9778 $dir = dol_buildpath($reldir, 0);
9779
9780 // Check if directory exists
9781 if (!dol_is_dir($dir)) {
9782 continue;
9783 }
9784
9785 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9786 foreach ($substitfiles as $substitfile) {
9787 $reg = array();
9788 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9789 $module = $reg[1];
9790
9791 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9792 // Include the user's functions file
9793 require_once $dir.$substitfile['name'];
9794 // Call the user's function, and only if it is defined
9795 $function_name = $module."_".$callfunc;
9796 if (function_exists($function_name)) {
9797 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9798 }
9799 }
9800 }
9801 }
9802 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9803 // to list all tags in odt template
9804 $tags = '';
9805 foreach ($substitutionarray as $key => $value) {
9806 $tags .= '{'.$key.'} => '.$value."\n";
9807 }
9808 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9809 }
9810}
9811
9821function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9822{
9823 print get_date_range($date_start, $date_end, $format, $outputlangs);
9824}
9825
9836function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9837{
9838 global $langs;
9839
9840 $out = '';
9841
9842 if (!is_object($outputlangs)) {
9843 $outputlangs = $langs;
9844 }
9845
9846 if ($date_start && $date_end) {
9847 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9848 }
9849 if ($date_start && !$date_end) {
9850 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9851 }
9852 if (!$date_start && $date_end) {
9853 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9854 }
9855
9856 return $out;
9857}
9858
9867function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9868{
9869 global $conf;
9870
9871 $ret = '';
9872 // If order not defined, we use the setup
9873 if ($nameorder < 0) {
9874 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9875 }
9876 if ($nameorder == 1) {
9877 $ret .= $firstname;
9878 if ($firstname && $lastname) {
9879 $ret .= ' ';
9880 }
9881 $ret .= $lastname;
9882 } elseif ($nameorder == 2 || $nameorder == 3) {
9883 $ret .= $firstname;
9884 if (empty($ret) && $nameorder == 3) {
9885 $ret .= $lastname;
9886 }
9887 } else { // 0, 4 or 5
9888 $ret .= $lastname;
9889 if (empty($ret) && $nameorder == 5) {
9890 $ret .= $firstname;
9891 }
9892 if ($nameorder == 0) {
9893 if ($firstname && $lastname) {
9894 $ret .= ' ';
9895 }
9896 $ret .= $firstname;
9897 }
9898 }
9899 return $ret;
9900}
9901
9902
9915function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
9916{
9917 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9918 if (!is_array($mesgs)) {
9919 $mesgs = trim((string) $mesgs);
9920 // If mesgs is a not an empty string
9921 if ($mesgs) {
9922 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9923 return;
9924 }
9925 if ($attop) {
9926 array_unshift($_SESSION['dol_events'][$style], $mesgs);
9927 } else {
9928 $_SESSION['dol_events'][$style][] = $mesgs;
9929 }
9930 }
9931 } else {
9932 // If mesgs is an array
9933 foreach ($mesgs as $mesg) {
9934 $mesg = trim((string) $mesg);
9935 if ($mesg) {
9936 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
9937 return;
9938 }
9939 if ($attop) {
9940 array_unshift($_SESSION['dol_events'][$style], $mesgs);
9941 } else {
9942 $_SESSION['dol_events'][$style][] = $mesg;
9943 }
9944 }
9945 }
9946 }
9947}
9948
9962function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
9963{
9964 if (empty($mesg) && empty($mesgs)) {
9965 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
9966 } else {
9967 if ($messagekey) {
9968 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
9969 // TODO
9970 $mesg .= '';
9971 }
9972 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
9973 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
9974 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
9975 }
9976 if (empty($mesgs)) {
9977 setEventMessage($mesg, $style, $noduplicate, $attop);
9978 } else {
9979 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
9980 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
9981 }
9982 setEventMessage($mesgs, $style, $noduplicate, $attop);
9983 }
9984 }
9985 }
9986}
9987
9997function dol_htmloutput_events($disabledoutputofmessages = 0)
9998{
9999 // Show mesgs
10000 if (isset($_SESSION['dol_events']['mesgs'])) {
10001 if (empty($disabledoutputofmessages)) {
10002 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10003 }
10004 unset($_SESSION['dol_events']['mesgs']);
10005 }
10006 // Show errors
10007 if (isset($_SESSION['dol_events']['errors'])) {
10008 if (empty($disabledoutputofmessages)) {
10009 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10010 }
10011 unset($_SESSION['dol_events']['errors']);
10012 }
10013
10014 // Show warnings
10015 if (isset($_SESSION['dol_events']['warnings'])) {
10016 if (empty($disabledoutputofmessages)) {
10017 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10018 }
10019 unset($_SESSION['dol_events']['warnings']);
10020 }
10021}
10022
10037function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10038{
10039 global $conf, $langs;
10040
10041 $ret = 0;
10042 $return = '';
10043 $out = '';
10044 $divstart = $divend = '';
10045
10046 // If inline message with no format, we add it.
10047 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10048 $divstart = '<div class="'.$style.' clearboth">';
10049 $divend = '</div>';
10050 }
10051
10052 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10053 $langs->load("errors");
10054 $out .= $divstart;
10055 if (is_array($mesgarray) && count($mesgarray)) {
10056 foreach ($mesgarray as $message) {
10057 $ret++;
10058 $out .= $langs->trans($message);
10059 if ($ret < count($mesgarray)) {
10060 $out .= "<br>\n";
10061 }
10062 }
10063 }
10064 if ($mesgstring) {
10065 $ret++;
10066 $out .= $langs->trans($mesgstring);
10067 }
10068 $out .= $divend;
10069 }
10070
10071 if ($out) {
10072 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10073 $return = '<script nonce="'.getNonce().'">
10074 $(document).ready(function() {
10075 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
10076 if (block) {
10077 $.dolEventValid("","'.dol_escape_js($out).'");
10078 } else {
10079 /* jnotify(message, preset of message type, keepmessage) */
10080 $.jnotify("'.dol_escape_js($out).'",
10081 "'.($style == "ok" ? 3000 : $style).'",
10082 '.($style == "ok" ? "false" : "true").',
10083 { remove: function (){} } );
10084 }
10085 });
10086 </script>';
10087 } else {
10088 $return = $out;
10089 }
10090 }
10091
10092 return $return;
10093}
10094
10106function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10107{
10108 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10109}
10110
10124function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10125{
10126 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10127 return;
10128 }
10129
10130 $iserror = 0;
10131 $iswarning = 0;
10132 if (is_array($mesgarray)) {
10133 foreach ($mesgarray as $val) {
10134 if ($val && preg_match('/class="error"/i', $val)) {
10135 $iserror++;
10136 break;
10137 }
10138 if ($val && preg_match('/class="warning"/i', $val)) {
10139 $iswarning++;
10140 break;
10141 }
10142 }
10143 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10144 $iserror++;
10145 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10146 $iswarning++;
10147 }
10148 if ($style == 'error') {
10149 $iserror++;
10150 }
10151 if ($style == 'warning') {
10152 $iswarning++;
10153 }
10154
10155 if ($iserror || $iswarning) {
10156 // Remove div from texts
10157 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10158 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10159 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10160 // Remove div from texts array
10161 if (is_array($mesgarray)) {
10162 $newmesgarray = array();
10163 foreach ($mesgarray as $val) {
10164 if (is_string($val)) {
10165 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10166 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10167 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10168 $newmesgarray[] = $tmpmesgstring;
10169 } else {
10170 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10171 }
10172 }
10173 $mesgarray = $newmesgarray;
10174 }
10175 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10176 } else {
10177 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10178 }
10179}
10180
10192function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10193{
10194 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10195}
10196
10210function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10211{
10212 // Clean parameters
10213 $order = strtolower($order);
10214
10215 if (is_array($array)) {
10216 $sizearray = count($array);
10217 if ($sizearray > 0) {
10218 $temp = array();
10219 foreach (array_keys($array) as $key) {
10220 if (is_object($array[$key])) {
10221 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10222 } else {
10223 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10224 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10225 }
10226 if ($natsort == -1) {
10227 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10228 }
10229 }
10230
10231 if (empty($natsort) || $natsort == -1) {
10232 if ($order == 'asc') {
10233 asort($temp);
10234 } else {
10235 arsort($temp);
10236 }
10237 } else {
10238 if ($case_sensitive) {
10239 natsort($temp);
10240 } else {
10241 natcasesort($temp); // natecasesort is not sensible to case
10242 }
10243 if ($order != 'asc') {
10244 $temp = array_reverse($temp, true);
10245 }
10246 }
10247
10248 $sorted = array();
10249
10250 foreach (array_keys($temp) as $key) {
10251 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10252 }
10253
10254 return $sorted;
10255 }
10256 }
10257 return $array;
10258}
10259
10260
10268function utf8_check($str)
10269{
10270 $str = (string) $str; // Sometimes string is an int.
10271
10272 // We must use here a binary strlen function (so not dol_strlen)
10273 $strLength = strlen($str);
10274 for ($i = 0; $i < $strLength; $i++) {
10275 if (ord($str[$i]) < 0x80) {
10276 continue; // 0bbbbbbb
10277 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10278 $n = 1; // 110bbbbb
10279 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10280 $n = 2; // 1110bbbb
10281 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10282 $n = 3; // 11110bbb
10283 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10284 $n = 4; // 111110bb
10285 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10286 $n = 5; // 1111110b
10287 } else {
10288 return false; // Does not match any model
10289 }
10290 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10291 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10292 return false;
10293 }
10294 }
10295 }
10296 return true;
10297}
10298
10306function utf8_valid($str)
10307{
10308 /* 2 other methods to test if string is utf8
10309 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10310 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10311 */
10312 return preg_match('//u', $str) ? true : false;
10313}
10314
10315
10322function ascii_check($str)
10323{
10324 if (function_exists('mb_check_encoding')) {
10325 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10326 if (!mb_check_encoding($str, 'ASCII')) {
10327 return false;
10328 }
10329 } else {
10330 if (preg_match('/[^\x00-\x7f]/', $str)) {
10331 return false; // Contains a byte > 7f
10332 }
10333 }
10334
10335 return true;
10336}
10337
10338
10346function dol_osencode($str)
10347{
10348 $tmp = ini_get("unicode.filesystem_encoding");
10349 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10350 $tmp = 'iso-8859-1'; // By default for windows
10351 }
10352 if (empty($tmp)) {
10353 $tmp = 'utf-8'; // By default for other
10354 }
10355 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10356 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10357 }
10358
10359 if ($tmp == 'iso-8859-1') {
10360 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10361 }
10362 return $str;
10363}
10364
10365
10380function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
10381{
10382 global $conf;
10383
10384 // If key empty
10385 if ($key == '') {
10386 return 0;
10387 }
10388
10389 // Check in cache
10390 if (isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10391 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10392 }
10393
10394 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10395
10396 $sql = "SELECT ".$fieldid." as valuetoget";
10397 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10398 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10399 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10400 } else {
10401 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10402 }
10403 if (!empty($entityfilter)) {
10404 $sql .= " AND entity IN (".getEntity($tablename).")";
10405 }
10406 if ($filters) {
10407 $sql .= $filters;
10408 }
10409
10410 $resql = $db->query($sql);
10411 if ($resql) {
10412 $obj = $db->fetch_object($resql);
10413 if ($obj) {
10414 $conf->cache['codeid'][$tablename][$key][$fieldid] = $obj->valuetoget;
10415 } else {
10416 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10417 }
10418 $db->free($resql);
10419
10420 return $conf->cache['codeid'][$tablename][$key][$fieldid];
10421 } else {
10422 return -1;
10423 }
10424}
10425
10435function isStringVarMatching($var, $regextext, $matchrule = 1)
10436{
10437 if ($matchrule == 1) {
10438 if ($var == 'mainmenu') {
10439 global $mainmenu;
10440 return (preg_match('/^'.$regextext.'/', $mainmenu));
10441 } elseif ($var == 'leftmenu') {
10442 global $leftmenu;
10443 return (preg_match('/^'.$regextext.'/', $leftmenu));
10444 } else {
10445 return 'This variable is not accessible with dol_eval';
10446 }
10447 } else {
10448 return 'This value for matchrule is not implemented';
10449 }
10450}
10451
10452
10462function verifCond($strToEvaluate, $onlysimplestring = '1')
10463{
10464 //print $strToEvaluate."<br>\n";
10465 $rights = true;
10466 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10467 //var_dump($strToEvaluate);
10468 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10469 $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
10470 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10471 //var_dump($rights);
10472 }
10473 return $rights;
10474}
10475
10490function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10491{
10492 // Only this global variables can be read by eval function and returned to caller
10493 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10494 global $db, $langs, $user, $website, $websitepage;
10495 global $action, $mainmenu, $leftmenu;
10496 global $mysoc;
10497 global $objectoffield; // To allow the use of $objectoffield in computed fields
10498
10499 // Old variables used
10500 global $object;
10501 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10502
10503 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10504 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10505 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10506 }
10507
10508 try {
10509 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10510 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10511 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10512 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10513 // 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"
10514
10515 // Check if there is dynamic call (first we check chars are all into use a whitelist chars)
10516 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10517 if ($onlysimplestring == '2') {
10518 $specialcharsallowed .= '[]';
10519 }
10520 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10521 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10522 }
10523 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10524 if ($returnvalue) {
10525 return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s;
10526 } else {
10527 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s, LOG_WARNING);
10528 return '';
10529 }
10530 }
10531
10532 // Check if there is dynamic call (first we use black list patterns)
10533 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10534 if ($returnvalue) {
10535 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;
10536 } else {
10537 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);
10538 return '';
10539 }
10540 }
10541
10542 // Now we check if we try dynamic call (by removing white list pattern of using parenthesis then testing if a parenthesis exists)
10543 $savescheck = '';
10544 $scheck = $s;
10545 while ($scheck && $savescheck != $scheck) {
10546 $savescheck = $scheck;
10547 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10548 $scheck = preg_replace('/^\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10549 $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
10550 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10551 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10552 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10553 }
10554 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10555 if (strpos($scheck, '(') !== false) {
10556 if ($returnvalue) {
10557 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10558 } else {
10559 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);
10560 return '';
10561 }
10562 }
10563
10564 // TODO
10565 // We can exclude $ char that are not:
10566 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10567 }
10568 if (is_array($s) || $s === 'Array') {
10569 if ($returnvalue) {
10570 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10571 } else {
10572 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10573 return '';
10574 }
10575 }
10576 if (strpos($s, '::') !== false) {
10577 if ($returnvalue) {
10578 return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
10579 } else {
10580 dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s, LOG_WARNING);
10581 return '';
10582 }
10583 }
10584 if (strpos($s, '`') !== false) {
10585 if ($returnvalue) {
10586 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10587 } else {
10588 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10589 return '';
10590 }
10591 }
10592 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10593 if ($returnvalue) {
10594 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10595 } else {
10596 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10597 return '';
10598 }
10599 }
10600
10601 // We block use of php exec or php file functions
10602 $forbiddenphpstrings = array('$$', '$_', '}[');
10603 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10604
10605 $forbiddenphpfunctions = array();
10606 // @phpcs:ignore
10607 $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
10608 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10609 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10610 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10611 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
10612 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10613 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10614 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10615 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
10616 $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
10617
10618 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10619
10620 $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
10621
10622 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10623
10624 do {
10625 $oldstringtoclean = $s;
10626 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10627 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10628 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10629 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10630 } while ($oldstringtoclean != $s);
10631
10632
10633 if (strpos($s, '__forbiddenstring__') !== false) {
10634 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10635 if ($returnvalue) {
10636 return 'Bad string syntax to evaluate: '.$s;
10637 } else {
10638 dol_syslog('Bad string syntax to evaluate: '.$s);
10639 return '';
10640 }
10641 }
10642
10643 //print $s."<br>\n";
10644 if ($returnvalue) {
10645 ob_start(); // An evaluation has no reason to output data
10646 $isObBufferActive = true;
10647 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
10648 $tmpo = ob_get_clean();
10649 $isObBufferActive = false;
10650 if ($tmpo) {
10651 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
10652 }
10653 return $tmps;
10654 } else {
10655 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10656 if ($hideerrors) {
10657 @eval($s);
10658 } else {
10659 eval($s);
10660 }
10661 return '';
10662 }
10663 } catch (Error $e) {
10664 if ($isObBufferActive) {
10665 // Clean up buffer which was left behind due to exception.
10666 $tmpo = ob_get_clean();
10667 $isObBufferActive = false;
10668 }
10669 $error = 'dol_eval try/catch error : ';
10670 $error .= $e->getMessage();
10671 dol_syslog($error, LOG_WARNING);
10672 if ($returnvalue) {
10673 return 'Exception during evaluation: '.$s;
10674 } else {
10675 return '';
10676 }
10677 }
10678}
10679
10687function dol_validElement($element)
10688{
10689 return (trim($element) != '');
10690}
10691
10700function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10701{
10702 if (empty($codelang)) {
10703 return '';
10704 }
10705
10706 if ($codelang == 'auto') {
10707 return '<span class="fa fa-language"></span>';
10708 }
10709
10710 $langtocountryflag = array(
10711 'ar_AR' => '',
10712 'ca_ES' => 'catalonia',
10713 'da_DA' => 'dk',
10714 'fr_CA' => 'mq',
10715 'sv_SV' => 'se',
10716 'sw_SW' => 'unknown',
10717 'AQ' => 'unknown',
10718 'CW' => 'unknown',
10719 'IM' => 'unknown',
10720 'JE' => 'unknown',
10721 'MF' => 'unknown',
10722 'BL' => 'unknown',
10723 'SX' => 'unknown'
10724 );
10725
10726 if (isset($langtocountryflag[$codelang])) {
10727 $flagImage = $langtocountryflag[$codelang];
10728 } else {
10729 $tmparray = explode('_', $codelang);
10730 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10731 }
10732
10733 $morecss = '';
10734 $reg = array();
10735 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10736 $morecss = $reg[1];
10737 $moreatt = "";
10738 }
10739
10740 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10741 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10742}
10743
10751function getLanguageCodeFromCountryCode($countrycode)
10752{
10753 global $mysoc;
10754
10755 if (empty($countrycode)) {
10756 return null;
10757 }
10758
10759 if (strtoupper($countrycode) == 'MQ') {
10760 return 'fr_CA';
10761 }
10762 if (strtoupper($countrycode) == 'SE') {
10763 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10764 }
10765 if (strtoupper($countrycode) == 'CH') {
10766 if ($mysoc->country_code == 'FR') {
10767 return 'fr_CH';
10768 }
10769 if ($mysoc->country_code == 'DE') {
10770 return 'de_CH';
10771 }
10772 if ($mysoc->country_code == 'IT') {
10773 return 'it_CH';
10774 }
10775 }
10776
10777 // Locale list taken from:
10778 // http://stackoverflow.com/questions/3191664/
10779 // list-of-all-locales-and-their-short-codes
10780 $locales = array(
10781 'af-ZA',
10782 'am-ET',
10783 'ar-AE',
10784 'ar-BH',
10785 'ar-DZ',
10786 'ar-EG',
10787 'ar-IQ',
10788 'ar-JO',
10789 'ar-KW',
10790 'ar-LB',
10791 'ar-LY',
10792 'ar-MA',
10793 'ar-OM',
10794 'ar-QA',
10795 'ar-SA',
10796 'ar-SY',
10797 'ar-TN',
10798 'ar-YE',
10799 //'as-IN', // Moved after en-IN
10800 'ba-RU',
10801 'be-BY',
10802 'bg-BG',
10803 'bn-BD',
10804 //'bn-IN', // Moved after en-IN
10805 'bo-CN',
10806 'br-FR',
10807 'ca-ES',
10808 'co-FR',
10809 'cs-CZ',
10810 'cy-GB',
10811 'da-DK',
10812 'de-AT',
10813 'de-CH',
10814 'de-DE',
10815 'de-LI',
10816 'de-LU',
10817 'dv-MV',
10818 'el-GR',
10819 'en-AU',
10820 'en-BZ',
10821 'en-CA',
10822 'en-GB',
10823 'en-IE',
10824 'en-IN',
10825 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10826 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10827 'en-JM',
10828 'en-MY',
10829 'en-NZ',
10830 'en-PH',
10831 'en-SG',
10832 'en-TT',
10833 'en-US',
10834 'en-ZA',
10835 'en-ZW',
10836 'es-AR',
10837 'es-BO',
10838 'es-CL',
10839 'es-CO',
10840 'es-CR',
10841 'es-DO',
10842 'es-EC',
10843 'es-ES',
10844 'es-GT',
10845 'es-HN',
10846 'es-MX',
10847 'es-NI',
10848 'es-PA',
10849 'es-PE',
10850 'es-PR',
10851 'es-PY',
10852 'es-SV',
10853 'es-US',
10854 'es-UY',
10855 'es-VE',
10856 'et-EE',
10857 'eu-ES',
10858 'fa-IR',
10859 'fi-FI',
10860 'fo-FO',
10861 'fr-BE',
10862 'fr-CA',
10863 'fr-CH',
10864 'fr-FR',
10865 'fr-LU',
10866 'fr-MC',
10867 'fy-NL',
10868 'ga-IE',
10869 'gd-GB',
10870 'gl-ES',
10871 'gu-IN',
10872 'he-IL',
10873 'hi-IN',
10874 'hr-BA',
10875 'hr-HR',
10876 'hu-HU',
10877 'hy-AM',
10878 'id-ID',
10879 'ig-NG',
10880 'ii-CN',
10881 'is-IS',
10882 'it-CH',
10883 'it-IT',
10884 'ja-JP',
10885 'ka-GE',
10886 'kk-KZ',
10887 'kl-GL',
10888 'km-KH',
10889 'kn-IN',
10890 'ko-KR',
10891 'ky-KG',
10892 'lb-LU',
10893 'lo-LA',
10894 'lt-LT',
10895 'lv-LV',
10896 'mi-NZ',
10897 'mk-MK',
10898 'ml-IN',
10899 'mn-MN',
10900 'mr-IN',
10901 'ms-BN',
10902 'ms-MY',
10903 'mt-MT',
10904 'nb-NO',
10905 'ne-NP',
10906 'nl-BE',
10907 'nl-NL',
10908 'nn-NO',
10909 'oc-FR',
10910 'or-IN',
10911 'pa-IN',
10912 'pl-PL',
10913 'ps-AF',
10914 'pt-BR',
10915 'pt-PT',
10916 'rm-CH',
10917 'ro-MD',
10918 'ro-RO',
10919 'ru-RU',
10920 'rw-RW',
10921 'sa-IN',
10922 'se-FI',
10923 'se-NO',
10924 'se-SE',
10925 'si-LK',
10926 'sk-SK',
10927 'sl-SI',
10928 'sq-AL',
10929 'sv-FI',
10930 'sv-SE',
10931 'sw-KE',
10932 'ta-IN',
10933 'te-IN',
10934 'th-TH',
10935 'tk-TM',
10936 'tn-ZA',
10937 'tr-TR',
10938 'tt-RU',
10939 'ug-CN',
10940 'uk-UA',
10941 'ur-PK',
10942 'vi-VN',
10943 'wo-SN',
10944 'xh-ZA',
10945 'yo-NG',
10946 'zh-CN',
10947 'zh-HK',
10948 'zh-MO',
10949 'zh-SG',
10950 'zh-TW',
10951 'zu-ZA',
10952 );
10953
10954 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
10955 if (in_array($buildprimarykeytotest, $locales)) {
10956 return strtolower($countrycode).'_'.strtoupper($countrycode);
10957 }
10958
10959 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
10960 foreach ($locales as $locale) {
10961 $locale_language = locale_get_primary_language($locale);
10962 $locale_region = locale_get_region($locale);
10963 if (strtoupper($countrycode) == $locale_region) {
10964 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
10965 return strtolower($locale_language).'_'.strtoupper($locale_region);
10966 }
10967 }
10968 } else {
10969 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
10970 }
10971
10972 return null;
10973}
10974
11005function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11006{
11007 global $hookmanager, $db;
11008
11009 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11010 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11011 $values = explode(':', $value);
11012
11013 $reg = array();
11014 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11015 $newtab = array();
11016 $postab = $h;
11017 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11018 $str = $values[1];
11019 $posstart = strpos($str, '(');
11020 if ($posstart > 0) {
11021 $posend = strpos($str, ')');
11022 if ($posstart > 0) {
11023 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11024 if (is_numeric($res1)) {
11025 $postab = (int) $res1;
11026 $values[1] = '+' . substr($str, $posend + 1);
11027 }
11028 }
11029 }
11030 if (count($values) == 6) {
11031 // new declaration with permissions:
11032 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11033 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11034 if ($values[0] != $type) {
11035 continue;
11036 }
11037
11038 if (verifCond($values[4], '2')) {
11039 if ($values[3]) {
11040 if ($filterorigmodule) { // If a filter of module origin has been requested
11041 if (strpos($values[3], '@')) { // This is an external module
11042 if ($filterorigmodule != 'external') {
11043 continue;
11044 }
11045 } else { // This looks a core module
11046 if ($filterorigmodule != 'core') {
11047 continue;
11048 }
11049 }
11050 }
11051 $langs->load($values[3]);
11052 }
11053 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11054 // If label is "SUBSTITUION_..."
11055 $substitutionarray = array();
11056 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11057 $label = make_substitutions($reg[1], $substitutionarray);
11058 } else {
11059 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11060 $labeltemp = explode(',', $values[2]);
11061 $label = $langs->trans($labeltemp[0]);
11062
11063 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11064 dol_include_once($labeltemp[2]);
11065 $classtoload = $labeltemp[1];
11066 if (class_exists($classtoload)) {
11067 $obj = new $classtoload($db);
11068 $function = $labeltemp[3];
11069 if ($obj && $function && method_exists($obj, $function)) {
11070 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11071 $nbrec = $obj->$function($object->id, $obj);
11072 if (!empty($nbrec)) {
11073 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11074 }
11075 }
11076 }
11077 }
11078 }
11079
11080 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11081 $newtab[1] = $label;
11082 $newtab[2] = str_replace('+', '', $values[1]);
11083 $h++;
11084 } else {
11085 continue;
11086 }
11087 } elseif (count($values) == 5) { // case deprecated
11088 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11089
11090 if ($values[0] != $type) {
11091 continue;
11092 }
11093 if ($values[3]) {
11094 if ($filterorigmodule) { // If a filter of module origin has been requested
11095 if (strpos($values[3], '@')) { // This is an external module
11096 if ($filterorigmodule != 'external') {
11097 continue;
11098 }
11099 } else { // This looks a core module
11100 if ($filterorigmodule != 'core') {
11101 continue;
11102 }
11103 }
11104 }
11105 $langs->load($values[3]);
11106 }
11107 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11108 $substitutionarray = array();
11109 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11110 $label = make_substitutions($reg[1], $substitutionarray);
11111 } else {
11112 $label = $langs->trans($values[2]);
11113 }
11114
11115 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11116 $newtab[1] = $label;
11117 $newtab[2] = str_replace('+', '', $values[1]);
11118 $h++;
11119 }
11120 // set tab at its position
11121 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11122 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11123 if ($values[0] != $type) {
11124 continue;
11125 }
11126 $tabname = str_replace('-', '', $values[1]);
11127 foreach ($head as $key => $val) {
11128 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11129 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11130 if ($head[$key][2] == $tabname && $condition) {
11131 unset($head[$key]);
11132 break;
11133 }
11134 }
11135 }
11136 }
11137 }
11138
11139 // No need to make a return $head. Var is modified as a reference
11140 if (!empty($hookmanager)) {
11141 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11142 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11143 if ($reshook > 0) { // Hook ask to replace completely the array
11144 $head = $hookmanager->resArray;
11145 } else { // Hook
11146 $head = array_merge($head, $hookmanager->resArray);
11147 }
11148 $h = count($head);
11149 }
11150}
11151
11163function printCommonFooter($zone = 'private')
11164{
11165 global $conf, $hookmanager, $user, $langs;
11166 global $debugbar;
11167 global $action;
11168 global $micro_start_time;
11169
11170 if ($zone == 'private') {
11171 print "\n".'<!-- Common footer for private page -->'."\n";
11172 } else {
11173 print "\n".'<!-- Common footer for public page -->'."\n";
11174 }
11175
11176 // A div to store page_y POST parameter so we can read it using javascript
11177 print "\n<!-- A div to store page_y POST parameter -->\n";
11178 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11179
11180 $parameters = array();
11181 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11182 if (empty($reshook)) {
11183 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11184 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11185 }
11186
11187 print "\n";
11188 if (!empty($conf->use_javascript_ajax)) {
11189 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11190 print '<script>'."\n";
11191 print 'jQuery(document).ready(function() {'."\n";
11192
11193 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11194 print "\n";
11195 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11196 print 'jQuery("li.menuhider").click(function(event) {';
11197 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11198 print ' console.log("We click on .menuhider");'."\n";
11199 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11200 print '});'."\n";
11201 }
11202
11203 // Management of focus and mandatory for fields
11204 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"])))) {
11205 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11206 $relativepathstring = $_SERVER["PHP_SELF"];
11207 // Clean $relativepathstring
11208 if (constant('DOL_URL_ROOT')) {
11209 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11210 }
11211 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11212 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11213 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11214
11215 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11216 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11217 $qualified = 0;
11218 if ($defkey != '_noquery_') {
11219 $tmpqueryarraytohave = explode('&', $defkey);
11220 $foundintru = 0;
11221 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11222 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11223 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11224 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11225 $foundintru = 1;
11226 }
11227 }
11228 if (!$foundintru) {
11229 $qualified = 1;
11230 }
11231 //var_dump($defkey.'-'.$qualified);
11232 } else {
11233 $qualified = 1;
11234 }
11235
11236 if ($qualified) {
11237 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11238 foreach ($defval as $paramkey => $paramval) {
11239 // Set focus on field
11240 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11241 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11242 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11243 }
11244 }
11245 }
11246 }
11247 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11248 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11249 $qualified = 0;
11250 if ($defkey != '_noquery_') {
11251 $tmpqueryarraytohave = explode('&', $defkey);
11252 $foundintru = 0;
11253 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11254 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11255 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11256 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11257 $foundintru = 1;
11258 }
11259 }
11260 if (!$foundintru) {
11261 $qualified = 1;
11262 }
11263 //var_dump($defkey.'-'.$qualified);
11264 } else {
11265 $qualified = 1;
11266 }
11267
11268 if ($qualified) {
11269 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11270
11271 foreach ($defval as $paramkey => $paramval) {
11272 // Solution 1: Add handler on submit to check if mandatory fields are empty
11273 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11274 print "form.on('submit', function(event) {
11275 var submitter = event.originalEvent.submitter;
11276 if (submitter) {
11277 var buttonName = $(submitter).attr('name');
11278 if (buttonName == 'cancel') {
11279 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11280 return true;
11281 }
11282 }
11283
11284 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11285
11286 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11287 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11288
11289 if (tmptypefield == 'textarea') {
11290 // We must instead check the content of ckeditor
11291 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11292 if (tmpeditor) {
11293 tmpvalue = tmpeditor.getData();
11294 console.log('For textarea tmpvalue is '+tmpvalue);
11295 }
11296 }
11297
11298 let tmpvalueisempty = false;
11299 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '') {
11300 tmpvalueisempty = true;
11301 }
11302 if (tmpvalue === '0' && tmptypefield == 'select') {
11303 tmpvalueisempty = true;
11304 }
11305 if (tmpvalueisempty) {
11306 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11307 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11308 event.stopPropagation(); // Stop other handlers.
11309 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11310 return false;
11311 }
11312 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11313 return true;
11314 });
11315 \n";
11316
11317 // Solution 2: Add property 'required' on input
11318 // so browser will check value and try to focus on it when submitting the form.
11319 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11320 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11321 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11322 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11323 //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";
11324 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11325 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11326 // Add 'field required' class on closest td for all input elements : input, textarea and select
11327 //print '}, 500);'; // 500 milliseconds delay
11328
11329 // Now set the class "fieldrequired"
11330 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11331 }
11332
11333
11334 // If we submit using the cancel button, we remove the required attributes
11335 print 'jQuery("input[name=\'cancel\']").click(function() {
11336 console.log("We click on cancel button so removed all required attribute");
11337 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11338 });'."\n";
11339 }
11340 }
11341 }
11342 }
11343
11344 print '});'."\n";
11345
11346 // End of tuning
11347 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11348 print "\n";
11349 print "/* JS CODE TO ENABLE to add memory info */\n";
11350 print 'window.console && console.log("';
11351 if (getDolGlobalString('MEMCACHED_SERVER')) {
11352 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11353 }
11354 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11355 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11356 $micro_end_time = microtime(true);
11357 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11358 }
11359
11360 if (function_exists("memory_get_usage")) {
11361 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11362 }
11363 if (function_exists("memory_get_peak_usage")) {
11364 print ' - Real mem peak: '.memory_get_peak_usage(true);
11365 }
11366 if (function_exists("zend_loader_file_encoded")) {
11367 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11368 }
11369 print '");'."\n";
11370 }
11371
11372 print "\n".'</script>'."\n";
11373
11374 // Google Analytics
11375 // TODO Add a hook here
11376 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11377 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11378 foreach ($tmptagarray as $tmptag) {
11379 print "\n";
11380 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11381 print '
11382 <!-- Global site tag (gtag.js) - Google Analytics -->
11383 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11384 <script>
11385 window.dataLayer = window.dataLayer || [];
11386 function gtag(){dataLayer.push(arguments);}
11387 gtag(\'js\', new Date());
11388
11389 gtag(\'config\', \''.trim($tmptag).'\');
11390 </script>';
11391 print "\n";
11392 }
11393 }
11394 }
11395
11396 // Add Xdebug coverage of code
11397 if (defined('XDEBUGCOVERAGE')) {
11398 print_r(xdebug_get_code_coverage());
11399 }
11400
11401 // Add DebugBar data
11402 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11403 if (isset($debugbar['time'])) {
11404 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11405 $debugbar['time']->stopMeasure('pageaftermaster');
11406 }
11407 print '<!-- Output debugbar data -->'."\n";
11408 $renderer = $debugbar->getJavascriptRenderer();
11409 print $renderer->render();
11410 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11411 print "\n";
11412 print "<!-- Start of log output\n";
11413 //print '<div class="hidden">'."\n";
11414 foreach ($conf->logbuffer as $logline) {
11415 print $logline."<br>\n";
11416 }
11417 //print '</div>'."\n";
11418 print "End of log output -->\n";
11419 }
11420 }
11421}
11422
11432function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11433{
11434 if (is_null($string)) {
11435 return array();
11436 }
11437
11438 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11439 // This is a regex string
11440 $newdelimiter = $delimiter;
11441 } else {
11442 // This is a simple string
11443 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11444 $newdelimiter = preg_quote($delimiter, '/');
11445 }
11446
11447 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11448 $ka = array();
11449 foreach ($a as $s) { // each part
11450 if ($s) {
11451 if ($pos = strpos($s, $kv)) { // key/value delimiter
11452 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11453 } else { // key delimiter not found
11454 $ka[] = trim($s);
11455 }
11456 }
11457 }
11458 return $ka;
11459 }
11460
11461 return array();
11462}
11463
11464
11471function dol_set_focus($selector)
11472{
11473 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11474 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11475}
11476
11477
11485function dol_getmypid()
11486{
11487 if (!function_exists('getmypid')) {
11488 return mt_rand(99900000, 99965535);
11489 } else {
11490 return getmypid(); // May be a number on 64 bits (depending on OS)
11491 }
11492}
11493
11494
11516function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11517{
11518 global $db, $langs;
11519
11520 $value = trim($value);
11521
11522 if ($mode == 0) {
11523 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11524 }
11525 if ($mode == 1) {
11526 $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
11527 }
11528
11529 $value = preg_replace('/\s*\|\s*/', '|', $value);
11530
11531 $crits = explode(' ', $value);
11532 $res = '';
11533 if (!is_array($fields)) {
11534 $fields = array($fields);
11535 }
11536
11537 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11538 foreach ($crits as $crit) { // Loop on each AND criteria
11539 $crit = trim($crit);
11540 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11541 $newres = '';
11542 foreach ($fields as $field) {
11543 if ($mode == 1) {
11544 $tmpcrits = explode('|', $crit);
11545 $i3 = 0; // count the nb of valid criteria added for this current field
11546 foreach ($tmpcrits as $tmpcrit) {
11547 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11548 continue;
11549 }
11550 $tmpcrit = trim($tmpcrit);
11551
11552 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11553
11554 $operator = '=';
11555 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11556
11557 $reg = array();
11558 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11559 if (!empty($reg[1])) {
11560 $operator = $reg[1];
11561 }
11562 if ($newcrit != '') {
11563 $numnewcrit = price2num($newcrit);
11564 if (is_numeric($numnewcrit)) {
11565 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11566 } else {
11567 $newres .= '1 = 2'; // force false, we received a corrupted data
11568 }
11569 $i3++; // a criteria was added to string
11570 }
11571 }
11572 $i2++; // a criteria for 1 more field was added to string
11573 } elseif ($mode == 2 || $mode == -2) {
11574 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11575 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11576 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11577 if ($mode == -2) {
11578 $newres .= ' OR '.$field.' IS NULL';
11579 }
11580 $i2++; // a criteria for 1 more field was added to string
11581 } elseif ($mode == 3 || $mode == -3) {
11582 $tmparray = explode(',', $crit);
11583 if (count($tmparray)) {
11584 $listofcodes = '';
11585 foreach ($tmparray as $val) {
11586 $val = trim($val);
11587 if ($val) {
11588 $listofcodes .= ($listofcodes ? ',' : '');
11589 $listofcodes .= "'".$db->escape($val)."'";
11590 }
11591 }
11592 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
11593 $i2++; // a criteria for 1 more field was added to string
11594 }
11595 if ($mode == -3) {
11596 $newres .= ' OR '.$field.' IS NULL';
11597 }
11598 } elseif ($mode == 4) {
11599 $tmparray = explode(',', $crit);
11600 if (count($tmparray)) {
11601 $listofcodes = '';
11602 foreach ($tmparray as $val) {
11603 $val = trim($val);
11604 if ($val) {
11605 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11606 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11607 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11608 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11609 $newres .= ')';
11610 $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)
11611 }
11612 }
11613 }
11614 } else { // $mode=0
11615 $tmpcrits = explode('|', $crit);
11616 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11617 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11618 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11619 continue;
11620 }
11621 $tmpcrit = trim($tmpcrit);
11622
11623 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11624 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11625 } else {
11626 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11627 }
11628
11629 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11630 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11631 } else {
11632 $tmpcrit2 = $tmpcrit;
11633 $tmpbefore = '%';
11634 $tmpafter = '%';
11635 $tmps = '';
11636
11637 if (preg_match('/^!/', $tmpcrit)) {
11638 $tmps .= $field." NOT LIKE "; // ! as exclude character
11639 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11640 } else {
11641 $tmps .= $field." LIKE ";
11642 }
11643 $tmps .= "'";
11644
11645 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11646 $tmpbefore = '';
11647 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11648 }
11649 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11650 $tmpafter = '';
11651 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11652 }
11653
11654 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11655 $tmps = "(".$tmps;
11656 }
11657 $newres .= $tmps;
11658 $newres .= $tmpbefore;
11659 $newres .= $db->escape($tmpcrit2);
11660 $newres .= $tmpafter;
11661 $newres .= "'";
11662 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11663 $newres .= " OR ".$field." IS NULL)";
11664 }
11665 }
11666
11667 $i3++;
11668 }
11669
11670 $i2++; // a criteria for 1 more field was added to string
11671 }
11672 }
11673
11674 if ($newres) {
11675 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11676 }
11677 $i1++;
11678 }
11679 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11680
11681 return $res;
11682}
11683
11690function showDirectDownloadLink($object)
11691{
11692 global $conf, $langs;
11693
11694 $out = '';
11695 $url = $object->getLastMainDocLink($object->element);
11696
11697 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11698 if ($url) {
11699 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11700 $out .= ajax_autoselect("directdownloadlink", '');
11701 } else {
11702 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11703 }
11704
11705 return $out;
11706}
11707
11716function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11717{
11718 $dirName = dirname($file);
11719 if ($dirName == '.') {
11720 $dirName = '';
11721 }
11722
11723 if (!in_array($extName, array('', '_small', '_mini'))) {
11724 return 'Bad parameter extName';
11725 }
11726
11727 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
11728 $fileName = basename($fileName);
11729
11730 if (empty($extImgTarget)) {
11731 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11732 }
11733 if (empty($extImgTarget)) {
11734 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11735 }
11736 if (empty($extImgTarget)) {
11737 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11738 }
11739 if (empty($extImgTarget)) {
11740 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11741 }
11742 if (empty($extImgTarget)) {
11743 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11744 }
11745 if (empty($extImgTarget)) {
11746 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11747 }
11748
11749 if (!$extImgTarget) {
11750 return $file;
11751 }
11752
11753 $subdir = '';
11754 if ($extName) {
11755 $subdir = 'thumbs/';
11756 }
11757
11758 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11759}
11760
11761
11771function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11772{
11773 global $conf, $langs;
11774
11775 if (empty($conf->use_javascript_ajax)) {
11776 return '';
11777 }
11778
11779 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11780
11781 if ($alldata == 1) {
11782 if ($isAllowedForPreview) {
11783 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));
11784 } else {
11785 return array();
11786 }
11787 }
11788
11789 // old behavior, return a string
11790 if ($isAllowedForPreview) {
11791 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11792 $title = $langs->transnoentities("Preview");
11793 //$title = '%27-alert(document.domain)-%27';
11794 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg");
11795
11796 // 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.
11797 // and when we click on href with this javascript string, a urlcode is done by browser, converted the %27 of file param
11798 return 'javascript:document_preview(\''.urlencode(dol_escape_js($tmpurl)).'\', \''.urlencode(dol_mimetype($relativepath)).'\', \''.urlencode(dol_escape_js($title)).'\')';
11799 } else {
11800 return '';
11801 }
11802}
11803
11804
11813function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11814{
11815 global $langs;
11816 $out = '<script nonce="'.getNonce().'">
11817 jQuery(document).ready(function () {
11818 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11819 });
11820 </script>';
11821 if ($addlink) {
11822 if ($textonlink === 'image') {
11823 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11824 } else {
11825 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11826 }
11827 }
11828 return $out;
11829}
11830
11838function dolIsAllowedForPreview($file)
11839{
11840 // Check .noexe extension in filename
11841 if (preg_match('/\.noexe$/i', $file)) {
11842 return 0;
11843 }
11844
11845 // Check mime types
11846 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11847 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11848 $mime_preview[] = 'svg+xml';
11849 }
11850 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11851 //$mime_preview[]='archive';
11852 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11853 if ($num_mime !== false) {
11854 return 1;
11855 }
11856
11857 // By default, not allowed for preview
11858 return 0;
11859}
11860
11861
11871function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11872{
11873 $mime = $default;
11874 $imgmime = 'other.png';
11875 $famime = 'file-o';
11876 $srclang = '';
11877
11878 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11879
11880 // Plain text files
11881 if (preg_match('/\.txt$/i', $tmpfile)) {
11882 $mime = 'text/plain';
11883 $imgmime = 'text.png';
11884 $famime = 'file-alt';
11885 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
11886 $mime = 'text/richtext';
11887 $imgmime = 'text.png';
11888 $famime = 'file-alt';
11889 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
11890 $mime = 'text/csv';
11891 $imgmime = 'text.png';
11892 $famime = 'file-csv';
11893 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
11894 $mime = 'text/tab-separated-values';
11895 $imgmime = 'text.png';
11896 $famime = 'file-alt';
11897 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
11898 $mime = 'text/plain';
11899 $imgmime = 'text.png';
11900 $famime = 'file-alt';
11901 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
11902 $mime = 'text/plain';
11903 $imgmime = 'text.png';
11904 $srclang = 'ini';
11905 $famime = 'file-alt';
11906 } elseif (preg_match('/\.md$/i', $tmpfile)) {
11907 $mime = 'text/plain';
11908 $imgmime = 'text.png';
11909 $srclang = 'md';
11910 $famime = 'file-alt';
11911 } elseif (preg_match('/\.css$/i', $tmpfile)) {
11912 $mime = 'text/css';
11913 $imgmime = 'css.png';
11914 $srclang = 'css';
11915 $famime = 'file-alt';
11916 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
11917 $mime = 'text/plain';
11918 $imgmime = 'text.png';
11919 $srclang = 'lang';
11920 $famime = 'file-alt';
11921 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
11922 $mime = 'text/plain';
11923 $imgmime = 'text.png';
11924 $famime = 'file-alt';
11925 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
11926 $mime = 'text/html';
11927 $imgmime = 'html.png';
11928 $srclang = 'html';
11929 $famime = 'file-alt';
11930 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
11931 $mime = 'text/xml';
11932 $imgmime = 'other.png';
11933 $srclang = 'xml';
11934 $famime = 'file-alt';
11935 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
11936 $mime = 'text/xml';
11937 $imgmime = 'other.png';
11938 $srclang = 'xaml';
11939 $famime = 'file-alt';
11940 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
11941 $mime = 'text/plain';
11942 $imgmime = 'text.png';
11943 $srclang = 'bas';
11944 $famime = 'file-code';
11945 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
11946 $mime = 'text/plain';
11947 $imgmime = 'text.png';
11948 $srclang = 'c';
11949 $famime = 'file-code';
11950 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
11951 $mime = 'text/plain';
11952 $imgmime = 'text.png';
11953 $srclang = 'cpp';
11954 $famime = 'file-code';
11955 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
11956 $mime = 'text/plain';
11957 $imgmime = 'text.png';
11958 $srclang = 'cs';
11959 $famime = 'file-code';
11960 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
11961 $mime = 'text/plain';
11962 $imgmime = 'text.png';
11963 $srclang = 'h';
11964 $famime = 'file-code';
11965 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
11966 $mime = 'text/plain';
11967 $imgmime = 'text.png';
11968 $srclang = 'java';
11969 $famime = 'file-code';
11970 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
11971 $mime = 'text/plain';
11972 $imgmime = 'php.png';
11973 $srclang = 'php';
11974 $famime = 'file-code';
11975 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
11976 $mime = 'text/plain';
11977 $imgmime = 'php.png';
11978 $srclang = 'php';
11979 $famime = 'file-code';
11980 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
11981 $mime = 'text/plain';
11982 $imgmime = 'pl.png';
11983 $srclang = 'perl';
11984 $famime = 'file-code';
11985 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
11986 $mime = 'text/plain';
11987 $imgmime = 'text.png';
11988 $srclang = 'sql';
11989 $famime = 'file-code';
11990 } elseif (preg_match('/\.js$/i', $tmpfile)) {
11991 $mime = 'text/x-javascript';
11992 $imgmime = 'jscript.png';
11993 $srclang = 'js';
11994 $famime = 'file-code';
11995 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
11996 $mime = 'application/vnd.oasis.opendocument.presentation';
11997 $imgmime = 'ooffice.png';
11998 $famime = 'file-powerpoint';
11999 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12000 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12001 $imgmime = 'ooffice.png';
12002 $famime = 'file-excel';
12003 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12004 $mime = 'application/vnd.oasis.opendocument.text';
12005 $imgmime = 'ooffice.png';
12006 $famime = 'file-word';
12007 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12008 $mime = 'application/msaccess';
12009 $imgmime = 'mdb.png';
12010 $famime = 'file';
12011 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12012 $mime = 'application/msword';
12013 $imgmime = 'doc.png';
12014 $famime = 'file-word';
12015 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12016 $mime = 'application/msword';
12017 $imgmime = 'doc.png';
12018 $famime = 'file-word';
12019 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12020 $mime = 'application/vnd.ms-excel';
12021 $imgmime = 'xls.png';
12022 $famime = 'file-excel';
12023 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12024 $mime = 'application/vnd.ms-excel';
12025 $imgmime = 'xls.png';
12026 $famime = 'file-excel';
12027 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12028 $mime = 'application/vnd.ms-excel';
12029 $imgmime = 'xls.png';
12030 $famime = 'file-excel';
12031 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12032 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12033 $imgmime = 'xls.png';
12034 $famime = 'file-excel';
12035 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12036 $mime = 'application/vnd.ms-powerpoint';
12037 $imgmime = 'ppt.png';
12038 $famime = 'file-powerpoint';
12039 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12040 $mime = 'application/x-mspowerpoint';
12041 $imgmime = 'ppt.png';
12042 $famime = 'file-powerpoint';
12043 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12044 $mime = 'application/pdf';
12045 $imgmime = 'pdf.png';
12046 $famime = 'file-pdf';
12047 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12048 $mime = 'text/x-bat';
12049 $imgmime = 'script.png';
12050 $srclang = 'dos';
12051 $famime = 'file-code';
12052 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12053 $mime = 'text/x-sh';
12054 $imgmime = 'script.png';
12055 $srclang = 'bash';
12056 $famime = 'file-code';
12057 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12058 $mime = 'text/x-ksh';
12059 $imgmime = 'script.png';
12060 $srclang = 'bash';
12061 $famime = 'file-code';
12062 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12063 $mime = 'text/x-bash';
12064 $imgmime = 'script.png';
12065 $srclang = 'bash';
12066 $famime = 'file-code';
12067 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12068 $mime = 'image/x-icon';
12069 $imgmime = 'image.png';
12070 $famime = 'file-image';
12071 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12072 $mime = 'image/jpeg';
12073 $imgmime = 'image.png';
12074 $famime = 'file-image';
12075 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12076 $mime = 'image/png';
12077 $imgmime = 'image.png';
12078 $famime = 'file-image';
12079 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12080 $mime = 'image/gif';
12081 $imgmime = 'image.png';
12082 $famime = 'file-image';
12083 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12084 $mime = 'image/bmp';
12085 $imgmime = 'image.png';
12086 $famime = 'file-image';
12087 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12088 $mime = 'image/tiff';
12089 $imgmime = 'image.png';
12090 $famime = 'file-image';
12091 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12092 $mime = 'image/svg+xml';
12093 $imgmime = 'image.png';
12094 $famime = 'file-image';
12095 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12096 $mime = 'image/webp';
12097 $imgmime = 'image.png';
12098 $famime = 'file-image';
12099 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12100 $mime = 'text/calendar';
12101 $imgmime = 'other.png';
12102 $famime = 'file-alt';
12103 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12104 $mime = 'text/calendar';
12105 $imgmime = 'other.png';
12106 $famime = 'file-alt';
12107 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12108 $mime = 'application/x-bittorrent';
12109 $imgmime = 'other.png';
12110 $famime = 'file-o';
12111 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12112 $mime = 'audio';
12113 $imgmime = 'audio.png';
12114 $famime = 'file-audio';
12115 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12116 $mime = 'video/mp4';
12117 $imgmime = 'video.png';
12118 $famime = 'file-video';
12119 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12120 $mime = 'video/ogg';
12121 $imgmime = 'video.png';
12122 $famime = 'file-video';
12123 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12124 $mime = 'video/webm';
12125 $imgmime = 'video.png';
12126 $famime = 'file-video';
12127 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12128 $mime = 'video/x-msvideo';
12129 $imgmime = 'video.png';
12130 $famime = 'file-video';
12131 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12132 $mime = 'video/divx';
12133 $imgmime = 'video.png';
12134 $famime = 'file-video';
12135 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12136 $mime = 'video/xvid';
12137 $imgmime = 'video.png';
12138 $famime = 'file-video';
12139 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12140 $mime = 'video';
12141 $imgmime = 'video.png';
12142 $famime = 'file-video';
12143 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12144 // application/xxx where zzz is zip, ...
12145 $mime = 'archive';
12146 $imgmime = 'archive.png';
12147 $famime = 'file-archive';
12148 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12149 $mime = 'application/octet-stream';
12150 $imgmime = 'other.png';
12151 $famime = 'file-o';
12152 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12153 $mime = 'library';
12154 $imgmime = 'library.png';
12155 $famime = 'file-o';
12156 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
12157 $mime = 'error';
12158 $imgmime = 'error.png';
12159 $famime = 'file-alt';
12160 }
12161
12162 // Return mimetype string
12163 switch ((int) $mode) {
12164 case 1:
12165 $tmp = explode('/', $mime);
12166 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12167 case 2:
12168 return $imgmime;
12169 case 3:
12170 return $srclang;
12171 case 4:
12172 return $famime;
12173 }
12174 return $mime;
12175}
12176
12188function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12189{
12190 global $conf, $db;
12191
12192 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12193
12194 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12195
12196 if (is_null($dictvalues)) {
12197 $dictvalues = array();
12198
12199 $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
12200 if ($checkentity) {
12201 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12202 }
12203
12204 $resql = $db->query($sql);
12205 if ($resql) {
12206 while ($obj = $db->fetch_object($resql)) {
12207 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12208 }
12209 } else {
12210 dol_print_error($db);
12211 }
12212
12213 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12214 }
12215
12216 if (!empty($dictvalues[$id])) {
12217 // Found
12218 $tmp = $dictvalues[$id];
12219 return (property_exists($tmp, $field) ? $tmp->$field : '');
12220 } else {
12221 // Not found
12222 return '';
12223 }
12224}
12225
12232function colorIsLight($stringcolor)
12233{
12234 $stringcolor = str_replace('#', '', $stringcolor);
12235 $res = -1;
12236 if (!empty($stringcolor)) {
12237 $res = 0;
12238 $tmp = explode(',', $stringcolor);
12239 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12240 $r = $tmp[0];
12241 $g = $tmp[1];
12242 $b = $tmp[2];
12243 } else {
12244 $hexr = $stringcolor[0].$stringcolor[1];
12245 $hexg = $stringcolor[2].$stringcolor[3];
12246 $hexb = $stringcolor[4].$stringcolor[5];
12247 $r = hexdec($hexr);
12248 $g = hexdec($hexg);
12249 $b = hexdec($hexb);
12250 }
12251 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12252 if ($bright > 0.6) {
12253 $res = 1;
12254 }
12255 }
12256 return $res;
12257}
12258
12267function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12268{
12269 global $conf;
12270
12271 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12272 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12273 if (empty($menuentry['enabled'])) {
12274 return 0; // Entry disabled by condition
12275 }
12276 if ($type_user && $menuentry['module']) {
12277 $tmploops = explode('|', $menuentry['module']);
12278 $found = 0;
12279 foreach ($tmploops as $tmploop) {
12280 if (in_array($tmploop, $listofmodulesforexternal)) {
12281 $found++;
12282 break;
12283 }
12284 }
12285 if (!$found) {
12286 return 0; // Entry is for menus all excluded to external users
12287 }
12288 }
12289 if (!$menuentry['perms'] && $type_user) {
12290 return 0; // No permissions and user is external
12291 }
12292 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12293 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12294 }
12295 if (!$menuentry['perms']) {
12296 return 2; // No permissions and user is external
12297 }
12298 return 1;
12299}
12300
12308function roundUpToNextMultiple($n, $x = 5)
12309{
12310 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12311 return (int) $result;
12312}
12313
12325function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12326{
12327 $csstouse = 'badge';
12328 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12329 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12330 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12331
12332 $attr = array(
12333 'class' => $csstouse
12334 );
12335
12336 if (empty($html)) {
12337 $html = $label;
12338 }
12339
12340 if (!empty($url)) {
12341 $attr['href'] = $url;
12342 }
12343
12344 if ($mode === 'dot') {
12345 $attr['class'] .= ' classfortooltip';
12346 $attr['title'] = $html;
12347 $attr['aria-label'] = $label;
12348 $html = '';
12349 }
12350
12351 // Override attr
12352 if (!empty($params['attr']) && is_array($params['attr'])) {
12353 foreach ($params['attr'] as $key => $value) {
12354 if ($key == 'class') {
12355 $attr['class'] .= ' '.$value;
12356 } elseif ($key == 'classOverride') {
12357 $attr['class'] = $value;
12358 } else {
12359 $attr[$key] = $value;
12360 }
12361 }
12362 }
12363
12364 // TODO: add hook
12365
12366 // escape all attribute
12367 $attr = array_map('dol_escape_htmltag', $attr);
12368
12369 $TCompiledAttr = array();
12370 foreach ($attr as $key => $value) {
12371 $TCompiledAttr[] = $key.'="'.$value.'"';
12372 }
12373
12374 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12375
12376 $tag = !empty($url) ? 'a' : 'span';
12377
12378 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12379}
12380
12381
12394function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12395{
12396 global $conf;
12397
12398 $return = '';
12399 $dolGetBadgeParams = array();
12400
12401 if (!empty($params['badgeParams'])) {
12402 $dolGetBadgeParams = $params['badgeParams'];
12403 }
12404
12405 // TODO : add a hook
12406 if ($displayMode == 0) {
12407 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12408 } elseif ($displayMode == 1) {
12409 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12410 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12411 // Use status with images (for backward compatibility)
12412 $return = '';
12413 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12414 $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>' : '');
12415
12416 // For small screen, we always use the short label instead of long label.
12417 if (!empty($conf->dol_optimize_smallscreen)) {
12418 if ($displayMode == 0) {
12419 $displayMode = 1;
12420 } elseif ($displayMode == 4) {
12421 $displayMode = 2;
12422 } elseif ($displayMode == 6) {
12423 $displayMode = 5;
12424 }
12425 }
12426
12427 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12428 $statusImg = array(
12429 'status0' => 'statut0',
12430 'status1' => 'statut1',
12431 'status2' => 'statut2',
12432 'status3' => 'statut3',
12433 'status4' => 'statut4',
12434 'status5' => 'statut5',
12435 'status6' => 'statut6',
12436 'status7' => 'statut7',
12437 'status8' => 'statut8',
12438 'status9' => 'statut9'
12439 );
12440
12441 if (!empty($statusImg[$statusType])) {
12442 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12443 } else {
12444 $htmlImg = img_picto($statusLabel, $statusType);
12445 }
12446
12447 if ($displayMode === 2) {
12448 $return = $htmlImg.' '.$htmlLabelShort;
12449 } elseif ($displayMode === 3) {
12450 $return = $htmlImg;
12451 } elseif ($displayMode === 4) {
12452 $return = $htmlImg.' '.$htmlLabel;
12453 } elseif ($displayMode === 5) {
12454 $return = $htmlLabelShort.' '.$htmlImg;
12455 } else { // $displayMode >= 6
12456 $return = $htmlLabel.' '.$htmlImg;
12457 }
12458 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12459 // Use new badge
12460 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12461
12462 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12463 if (empty($dolGetBadgeParams['attr']['title'])) {
12464 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12465 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12466 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12467 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
12468 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12469 }
12470
12471 if ($displayMode == 3) {
12472 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12473 } elseif ($displayMode === 5) {
12474 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12475 } else {
12476 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12477 }
12478 }
12479
12480 return $return;
12481}
12482
12483
12522function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12523{
12524 global $hookmanager, $action, $object, $langs;
12525
12526 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12527 if (is_array($url)) {
12528 // Loop on $url array to remove entries of disabled modules
12529 foreach ($url as $key => $subbutton) {
12530 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12531 unset($url[$key]);
12532 }
12533 }
12534
12535 $out = '';
12536
12537 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
12538 foreach ($url as $button) {
12539 if (!empty($button['lang'])) {
12540 $langs->load($button['lang']);
12541 }
12542 $label = $langs->trans($button['label']);
12543 $text = $button['text'] ?? '';
12544 $actionType = $button['actionType'] ?? '';
12545 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12546 $id = $button['id'] ?? '';
12547 $userRight = $button['perm'] ?? 1;
12548 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
12549
12550 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12551 }
12552 return $out;
12553 }
12554
12555 if (count($url) > 1) {
12556 $out .= '<div class="dropdown inline-block dropdown-holder">';
12557 $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>';
12558 $out .= '<div class="dropdown-content">';
12559 foreach ($url as $subbutton) {
12560 if (!empty($subbutton['lang'])) {
12561 $langs->load($subbutton['lang']);
12562 }
12563
12564 if (!empty($subbutton['urlraw'])) {
12565 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12566 } else {
12567 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12568 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12569 }
12570
12571 $subbuttonparam = array();
12572 if (!empty($subbutton['attr'])) {
12573 $subbuttonparam['attr'] = $subbutton['attr'];
12574 }
12575 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
12576
12577 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12578 }
12579 $out .= "</div>";
12580 $out .= "</div>";
12581 } else {
12582 foreach ($url as $subbutton) { // Should loop on 1 record only
12583 if (!empty($subbutton['lang'])) {
12584 $langs->load($subbutton['lang']);
12585 }
12586
12587 if (!empty($subbutton['urlraw'])) {
12588 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12589 } else {
12590 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12591 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12592 }
12593
12594 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12595 }
12596 }
12597
12598 return $out;
12599 }
12600
12601 // Here, $url is a simple link
12602
12603 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12604 $class = "dropdown-item";
12605 } else {
12606 $class = 'butAction';
12607 if ($actionType == 'danger' || $actionType == 'delete') {
12608 $class = 'butActionDelete';
12609 if (!empty($url) && strpos($url, 'token=') === false) {
12610 $url .= '&token='.newToken();
12611 }
12612 }
12613 }
12614 $attr = array(
12615 'class' => $class,
12616 'href' => empty($url) ? '' : $url,
12617 'title' => $label
12618 );
12619
12620 if (empty($text)) {
12621 $text = $label;
12622 $attr['title'] = ''; // if html not set, leave label on title is redundant
12623 } else {
12624 $attr['title'] = $label;
12625 $attr['aria-label'] = $label;
12626 }
12627
12628 if (empty($userRight)) {
12629 $attr['class'] = 'butActionRefused';
12630 $attr['href'] = '';
12631 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12632 $attr['title'] = ($attr['title'] ? $attr['title'].'<br>' : '').$langs->trans('NotEnoughPermissions');
12633 }
12634
12635 if (!empty($id)) {
12636 $attr['id'] = $id;
12637 }
12638
12639 // Override attr
12640 if (!empty($params['attr']) && is_array($params['attr'])) {
12641 foreach ($params['attr'] as $key => $value) {
12642 if ($key == 'class') {
12643 $attr['class'] .= ' '.$value;
12644 } elseif ($key == 'classOverride') {
12645 $attr['class'] = $value;
12646 } else {
12647 $attr[$key] = $value;
12648 }
12649 }
12650 }
12651
12652 // automatic add tooltip when title is detected
12653 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12654 $attr['class'] .= ' classfortooltip';
12655 }
12656
12657 // Js Confirm button
12658 if ($userRight && !empty($params['confirm'])) {
12659 if (!is_array($params['confirm'])) {
12660 $params['confirm'] = array();
12661 }
12662
12663 if (empty($params['confirm']['url'])) {
12664 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12665 }
12666
12667 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12668 $attr['data-confirm-url'] = $params['confirm']['url'];
12669 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12670 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12671 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12672 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12673 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12674 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12675
12676 $attr['class'] .= ' butActionConfirm';
12677 }
12678
12679 if (isset($attr['href']) && empty($attr['href'])) {
12680 unset($attr['href']);
12681 }
12682
12683 // escape all attribute
12684 $attr = array_map('dol_escape_htmltag', $attr);
12685
12686 $TCompiledAttr = array();
12687 foreach ($attr as $key => $value) {
12688 $TCompiledAttr[] = $key.'= "'.$value.'"';
12689 }
12690
12691 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12692
12693 $tag = !empty($attr['href']) ? 'a' : 'span';
12694
12695
12696 $parameters = array(
12697 'TCompiledAttr' => $TCompiledAttr, // array
12698 'compiledAttributes' => $compiledAttributes, // string
12699 'attr' => $attr,
12700 'tag' => $tag,
12701 'label' => $label,
12702 'html' => $text,
12703 'actionType' => $actionType,
12704 'url' => $url,
12705 'id' => $id,
12706 'userRight' => $userRight,
12707 'params' => $params
12708 );
12709
12710 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12711 if ($reshook < 0) {
12712 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12713 }
12714
12715 if (empty($reshook)) {
12716 if (dol_textishtml($text)) { // If content already HTML encoded
12717 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
12718 } else {
12719 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
12720 }
12721 } else {
12722 return $hookmanager->resPrint;
12723 }
12724}
12725
12726
12735function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
12736{
12737 if (empty($url)) {
12738 return '';
12739 }
12740
12741 $parsedUrl = parse_url($url);
12742 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
12743 return $url;
12744 }
12745
12746 if (!empty($parsedUrl['query'])) {
12747 // Use parse_str() function to parse the string passed via URL
12748 parse_str($parsedUrl['query'], $urlQuery);
12749 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
12750 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
12751 }
12752 }
12753
12754 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
12755 $url = DOL_URL_ROOT.$url;
12756 }
12757
12758 return $url;
12759}
12760
12761
12768function dolGetButtonTitleSeparator($moreClass = "")
12769{
12770 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12771}
12772
12779function getFieldErrorIcon($fieldValidationErrorMsg)
12780{
12781 $out = '';
12782 if (!empty($fieldValidationErrorMsg)) {
12783 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12784 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12785 $out .= '</span>';
12786 }
12787
12788 return $out;
12789}
12790
12803function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12804{
12805 global $langs, $conf, $user;
12806
12807 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12808 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12809 return '';
12810 }
12811
12812 $class = 'btnTitle';
12813 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12814 $class .= ' btnTitlePlus';
12815 }
12816 $useclassfortooltip = 1;
12817
12818 if (!empty($params['morecss'])) {
12819 $class .= ' '.$params['morecss'];
12820 }
12821
12822 $attr = array(
12823 'class' => $class,
12824 'href' => empty($url) ? '' : $url
12825 );
12826
12827 if (!empty($helpText)) {
12828 $attr['title'] = dol_escape_htmltag($helpText);
12829 } elseif ($label) { // empty($attr['title']) &&
12830 $attr['title'] = $label;
12831 $useclassfortooltip = 0;
12832 }
12833
12834 if ($status == 2) {
12835 $attr['class'] .= ' btnTitleSelected';
12836 } elseif ($status <= 0) {
12837 $attr['class'] .= ' refused';
12838
12839 $attr['href'] = '';
12840
12841 if ($status == -1) { // disable
12842 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12843 } elseif ($status == 0) { // Not enough permissions
12844 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12845 }
12846 }
12847
12848 if (!empty($attr['title']) && $useclassfortooltip) {
12849 $attr['class'] .= ' classfortooltip';
12850 }
12851
12852 if (!empty($id)) {
12853 $attr['id'] = $id;
12854 }
12855
12856 // Override attr
12857 if (!empty($params['attr']) && is_array($params['attr'])) {
12858 foreach ($params['attr'] as $key => $value) {
12859 if ($key == 'class') {
12860 $attr['class'] .= ' '.$value;
12861 } elseif ($key == 'classOverride') {
12862 $attr['class'] = $value;
12863 } else {
12864 $attr[$key] = $value;
12865 }
12866 }
12867 }
12868
12869 if (isset($attr['href']) && empty($attr['href'])) {
12870 unset($attr['href']);
12871 }
12872
12873 // TODO : add a hook
12874
12875 $TCompiledAttr = array();
12876 foreach ($attr as $key => $value) {
12877 $TCompiledAttr[] = $key.'="'.dolPrintHTMLForAttribute($value).'"';
12878 }
12879
12880 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12881
12882 $tag = (empty($attr['href']) ? 'span' : 'a');
12883
12884 $button = '<'.$tag.' '.$compiledAttributes.'>';
12885 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
12886 if (!empty($params['forcenohideoftext'])) {
12887 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
12888 }
12889 $button .= '</'.$tag.'>';
12890
12891 return $button;
12892}
12893
12903function getElementProperties($elementType)
12904{
12905 global $conf, $db, $hookmanager;
12906
12907 $regs = array();
12908
12909 //$element_type='facture';
12910
12911 $classfile = $classname = $classpath = $subdir = $dir_output = $parent_element = '';
12912
12913 // Parse element/subelement
12914 $module = $elementType;
12915 $element = $elementType;
12916 $subelement = $elementType;
12917 $table_element = $elementType;
12918
12919 // If we ask a resource form external module (instead of default path)
12920 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
12921 $element = $subelement = $regs[1];
12922 $module = $regs[2];
12923 }
12924
12925 // If we ask a resource for a string with an element and a subelement
12926 // Example 'project_task'
12927 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
12928 $module = $element = $regs[1];
12929 $subelement = $regs[2];
12930 }
12931
12932 // Object lines will use parent classpath and module ref
12933 if (substr($elementType, -3) == 'det') {
12934 $module = preg_replace('/det$/', '', $element);
12935 $subelement = preg_replace('/det$/', '', $subelement);
12936 $classpath = $module.'/class';
12937 $classfile = $module;
12938 $classname = preg_replace('/det$/', 'Line', $element);
12939 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
12940 $classname = preg_replace('/det$/', 'Ligne', $element);
12941 }
12942 }
12943 // For compatibility and to work with non standard path
12944 if ($elementType == "action" || $elementType == "actioncomm") {
12945 $classpath = 'comm/action/class';
12946 $subelement = 'Actioncomm';
12947 $module = 'agenda';
12948 $table_element = 'actioncomm';
12949 } elseif ($elementType == 'cronjob') {
12950 $classpath = 'cron/class';
12951 $module = 'cron';
12952 $table_element = 'cron';
12953 } elseif ($elementType == 'adherent_type') {
12954 $classpath = 'adherents/class';
12955 $classfile = 'adherent_type';
12956 $module = 'adherent';
12957 $subelement = 'adherent_type';
12958 $classname = 'AdherentType';
12959 $table_element = 'adherent_type';
12960 } elseif ($elementType == 'bank_account') {
12961 $classpath = 'compta/bank/class';
12962 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
12963 $classfile = 'account';
12964 $classname = 'Account';
12965 } elseif ($elementType == 'category') {
12966 $classpath = 'categories/class';
12967 $module = 'categorie';
12968 $subelement = 'categorie';
12969 $table_element = 'categorie';
12970 } elseif ($elementType == 'contact') {
12971 $classpath = 'contact/class';
12972 $classfile = 'contact';
12973 $module = 'societe';
12974 $subelement = 'contact';
12975 $table_element = 'socpeople';
12976 } elseif ($elementType == 'inventory') {
12977 $module = 'product';
12978 $classpath = 'product/inventory/class';
12979 } elseif ($elementType == 'inventoryline') {
12980 $module = 'product';
12981 $classpath = 'product/inventory/class';
12982 $table_element = 'inventorydet';
12983 $parent_element = 'inventory';
12984 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
12985 $module = 'stock';
12986 $classpath = 'product/stock/class';
12987 $classfile = 'entrepot';
12988 $classname = 'Entrepot';
12989 $table_element = 'entrepot';
12990 } elseif ($elementType == 'project') {
12991 $classpath = 'projet/class';
12992 $module = 'projet';
12993 $table_element = 'projet';
12994 } elseif ($elementType == 'project_task') {
12995 $classpath = 'projet/class';
12996 $module = 'projet';
12997 $subelement = 'task';
12998 $table_element = 'projet_task';
12999 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13000 $classpath = 'compta/facture/class';
13001 $module = 'facture';
13002 $subelement = 'facture';
13003 $table_element = 'facture';
13004 } elseif ($elementType == 'facturedet') {
13005 $classpath = 'compta/facture/class';
13006 $classfile = 'facture';
13007 $classname = 'FactureLigne';
13008 $module = 'facture';
13009 $table_element = 'facturedet';
13010 $parent_element = 'facture';
13011 } elseif ($elementType == 'facturerec') {
13012 $classpath = 'compta/facture/class';
13013 $module = 'facture';
13014 $classname = 'FactureRec';
13015 } elseif ($elementType == 'commande' || $elementType == 'order') {
13016 $classpath = 'commande/class';
13017 $module = 'commande';
13018 $subelement = 'commande';
13019 $table_element = 'commande';
13020 } elseif ($elementType == 'commandedet') {
13021 $classpath = 'commande/class';
13022 $classfile = 'commande';
13023 $classname = 'OrderLine';
13024 $module = 'commande';
13025 $table_element = 'commandedet';
13026 $parent_element = 'commande';
13027 } elseif ($elementType == 'propal') {
13028 $classpath = 'comm/propal/class';
13029 $table_element = 'propal';
13030 } elseif ($elementType == 'propaldet') {
13031 $classpath = 'comm/propal/class';
13032 $classfile = 'propal';
13033 $subelement = 'propaleligne';
13034 $module = 'propal';
13035 $table_element = 'propaldet';
13036 $parent_element = 'propal';
13037 } elseif ($elementType == 'shipping') {
13038 $classpath = 'expedition/class';
13039 $classfile = 'expedition';
13040 $classname = 'Expedition';
13041 $module = 'expedition';
13042 $table_element = 'expedition';
13043 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13044 $classpath = 'expedition/class';
13045 $classfile = 'expedition';
13046 $classname = 'ExpeditionLigne';
13047 $module = 'expedition';
13048 $table_element = 'expeditiondet';
13049 $parent_element = 'expedition';
13050 } elseif ($elementType == 'delivery_note') {
13051 $classpath = 'delivery/class';
13052 $subelement = 'delivery';
13053 $module = 'expedition';
13054 } elseif ($elementType == 'delivery') {
13055 $classpath = 'delivery/class';
13056 $subelement = 'delivery';
13057 $module = 'expedition';
13058 } elseif ($elementType == 'deliverydet') {
13059 // @todo
13060 } elseif ($elementType == 'supplier_proposal') {
13061 $classpath = 'supplier_proposal/class';
13062 $module = 'supplier_proposal';
13063 $element = 'supplierproposal';
13064 $classfile = 'supplier_proposal';
13065 $subelement = 'supplierproposal';
13066 } elseif ($elementType == 'supplier_proposaldet') {
13067 $classpath = 'supplier_proposal/class';
13068 $module = 'supplier_proposal';
13069 $classfile = 'supplier_proposal';
13070 $table_element = 'supplier_proposaldet';
13071 $parent_element = 'supplier_proposal';
13072 } elseif ($elementType == 'contract') {
13073 $classpath = 'contrat/class';
13074 $module = 'contrat';
13075 $subelement = 'contrat';
13076 $table_element = 'contract';
13077 } elseif ($elementType == 'contratdet') {
13078 $classpath = 'contrat/class';
13079 $module = 'contrat';
13080 $table_element = 'contratdet';
13081 $parent_element = 'contrat';
13082 } elseif ($elementType == 'mailing') {
13083 $classpath = 'comm/mailing/class';
13084 $module = 'mailing';
13085 $classfile = 'mailing';
13086 $classname = 'Mailing';
13087 $subelement = '';
13088 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13089 $classpath = 'adherents/class';
13090 $module = 'adherent';
13091 $subelement = 'adherent';
13092 $table_element = 'adherent';
13093 } elseif ($elementType == 'usergroup') {
13094 $classpath = 'user/class';
13095 $module = 'user';
13096 } elseif ($elementType == 'mo') {
13097 $classpath = 'mrp/class';
13098 $classfile = 'mo';
13099 $classname = 'Mo';
13100 $module = 'mrp';
13101 $subelement = '';
13102 $table_element = 'mrp_mo';
13103 } elseif ($elementType == 'mrp_production') {
13104 $classpath = 'mrp/class';
13105 $classfile = 'mo';
13106 $classname = 'MoLine';
13107 $module = 'mrp';
13108 $subelement = '';
13109 $table_element = 'mrp_production';
13110 $parent_element = 'mo';
13111 } elseif ($elementType == 'cabinetmed_cons') {
13112 $classpath = 'cabinetmed/class';
13113 $module = 'cabinetmed';
13114 $subelement = 'cabinetmedcons';
13115 $table_element = 'cabinetmedcons';
13116 } elseif ($elementType == 'fichinter') {
13117 $classpath = 'fichinter/class';
13118 $module = 'ficheinter';
13119 $subelement = 'fichinter';
13120 $table_element = 'fichinter';
13121 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13122 $classpath = 'resource/class';
13123 $module = 'resource';
13124 $subelement = 'dolresource';
13125 $table_element = 'resource';
13126 } elseif ($elementType == 'opensurvey_sondage') {
13127 $classpath = 'opensurvey/class';
13128 $module = 'opensurvey';
13129 $subelement = 'opensurveysondage';
13130 } elseif ($elementType == 'order_supplier' || $elementType == 'commande_fournisseur') {
13131 $classpath = 'fourn/class';
13132 $module = 'fournisseur';
13133 $classfile = 'fournisseur.commande';
13134 $element = 'order_supplier';
13135 $subelement = '';
13136 $classname = 'CommandeFournisseur';
13137 $table_element = 'commande_fournisseur';
13138 } elseif ($elementType == 'commande_fournisseurdet') {
13139 $classpath = 'fourn/class';
13140 $module = 'fournisseur';
13141 $classfile = 'fournisseur.commande';
13142 $element = 'commande_fournisseurdet';
13143 $subelement = '';
13144 $classname = 'CommandeFournisseurLigne';
13145 $table_element = 'commande_fournisseurdet';
13146 $parent_element = 'commande_fournisseur';
13147 } elseif ($elementType == 'invoice_supplier') {
13148 $classpath = 'fourn/class';
13149 $module = 'fournisseur';
13150 $classfile = 'fournisseur.facture';
13151 $element = 'invoice_supplier';
13152 $subelement = '';
13153 $classname = 'FactureFournisseur';
13154 $table_element = 'facture_fourn';
13155 } elseif ($elementType == 'facture_fourn_det') {
13156 $classpath = 'fourn/class';
13157 $module = 'fournisseur';
13158 $classfile = 'fournisseur.facture';
13159 $element = 'facture_fourn_det';
13160 $subelement = '';
13161 $classname = 'SupplierInvoiceLine';
13162 $table_element = 'facture_fourn_det';
13163 $parent_element = 'invoice_supplier';
13164 } elseif ($elementType == "service") {
13165 $classpath = 'product/class';
13166 $subelement = 'product';
13167 $table_element = 'product';
13168 } elseif ($elementType == 'salary') {
13169 $classpath = 'salaries/class';
13170 $module = 'salaries';
13171 } elseif ($elementType == 'payment_salary') {
13172 $classpath = 'salaries/class';
13173 $classfile = 'paymentsalary';
13174 $classname = 'PaymentSalary';
13175 $module = 'salaries';
13176 } elseif ($elementType == 'productlot') {
13177 $module = 'productbatch';
13178 $classpath = 'product/stock/class';
13179 $classfile = 'productlot';
13180 $classname = 'Productlot';
13181 $element = 'productlot';
13182 $subelement = '';
13183 $table_element = 'product_lot';
13184 } elseif ($elementType == 'societeaccount') {
13185 $classpath = 'societe/class';
13186 $classfile = 'societeaccount';
13187 $classname = 'SocieteAccount';
13188 $module = 'societe';
13189 } elseif ($elementType == 'websitepage') {
13190 $classpath = 'website/class';
13191 $classfile = 'websitepage';
13192 $classname = 'Websitepage';
13193 $module = 'website';
13194 $subelement = 'websitepage';
13195 $table_element = 'website_page';
13196 } elseif ($elementType == 'fiscalyear') {
13197 $classpath = 'core/class';
13198 $module = 'accounting';
13199 $subelement = 'fiscalyear';
13200 } elseif ($elementType == 'chargesociales') {
13201 $classpath = 'compta/sociales/class';
13202 $module = 'tax';
13203 $table_element = 'chargesociales';
13204 } elseif ($elementType == 'tva') {
13205 $classpath = 'compta/tva/class';
13206 $module = 'tax';
13207 $subdir = '/vat';
13208 $table_element = 'tva';
13209 } elseif ($elementType == 'emailsenderprofile') {
13210 $module = '';
13211 $classpath = 'core/class';
13212 $classfile = 'emailsenderprofile';
13213 $classname = 'EmailSenderProfile';
13214 $table_element = 'c_email_senderprofile';
13215 $subelement = '';
13216 } elseif ($elementType == 'conferenceorboothattendee') {
13217 $classpath = 'eventorganization/class';
13218 $classfile = 'conferenceorboothattendee';
13219 $classname = 'ConferenceOrBoothAttendee';
13220 $module = 'eventorganization';
13221 } elseif ($elementType == 'conferenceorbooth') {
13222 $classpath = 'eventorganization/class';
13223 $classfile = 'conferenceorbooth';
13224 $classname = 'ConferenceOrBooth';
13225 $module = 'eventorganization';
13226 } elseif ($elementType == 'ccountry') {
13227 $module = '';
13228 $classpath = 'core/class';
13229 $classfile = 'ccountry';
13230 $classname = 'Ccountry';
13231 $table_element = 'c_country';
13232 $subelement = '';
13233 } elseif ($elementType == 'knowledgerecord') {
13234 $module = '';
13235 $classpath = 'knowledgemanagement/class';
13236 $classfile = 'knowledgerecord';
13237 $classname = 'KnowledgeRecord';
13238 $table_element = 'knowledgemanagement_knowledgerecord';
13239 $subelement = '';
13240 }
13241
13242 if (empty($classfile)) {
13243 $classfile = strtolower($subelement);
13244 }
13245 if (empty($classname)) {
13246 $classname = ucfirst($subelement);
13247 }
13248 if (empty($classpath)) {
13249 $classpath = $module.'/class';
13250 }
13251
13252 //print 'getElementProperties subdir='.$subdir;
13253
13254 // Set dir_output
13255 if ($module && isset($conf->$module)) { // The generic case
13256 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13257 $dir_output = $conf->$module->multidir_output[$conf->entity];
13258 } elseif (!empty($conf->$module->output[$conf->entity])) {
13259 $dir_output = $conf->$module->output[$conf->entity];
13260 } elseif (!empty($conf->$module->dir_output)) {
13261 $dir_output = $conf->$module->dir_output;
13262 }
13263 }
13264
13265 // Overwrite value for special cases
13266 if ($element == 'order_supplier') {
13267 $dir_output = $conf->fournisseur->commande->dir_output;
13268 } elseif ($element == 'invoice_supplier') {
13269 $dir_output = $conf->fournisseur->facture->dir_output;
13270 }
13271 $dir_output .= $subdir;
13272
13273 $elementProperties = array(
13274 'module' => $module,
13275 'element' => $element,
13276 'table_element' => $table_element,
13277 'subelement' => $subelement,
13278 'classpath' => $classpath,
13279 'classfile' => $classfile,
13280 'classname' => $classname,
13281 'dir_output' => $dir_output,
13282 'parent_element' => $parent_element,
13283 );
13284
13285
13286 // Add hook
13287 if (!is_object($hookmanager)) {
13288 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13289 $hookmanager = new HookManager($db);
13290 }
13291 $hookmanager->initHooks(array('elementproperties'));
13292
13293
13294 // Hook params
13295 $parameters = array(
13296 'elementType' => $elementType,
13297 'elementProperties' => $elementProperties
13298 );
13299
13300 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13301
13302 if ($reshook) {
13303 $elementProperties = $hookmanager->resArray;
13304 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13305 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13306 }
13307
13308 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13309 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13310 unset($hookmanager->contextarray[$key]);
13311 }
13312
13313 return $elementProperties;
13314}
13315
13328function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13329{
13330 global $db, $conf;
13331
13332 $ret = 0;
13333
13334 $element_prop = getElementProperties($element_type);
13335
13336 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13337 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13338 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13339 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13340 // of service and we will return properties of a product.
13341 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13342 } elseif ($element_prop['module'] == 'societeaccount') {
13343 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13344 } else {
13345 $ismodenabled = isModEnabled($element_prop['module']);
13346 }
13347 //var_dump('element_type='.$element_type);
13348 //var_dump($element_prop);
13349 //var_dump($element_prop['module'].' '.$ismodenabled);
13350 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13351 if ($useCache === 1
13352 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13353 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13354 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13355 ) {
13356 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13357 }
13358
13359 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13360
13361 if (class_exists($element_prop['classname'])) {
13362 $className = $element_prop['classname'];
13363 $objecttmp = new $className($db);
13364 '@phan-var-force CommonObject $objecttmp';
13365
13366 if ($element_id > 0 || !empty($element_ref)) {
13367 $ret = $objecttmp->fetch($element_id, $element_ref);
13368 if ($ret >= 0) {
13369 if (empty($objecttmp->module)) {
13370 $objecttmp->module = $element_prop['module'];
13371 }
13372
13373 if ($useCache > 0) {
13374 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13375 $conf->cache['fetchObjectByElement'][$element_type] = [];
13376 }
13377
13378 // Manage cache limit
13379 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13380 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13381 }
13382
13383 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13384 }
13385
13386 return $objecttmp;
13387 }
13388 } else {
13389 return $objecttmp; // returned an object without fetch
13390 }
13391 } else {
13392 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13393 return -1;
13394 }
13395 }
13396
13397 return $ret;
13398}
13399
13406function isAFileWithExecutableContent($filename)
13407{
13408 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)) {
13409 return true;
13410 }
13411
13412 return false;
13413}
13414
13422function newToken()
13423{
13424 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13425}
13426
13434function currentToken()
13435{
13436 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13437}
13438
13444function getNonce()
13445{
13446 global $conf;
13447
13448 if (empty($conf->cache['nonce'])) {
13449 $conf->cache['nonce'] = dolGetRandomBytes(8);
13450 }
13451
13452 return $conf->cache['nonce'];
13453}
13454
13455
13469function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13470{
13471 global $langs;
13472
13473 print '<div class="div-table-responsive-no-min">';
13474 print '<table class="noborder centpercent">';
13475 print '<tr class="liste_titre">';
13476
13477 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13478
13479 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13480
13481 if (!empty($link)) {
13482 if (!empty($arguments)) {
13483 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13484 } else {
13485 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13486 }
13487 }
13488
13489 if ($number > -1) {
13490 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13491 } elseif (!empty($link)) {
13492 print '<span class="badge marginleftonlyshort">...</span>';
13493 }
13494
13495 if (!empty($link)) {
13496 print '</a>';
13497 }
13498
13499 print '</th>';
13500
13501 if ($number < 0 && !empty($link)) {
13502 print '<th class="right">';
13503 print '</th>';
13504 }
13505
13506 print '</tr>';
13507}
13508
13517function finishSimpleTable($addLineBreak = false)
13518{
13519 print '</table>';
13520 print '</div>';
13521
13522 if ($addLineBreak) {
13523 print '<br>';
13524 }
13525}
13526
13538function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13539{
13540 global $langs;
13541
13542 if ($num === 0) {
13543 print '<tr class="oddeven">';
13544 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13545 print '</tr>';
13546 return;
13547 }
13548
13549 if ($nbofloop === 0) {
13550 // don't show a summary line
13551 return;
13552 }
13553
13554 /* Case already handled above, commented to satisfy phpstan.
13555 if ($num === 0) {
13556 $colspan = $tableColumnCount;
13557 } else
13558 */
13559 if ($num > $nbofloop) {
13560 $colspan = $tableColumnCount;
13561 } else {
13562 $colspan = $tableColumnCount - 1;
13563 }
13564
13565 if ($extraRightColumn) {
13566 $colspan--;
13567 }
13568
13569 print '<tr class="liste_total">';
13570
13571 if ($nbofloop > 0 && $num > $nbofloop) {
13572 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13573 } else {
13574 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13575 print '<td class="right centpercent">'.price($total).'</td>';
13576 }
13577
13578 if ($extraRightColumn) {
13579 print '<td></td>';
13580 }
13581
13582 print '</tr>';
13583}
13584
13593function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13594{
13595 if ($method == -1) {
13596 $method = 0;
13597 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13598 $method = 1;
13599 }
13600 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13601 $method = 2;
13602 }
13603 }
13604
13605 // Be sure we don't have output buffering enabled to have readfile working correctly
13606 while (ob_get_level()) {
13607 ob_end_flush();
13608 }
13609
13610 // Solution 0
13611 if ($method == 0) {
13612 readfile($fullpath_original_file_osencoded);
13613 } elseif ($method == 1) {
13614 // Solution 1
13615 $handle = fopen($fullpath_original_file_osencoded, "rb");
13616 while (!feof($handle)) {
13617 print fread($handle, 8192);
13618 }
13619 fclose($handle);
13620 } elseif ($method == 2) {
13621 // Solution 2
13622 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13623 $handle2 = fopen("php://output", "wb");
13624 stream_copy_to_stream($handle1, $handle2);
13625 fclose($handle1);
13626 fclose($handle2);
13627 }
13628}
13629
13639function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13640{
13641 global $langs;
13642
13643 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13644
13645 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
13646 if ($texttoshow === 'none') {
13647 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13648 $result .= '<span class="clipboardCPValueToPrint"></span>';
13649 } elseif ($texttoshow) {
13650 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13651 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
13652 } else {
13653 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13654 }
13655 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
13656 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
13657 $result .= '<span class="clipboardCPText"></span>';
13658 $result .= '</span>';
13659
13660 return $result;
13661}
13662
13663
13670function jsonOrUnserialize($stringtodecode)
13671{
13672 $result = json_decode($stringtodecode);
13673 if ($result === null) {
13674 $result = unserialize($stringtodecode);
13675 }
13676
13677 return $result;
13678}
13679
13680
13697function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13698{
13699 global $db, $user;
13700
13701 if (is_null($filter) || !is_string($filter) || $filter === '') {
13702 return '';
13703 }
13704 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13705 $filter = '(' . $filter . ')';
13706 }
13707
13708 $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'
13709 $firstandlastparenthesis = 0;
13710
13711 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13712 if ($noerror) {
13713 return '1 = 2';
13714 } else {
13715 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13716 }
13717 }
13718
13719 // Test the filter syntax
13720 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13721 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
13722 // If the string result contains something else than '()', the syntax was wrong
13723
13724 if (preg_match('/[^\‍(\‍)]/', $t)) {
13725 $tmperrorstr = 'Bad syntax of the search string';
13726 $errorstr = 'Bad syntax of the search string: '.$filter;
13727 if ($noerror) {
13728 return '1 = 2';
13729 } else {
13730 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
13731 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13732 }
13733 }
13734
13735 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
13736
13737 if (is_object($db)) {
13738 $ret = str_replace('__NOW__', $db->idate(dol_now()), $ret);
13739 }
13740 if (is_object($user)) {
13741 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13742 }
13743
13744 return $ret;
13745}
13746
13754function dolForgeExplodeAnd($sqlfilters)
13755{
13756 $arrayofandtags = array();
13757 $nbofchars = dol_strlen($sqlfilters);
13758
13759 $error = '';
13760 $parenthesislevel = 0;
13761 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13762 if (!$result) {
13763 return array();
13764 }
13765 if ($parenthesislevel >= 1) {
13766 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13767 }
13768
13769 $i = 0;
13770 $s = '';
13771 $countparenthesis = 0;
13772 while ($i < $nbofchars) {
13773 $char = dol_substr($sqlfilters, $i, 1);
13774
13775 if ($char == '(') {
13776 $countparenthesis++;
13777 } elseif ($char == ')') {
13778 $countparenthesis--;
13779 }
13780
13781 if ($countparenthesis == 0) {
13782 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13783 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13784 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13785 // We found a AND
13786 $s = trim($s);
13787 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13788 $s = '('.$s.')';
13789 }
13790 $arrayofandtags[] = $s;
13791 $s = '';
13792 $i += 2;
13793 } else {
13794 $s .= $char;
13795 }
13796 } else {
13797 $s .= $char;
13798 }
13799 $i++;
13800 }
13801 if ($s) {
13802 $s = trim($s);
13803 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13804 $s = '('.$s.')';
13805 }
13806 $arrayofandtags[] = $s;
13807 }
13808
13809 return $arrayofandtags;
13810}
13811
13821function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13822{
13823 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13824 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13825 $tmp = $sqlfilters;
13826
13827 $nb = dol_strlen($tmp);
13828 $counter = 0;
13829 $parenthesislevel = 0;
13830
13831 $error = '';
13832
13833 $i = 0;
13834 while ($i < $nb) {
13835 $char = dol_substr($tmp, $i, 1);
13836
13837 if ($char == '(') {
13838 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13839 // We open a parenthesis and it is the first char
13840 $parenthesislevel++;
13841 }
13842 $counter++;
13843 } elseif ($char == ')') {
13844 $nbcharremaining = ($nb - $i - 1);
13845 if ($nbcharremaining >= $counter) {
13846 $parenthesislevel = min($parenthesislevel, $counter - 1);
13847 }
13848 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13849 $parenthesislevel = $counter;
13850 }
13851 $counter--;
13852 }
13853
13854 if ($counter < 0) {
13855 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13856 $parenthesislevel = 0;
13857 dol_syslog($error, LOG_WARNING);
13858 return false;
13859 }
13860
13861 $i++;
13862 }
13863
13864 if ($counter > 0) {
13865 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13866 $parenthesislevel = 0;
13867 dol_syslog($error, LOG_WARNING);
13868 return false;
13869 }
13870
13871 return true;
13872}
13873
13881function dolForgeDummyCriteriaCallback($matches)
13882{
13883 //dol_syslog("Convert matches ".$matches[1]);
13884 if (empty($matches[1])) {
13885 return '';
13886 }
13887 $tmp = explode(':', $matches[1]);
13888 if (count($tmp) < 3) {
13889 return '';
13890 }
13891
13892 return '()'; // An empty criteria
13893}
13894
13903function dolForgeCriteriaCallback($matches)
13904{
13905 global $db;
13906
13907 //dol_syslog("Convert matches ".$matches[1]);
13908 if (empty($matches[1])) {
13909 return '';
13910 }
13911 $tmp = explode(':', $matches[1], 3);
13912 if (count($tmp) < 3) {
13913 return '';
13914 }
13915
13916 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
13917
13918 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
13919
13920 $realOperator = [
13921 'NOTLIKE' => 'NOT LIKE',
13922 'ISNOT' => 'IS NOT',
13923 'NOTIN' => 'NOT IN',
13924 '!=' => '<>',
13925 ];
13926
13927 if (array_key_exists($operator, $realOperator)) {
13928 $operator = $realOperator[$operator];
13929 }
13930
13931 $tmpescaped = $tmp[2];
13932
13933 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
13934
13935 $regbis = array();
13936
13937 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
13938 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
13939 $tmpescaped2 = '(';
13940 // Explode and sanitize each element in list
13941 $tmpelemarray = explode(',', $tmpescaped);
13942 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
13943 $reg = array();
13944 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
13945 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
13946 } else {
13947 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
13948 }
13949 }
13950 $tmpescaped2 .= implode(',', $tmpelemarray);
13951 $tmpescaped2 .= ')';
13952
13953 $tmpescaped = $tmpescaped2;
13954 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
13955 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
13956 $tmpescaped = $regbis[1];
13957 }
13958 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
13959 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
13960 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
13961 // TODO Retrieve type of field for $operand field name.
13962 // So we can complete format. For example we could complete a year with month and day.
13963 $tmpescaped = "'".$db->escape($regbis[1])."'";
13964 } else {
13965 if (strtoupper($tmpescaped) == 'NULL') {
13966 $tmpescaped = 'NULL';
13967 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
13968 $tmpescaped = (int) $tmpescaped;
13969 } else {
13970 $tmpescaped = (float) $tmpescaped;
13971 }
13972 }
13973
13974 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
13975}
13976
13977
13987function getTimelineIcon($actionstatic, &$histo, $key)
13988{
13989 global $langs;
13990
13991 $out = '<!-- timeline icon -->'."\n";
13992 $iconClass = 'fa fa-comments';
13993 $img_picto = '';
13994 $colorClass = '';
13995 $pictoTitle = '';
13996
13997 if ($histo[$key]['percent'] == -1) {
13998 $colorClass = 'timeline-icon-not-applicble';
13999 $pictoTitle = $langs->trans('StatusNotApplicable');
14000 } elseif ($histo[$key]['percent'] == 0) {
14001 $colorClass = 'timeline-icon-todo';
14002 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14003 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14004 $colorClass = 'timeline-icon-in-progress';
14005 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14006 } elseif ($histo[$key]['percent'] >= 100) {
14007 $colorClass = 'timeline-icon-done';
14008 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14009 }
14010
14011 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14012 $iconClass = 'fa fa-ticket';
14013 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14014 $iconClass = 'fa fa-pencilxxx';
14015 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14016 $iconClass = 'fa fa-comments';
14017 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14018 $iconClass = 'fa fa-mask';
14019 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14020 if ($actionstatic->type_picto) {
14021 $img_picto = img_picto('', $actionstatic->type_picto);
14022 } else {
14023 if ($actionstatic->type_code == 'AC_RDV') {
14024 $iconClass = 'fa fa-handshake';
14025 } elseif ($actionstatic->type_code == 'AC_TEL') {
14026 $iconClass = 'fa fa-phone';
14027 } elseif ($actionstatic->type_code == 'AC_FAX') {
14028 $iconClass = 'fa fa-fax';
14029 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14030 $iconClass = 'fa fa-envelope';
14031 } elseif ($actionstatic->type_code == 'AC_INT') {
14032 $iconClass = 'fa fa-shipping-fast';
14033 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14034 $iconClass = 'fa fa-robot';
14035 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14036 $iconClass = 'fa fa-robot';
14037 }
14038 }
14039 }
14040
14041 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14042 return $out;
14043}
14044
14052{
14053 global $conf, $db;
14054
14055 $documents = array();
14056
14057 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
14058 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14059 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14060 //$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
14061 $sql .= ' ORDER BY ecm.position ASC';
14062
14063 $resql = $db->query($sql);
14064 if ($resql) {
14065 if ($db->num_rows($resql)) {
14066 while ($obj = $db->fetch_object($resql)) {
14067 $documents[$obj->id] = $obj;
14068 }
14069 }
14070 }
14071
14072 return $documents;
14073}
14074
14075
14093function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
14094{
14095 global $user, $conf;
14096 global $form;
14097
14098 global $param, $massactionbutton;
14099
14100 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
14101
14102 // Check parameters
14103 if (!is_object($filterobj) && !is_object($objcon)) {
14104 dol_print_error(null, 'BadParameter');
14105 }
14106
14107 $histo = array();
14108 '@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';
14109
14110 $numaction = 0;
14111 $now = dol_now();
14112
14113 $sortfield_list = explode(',', $sortfield);
14114 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
14115 $sortfield_new_list = array();
14116 foreach ($sortfield_list as $sortfield_value) {
14117 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
14118 }
14119 $sortfield_new = implode(',', $sortfield_new_list);
14120
14121 $sql = null;
14122 $sql2 = null;
14123
14124 if (isModEnabled('agenda')) {
14125 // Search histo on actioncomm
14126 if (is_object($objcon) && $objcon->id > 0) {
14127 $sql = "SELECT DISTINCT a.id, a.label as label,";
14128 } else {
14129 $sql = "SELECT a.id, a.label as label,";
14130 }
14131 $sql .= " a.datep as dp,";
14132 $sql .= " a.note as message,";
14133 $sql .= " a.datep2 as dp2,";
14134 $sql .= " a.percent as percent, 'action' as type,";
14135 $sql .= " a.fk_element, a.elementtype,";
14136 $sql .= " a.fk_contact,";
14137 $sql .= " a.email_from as msg_from,";
14138 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
14139 $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";
14140 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14141 $sql .= ", sp.lastname, sp.firstname";
14142 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14143 $sql .= ", m.lastname, m.firstname";
14144 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
14145 $sql .= ", o.ref";
14146 }
14147 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
14148 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
14149 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
14150
14151 $force_filter_contact = $filterobj instanceof User;
14152
14153 if (is_object($objcon) && $objcon->id > 0) {
14154 $force_filter_contact = true;
14155 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
14156 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
14157 }
14158
14159 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
14160 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
14161 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
14162 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
14163 $sql .= " ON er.resource_type = 'dolresource'";
14164 $sql .= " AND er.element_id = a.id";
14165 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
14166 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14167 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
14168 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14169 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
14170 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14171 $sql .= ", ".MAIN_DB_PREFIX."product as o";
14172 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14173 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
14174 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14175 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
14176 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14177 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
14178 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14179 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
14180 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14181 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
14182 }
14183
14184 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
14185 if (!$force_filter_contact) {
14186 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
14187 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
14188 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
14189 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
14190 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14191 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
14192 if ($filterobj->id) {
14193 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14194 }
14195 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
14196 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
14197 if ($filterobj->id) {
14198 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14199 }
14200 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14201 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
14202 if ($filterobj->id) {
14203 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14204 }
14205 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14206 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
14207 if ($filterobj->id) {
14208 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14209 }
14210 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14211 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
14212 if ($filterobj->id) {
14213 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14214 }
14215 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14216 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
14217 if ($filterobj->id) {
14218 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14219 }
14220 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14221 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
14222 if ($filterobj->id) {
14223 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14224 }
14225 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
14226 $sql .= " AND a.fk_contact = sp.rowid";
14227 if ($filterobj->id) {
14228 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
14229 }
14230 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14231 $sql .= " AND a.fk_element = o.rowid";
14232 if ($filterobj->id) {
14233 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
14234 }
14235 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14236 $sql .= " AND a.fk_element = o.rowid";
14237 if ($filterobj->id) {
14238 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
14239 }
14240 }
14241 } else {
14242 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
14243 }
14244
14245 // Condition on actioncode
14246 if (!empty($actioncode) && $actioncode != '-1') {
14247 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14248 if ($actioncode == 'AC_NON_AUTO') {
14249 $sql .= " AND c.type != 'systemauto'";
14250 } elseif ($actioncode == 'AC_ALL_AUTO') {
14251 $sql .= " AND c.type = 'systemauto'";
14252 } else {
14253 if ($actioncode == 'AC_OTH') {
14254 $sql .= " AND c.type != 'systemauto'";
14255 } elseif ($actioncode == 'AC_OTH_AUTO') {
14256 $sql .= " AND c.type = 'systemauto'";
14257 }
14258 }
14259 } else {
14260 if ($actioncode == 'AC_NON_AUTO') {
14261 $sql .= " AND c.type != 'systemauto'";
14262 } elseif ($actioncode == 'AC_ALL_AUTO') {
14263 $sql .= " AND c.type = 'systemauto'";
14264 } else {
14265 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14266 }
14267 }
14268 }
14269 if ($donetodo == 'todo') {
14270 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14271 } elseif ($donetodo == 'done') {
14272 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14273 }
14274 if (is_array($filters) && $filters['search_agenda_label']) {
14275 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14276 }
14277 }
14278
14279 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14280 if (isModEnabled('mailing') && !empty($objcon->email)
14281 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14282 $langs->load("mails");
14283
14284 $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";
14285 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14286 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14287 $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
14288 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14289 $sql2 .= ", '' as lastname, '' as firstname";
14290 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14291 $sql2 .= ", '' as lastname, '' as firstname";
14292 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14293 $sql2 .= ", '' as ref";
14294 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14295 $sql2 .= ", '' as ref";
14296 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14297 $sql2 .= ", '' as ref";
14298 }
14299 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14300 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14301 $sql2 .= " AND mc.statut = 1";
14302 $sql2 .= " AND u.rowid = m.fk_user_valid";
14303 $sql2 .= " AND mc.fk_mailing=m.rowid";
14304 }
14305
14306 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14307 if (!empty($sql) && !empty($sql2)) {
14308 $sql = $sql." UNION ".$sql2;
14309 } elseif (empty($sql) && !empty($sql2)) {
14310 $sql = $sql2;
14311 }
14312
14313 //TODO Add navigation with this limits...
14314 $offset = 0;
14315 $limit = 1000;
14316
14317 // Complete request and execute it with limit
14318 $sql .= $db->order($sortfield_new, $sortorder);
14319 if ($limit) {
14320 $sql .= $db->plimit($limit + 1, $offset);
14321 }
14322
14323 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14324 $resql = $db->query($sql);
14325 if ($resql) {
14326 $i = 0;
14327 $num = $db->num_rows($resql);
14328
14329 $imaxinloop = ($limit ? min($num, $limit) : $num);
14330 while ($i < $imaxinloop) {
14331 $obj = $db->fetch_object($resql);
14332
14333 if ($obj->type == 'action') {
14334 $contactaction = new ActionComm($db);
14335 $contactaction->id = $obj->id;
14336 $result = $contactaction->fetchResources();
14337 if ($result < 0) {
14338 dol_print_error($db);
14339 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14340 }
14341
14342 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14343 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14344 $tododone = '';
14345 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14346 $tododone = 'todo';
14347 }
14348
14349 $histo[$numaction] = array(
14350 'type' => $obj->type,
14351 'tododone' => $tododone,
14352 'id' => $obj->id,
14353 'datestart' => $db->jdate($obj->dp),
14354 'dateend' => $db->jdate($obj->dp2),
14355 'note' => $obj->label,
14356 'message' => dol_htmlentitiesbr($obj->message),
14357 'percent' => $obj->percent,
14358
14359 'userid' => $obj->user_id,
14360 'login' => $obj->user_login,
14361 'userfirstname' => $obj->user_firstname,
14362 'userlastname' => $obj->user_lastname,
14363 'userphoto' => $obj->user_photo,
14364 'msg_from' => $obj->msg_from,
14365
14366 'contact_id' => $obj->fk_contact,
14367 'socpeopleassigned' => $contactaction->socpeopleassigned,
14368 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14369 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14370 'fk_element' => $obj->fk_element,
14371 'elementtype' => $obj->elementtype,
14372 // Type of event
14373 'acode' => $obj->acode,
14374 'alabel' => $obj->alabel,
14375 'libelle' => $obj->alabel, // deprecated
14376 'apicto' => $obj->apicto
14377 );
14378 } else {
14379 $histo[$numaction] = array(
14380 'type' => $obj->type,
14381 'tododone' => 'done',
14382 'id' => $obj->id,
14383 'datestart' => $db->jdate($obj->dp),
14384 'dateend' => $db->jdate($obj->dp2),
14385 'note' => $obj->label,
14386 'message' => dol_htmlentitiesbr($obj->message),
14387 'percent' => $obj->percent,
14388 'acode' => $obj->acode,
14389
14390 'userid' => $obj->user_id,
14391 'login' => $obj->user_login,
14392 'userfirstname' => $obj->user_firstname,
14393 'userlastname' => $obj->user_lastname,
14394 'userphoto' => $obj->user_photo
14395 );
14396 }
14397
14398 $numaction++;
14399 $i++;
14400 }
14401 } else {
14402 dol_print_error($db);
14403 }
14404 }
14405
14406 // Set $out to show events
14407 $out = '';
14408
14409 if (!isModEnabled('agenda')) {
14410 $langs->loadLangs(array("admin", "errors"));
14411 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14412 }
14413
14414 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14415 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
14416
14417 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14418 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14419 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14420 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14421
14422 $formactions = new FormActions($db);
14423
14424 $actionstatic = new ActionComm($db);
14425 $userstatic = new User($db);
14426 $contactstatic = new Contact($db);
14427 $userGetNomUrlCache = array();
14428 $contactGetNomUrlCache = array();
14429
14430 $out .= '<div class="filters-container" >';
14431 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14432 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14433
14434 if ($objcon && get_class($objcon) == 'Contact' &&
14435 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14436 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14437 } else {
14438 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14439 }
14440 if (($filterobj && get_class($filterobj) == 'Societe')) {
14441 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14442 } else {
14443 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14444 }
14445
14446 $out .= "\n";
14447
14448 $out .= '<div class="div-table-responsive-no-min">';
14449 $out .= '<table class="noborder borderbottom centpercent">';
14450
14451 $out .= '<tr class="liste_titre">';
14452
14453 // Action column
14454 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14455 $out .= '<th class="liste_titre width50 middle">';
14456 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14457 $out .= $searchpicto;
14458 $out .= '</th>';
14459 }
14460
14461 // Date
14462 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
14463
14464 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14465 if ($donetodo) {
14466 $out .= '<th class="liste_titre"></th>';
14467 }
14468 // Type of event
14469 $out .= '<th class="liste_titre">';
14470 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14471 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
14472 $out .= '</th>';
14473 // Label
14474 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14475 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14476 $out .= '</th>';
14477
14478 // Action column
14479 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14480 $out .= '<th class="liste_titre width50 middle">';
14481 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14482 $out .= $searchpicto;
14483 $out .= '</th>';
14484 }
14485
14486 $out .= '</tr>';
14487
14488 $out .= '</table>';
14489
14490 $out .= '</form>';
14491 $out .= '</div>';
14492
14493 $out .= "\n";
14494
14495 $out .= '<ul class="timeline">';
14496
14497 if ($donetodo) {
14498 $tmp = '';
14499 if ($filterobj instanceof Societe) {
14500 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14501 }
14502 if ($filterobj instanceof User) {
14503 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14504 }
14505 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14506 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14507 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14508 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14509 if ($filterobj instanceof Societe) {
14510 $tmp .= '</a>';
14511 }
14512 if ($filterobj instanceof User) {
14513 $tmp .= '</a>';
14514 }
14515 $out .= getTitleFieldOfList($tmp);
14516 }
14517
14518 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14519 $caction = new CActionComm($db);
14520 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14521
14522 $actualCycleDate = false;
14523
14524 // Loop on each event to show it
14525 foreach ($histo as $key => $value) {
14526 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14527
14528 $actionstatic->type_picto = $histo[$key]['apicto'];
14529 $actionstatic->type_code = $histo[$key]['acode'];
14530
14531 $labeltype = $actionstatic->type_code;
14532 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14533 $labeltype = 'AC_OTH';
14534 }
14535 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14536 $labeltype = $langs->trans("Message");
14537 } else {
14538 if (!empty($arraylist[$labeltype])) {
14539 $labeltype = $arraylist[$labeltype];
14540 }
14541 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14542 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14543 }
14544 }
14545
14546 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14547
14548 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14549
14550 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14551 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14552 $out .= '<!-- timeline time label -->';
14553 $out .= '<li class="time-label">';
14554 $out .= '<span class="timeline-badge-date">';
14555 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14556 $out .= '</span>';
14557 $out .= '</li>';
14558 $out .= '<!-- /.timeline-label -->';
14559 }
14560
14561
14562 $out .= '<!-- timeline item -->'."\n";
14563 $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
14564
14565 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14566 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14567 //$out .= $timelineicon;
14568 //var_dump($timelineicon);
14569 $out .= $typeicon;
14570
14571 $out .= '<div class="timeline-item">'."\n";
14572
14573 $out .= '<span class="time timeline-header-action2">';
14574
14575 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14576 $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").' ';
14577 $out .= $histo[$key]['id'];
14578 $out .= '</a> ';
14579 } else {
14580 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14581 }
14582
14583 if ($user->hasRight('agenda', 'allactions', 'create') ||
14584 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14585 $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).'">';
14586 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14587 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14588 $out .= '</a>';
14589 }
14590
14591 $out .= '</span>';
14592
14593 // Date
14594 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14595 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14596 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14597 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14598 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14599 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14600 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14601 } else {
14602 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14603 }
14604 }
14605 $late = 0;
14606 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14607 $late = 1;
14608 }
14609 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14610 $late = 1;
14611 }
14612 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14613 $late = 1;
14614 }
14615 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14616 $late = 1;
14617 }
14618 if ($late) {
14619 $out .= img_warning($langs->trans("Late")).' ';
14620 }
14621 $out .= "</span></span>\n";
14622
14623 // Ref
14624 $out .= '<h3 class="timeline-header">';
14625
14626 // Author of event
14627 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14628 if ($histo[$key]['userid'] > 0) {
14629 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14630 $userstatic->fetch($histo[$key]['userid']);
14631 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14632 }
14633 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14634 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14635 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14636 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14637 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14638 } else {
14639 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14640 }
14641 }
14642 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14643 }
14644 $out .= '</div>';
14645
14646 // Title
14647 $out .= ' <div class="messaging-title inline-block">';
14648 //$out .= $actionstatic->getTypePicto();
14649 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14650 $out .= $labeltype.' - ';
14651 }
14652
14653 $libelle = '';
14654 if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14655 $out .= $langs->trans('TicketNewMessage');
14656 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14657 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14658 } elseif (isset($histo[$key]['type'])) {
14659 if ($histo[$key]['type'] == 'action') {
14660 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14661 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14662 $libelle = $histo[$key]['note'];
14663 $actionstatic->id = $histo[$key]['id'];
14664 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14665 } elseif ($histo[$key]['type'] == 'mailing') {
14666 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14667 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14668 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14669 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14670 } else {
14671 $libelle .= $histo[$key]['note'];
14672 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14673 }
14674 }
14675
14676 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14677 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14678 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14679 } else {
14680 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14681 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14682 }
14683 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14684 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14685 }
14686 if ($link) {
14687 $out .= ' - '.$link;
14688 }
14689 }
14690
14691 $out .= '</div>';
14692
14693 $out .= '</h3>';
14694
14695 // Message
14696 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14697 && $actionstatic->code != 'AC_TICKET_CREATE'
14698 && $actionstatic->code != 'AC_TICKET_MODIFY'
14699 ) {
14700 $out .= '<div class="timeline-body wordbreak small">';
14701 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14702 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14703 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14704 $out .= '<div class="readmore-block --closed" >';
14705 $out .= ' <div class="readmore-block__excerpt">';
14706 $out .= dolPrintHTML($truncatedText);
14707 $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>';
14708 $out .= ' </div>';
14709 $out .= ' <div class="readmore-block__full-text" >';
14710 $out .= dolPrintHTML($histo[$key]['message']);
14711 $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>';
14712 $out .= ' </div>';
14713 $out .= '</div>';
14714 } else {
14715 $out .= dolPrintHTML($histo[$key]['message']);
14716 }
14717
14718 $out .= '</div>';
14719 }
14720
14721 // Timeline footer
14722 $footer = '';
14723
14724 // Contact for this action
14725 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14726 $contactList = '';
14727 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14728 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14729 $contact = new Contact($db);
14730 $contact->fetch($cid);
14731 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14732 } else {
14733 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14734 }
14735
14736 if ($contact) {
14737 $contactList .= !empty($contactList) ? ', ' : '';
14738 $contactList .= $contact->getNomUrl(1);
14739 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14740 if (!empty($contact->phone_pro)) {
14741 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14742 }
14743 }
14744 }
14745 }
14746
14747 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14748 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14749 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14750 $contact = new Contact($db);
14751 $result = $contact->fetch($histo[$key]['contact_id']);
14752 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14753 } else {
14754 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14755 $result = ($contact instanceof Contact) ? $contact->id : 0;
14756 }
14757
14758 if ($result > 0) {
14759 $footer .= $contact->getNomUrl(1);
14760 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14761 if (!empty($contact->phone_pro)) {
14762 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14763 }
14764 }
14765 }
14766 }
14767
14768 $documents = getActionCommEcmList($actionstatic);
14769 if (!empty($documents)) {
14770 $footer .= '<div class="timeline-documents-container">';
14771 foreach ($documents as $doc) {
14772 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14773 $footer .= ' data-id="'.$doc->id.'" ';
14774 $footer .= ' data-path="'.$doc->filepath.'"';
14775 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14776 $footer .= '>';
14777
14778 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14779 $mime = dol_mimetype($filePath);
14780 $file = $actionstatic->id.'/'.$doc->filename;
14781 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14782 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14783 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14784
14785 $mimeAttr = ' mime="'.$mime.'" ';
14786 $class = '';
14787 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14788 $class .= ' documentpreview';
14789 }
14790
14791 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14792 $footer .= img_mime($filePath).' '.$doc->filename;
14793 $footer .= '</a>';
14794
14795 $footer .= '</span>';
14796 }
14797 $footer .= '</div>';
14798 }
14799
14800 if (!empty($footer)) {
14801 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14802 }
14803
14804 $out .= '</div>'."\n"; // end timeline-item
14805
14806 $out .= '</li>';
14807 $out .= '<!-- END timeline item -->';
14808 }
14809
14810 $out .= "</ul>\n";
14811
14812 $out .= '<script>
14813 jQuery(document).ready(function () {
14814 $(document).on("click", "[data-read-more-action]", function(e){
14815 let readMoreBloc = $(this).closest(".readmore-block");
14816 if(readMoreBloc.length > 0){
14817 e.preventDefault();
14818 if($(this).attr("data-read-more-action") == "close"){
14819 readMoreBloc.addClass("--closed").removeClass("--open");
14820 $("html, body").animate({
14821 scrollTop: readMoreBloc.offset().top - 200
14822 }, 100);
14823 }else{
14824 readMoreBloc.addClass("--open").removeClass("--closed");
14825 }
14826 }
14827 });
14828 });
14829 </script>';
14830
14831
14832 if (empty($histo)) {
14833 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14834 }
14835 }
14836
14837 if ($noprint) {
14838 return $out;
14839 } else {
14840 print $out;
14841 return null;
14842 }
14843}
14844
14856function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14857{
14858 if ($timestamp === null) {
14859 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14860 }
14861 $TParam = array(
14862 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14863 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14864 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14865 );
14866 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
14867 $TParam = array_merge($TParam, array(
14868 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
14869 $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
14870 $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
14871 ));
14872 }
14873
14874 return '&' . http_build_query($TParam);
14875}
14876
14895function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
14896{
14897 global $conf, $db, $langs, $hookmanager;
14898 global $action, $object;
14899
14900 if (!is_object($langs)) {
14901 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
14902 $langs = new Translate('', $conf);
14903 $langs->setDefaultLang();
14904 }
14905
14906 $langs->load("errors");
14907
14908 if ($printheader) {
14909 if (function_exists("llxHeader")) {
14910 llxHeader('');
14911 } elseif (function_exists("llxHeaderVierge")) {
14912 llxHeaderVierge('');
14913 }
14914 }
14915
14916 print '<div class="error">';
14917 if (empty($message)) {
14918 print $langs->trans("ErrorRecordNotFound");
14919 } else {
14920 print $langs->trans($message);
14921 }
14922 print '</div>';
14923 print '<br>';
14924
14925 if (empty($showonlymessage)) {
14926 if (empty($hookmanager)) {
14927 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14928 $hookmanager = new HookManager($db);
14929 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
14930 $hookmanager->initHooks(array('main'));
14931 }
14932
14933 $parameters = array('message' => $message, 'params' => $params);
14934 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14935 print $hookmanager->resPrint;
14936 }
14937
14938 if ($printfooter && function_exists("llxFooter")) {
14939 llxFooter();
14940 }
14941 exit(0);
14942}
$id
Definition account.php:39
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
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:457
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:733
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:70
$object ref
Definition info.php:79
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:515
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:500
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:484
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:534
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.
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_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
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...
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.
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_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.
dolForgeCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax string.
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
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:137
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:140
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length for cryptographic purposes.