dolibarr 21.0.0-beta
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2024 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2019-2023 Thibault Foucart <support@ptibogxiv.net>
18 * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20 * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21 * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22 * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
24 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
25 * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26 * Copyright (C) 2024 Josep Lluís Amador Teruel <joseplluis@lliuretic.cat>
27 * Copyright (C) 2024 Benoît PASCAL <contact@p-ben.com>
28 *
29 * This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 3 of the License, or
32 * (at your option) any later version.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program. If not, see <https://www.gnu.org/licenses/>.
41 * or see https://www.gnu.org/
42 */
43
50//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
51
52// Function for better PHP x compatibility
53if (!function_exists('utf8_encode')) {
61 function utf8_encode($elements)
62 {
63 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
64 }
65}
66
67if (!function_exists('utf8_decode')) {
75 function utf8_decode($elements)
76 {
77 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
78 }
79}
80if (!function_exists('str_starts_with')) {
89 function str_starts_with($haystack, $needle)
90 {
91 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
92 }
93}
94if (!function_exists('str_ends_with')) {
103 function str_ends_with($haystack, $needle)
104 {
105 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
106 }
107}
108if (!function_exists('str_contains')) {
117 function str_contains($haystack, $needle)
118 {
119 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
120 }
121}
122
123
135function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
136{
137 global $conf;
138
139 if (!is_object($object) && empty($module)) {
140 return null;
141 }
142 if (empty($module) && !empty($object->element)) {
143 $module = $object->element;
144 }
145
146 // Special case for backward compatibility
147 if ($module == 'fichinter') {
148 $module = 'ficheinter';
149 } elseif ($module == 'invoice_supplier') {
150 $module = 'supplier_invoice';
151 } elseif ($module == 'order_supplier') {
152 $module = 'supplier_order';
153 }
154
155 // Get the relative path of directory
156 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
157 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
158 $s = '';
159 if ($mode != 'outputrel') {
160 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
161 }
162 if ($forobject && $object->id > 0) {
163 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
164 }
165 return $s;
166 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
167 $s = '';
168 if ($mode != 'outputrel') {
169 $s = $conf->$module->dir_output;
170 }
171 if ($forobject && $object->id > 0) {
172 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
173 }
174 return $s;
175 } else {
176 return 'error-diroutput-not-defined-for-this-object='.$module;
177 }
178 } elseif ($mode == 'temp') {
179 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
180 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
181 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
182 return $conf->$module->dir_temp;
183 } else {
184 return 'error-dirtemp-not-defined-for-this-object='.$module;
185 }
186 } else {
187 return 'error-bad-value-for-mode';
188 }
189}
190
200function getMultidirTemp($object, $module = '', $forobject = 0)
201{
202 return getMultidirOutput($object, $module, $forobject, 'temp');
203}
204
214function getMultidirVersion($object, $module = '', $forobject = 0)
215{
216 return getMultidirOutput($object, $module, $forobject, 'version');
217}
218
219
228function getDolGlobalString($key, $default = '')
229{
230 global $conf;
231 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
232}
233
243function getDolGlobalInt($key, $default = 0)
244{
245 global $conf;
246 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
247}
248
258function getDolGlobalFloat($key, $default = 0)
259{
260 global $conf;
261 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
262}
263
272function getDolGlobalBool($key, $default = false)
273{
274 global $conf;
275 return (bool) ($conf->global->$key ?? $default);
276}
277
287function getDolUserString($key, $default = '', $tmpuser = null)
288{
289 if (empty($tmpuser)) {
290 global $user;
291 $tmpuser = $user;
292 }
293
294 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
295}
296
305function getDolUserInt($key, $default = 0, $tmpuser = null)
306{
307 if (empty($tmpuser)) {
308 global $user;
309 $tmpuser = $user;
310 }
311
312 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
313}
314
315
325define(
326 'MODULE_MAPPING',
327 array(
328 // Map deprecated names to new names
329 'adherent' => 'member', // Has new directory
330 'member_type' => 'adherent_type', // No directory, but file called adherent_type
331 'banque' => 'bank', // Has new directory
332 'contrat' => 'contract', // Has new directory
333 'entrepot' => 'stock', // Has new directory
334 'projet' => 'project', // Has new directory
335 'categorie' => 'category', // Has old directory
336 'commande' => 'order', // Has old directory
337 'expedition' => 'shipping', // Has old directory
338 'facture' => 'invoice', // Has old directory
339 'fichinter' => 'intervention', // Has old directory
340 'ficheinter' => 'intervention', // Backup for 'fichinter'
341 'propale' => 'propal', // Has old directory
342 'socpeople' => 'contact', // Has old directory
343 'fournisseur' => 'supplier', // Has old directory
344
345 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
346 'product_price' => 'productprice', // NO directory
347 'product_fournisseur_price' => 'productsupplierprice', // NO directory
348 )
349);
350
357function isModEnabled($module)
358{
359 global $conf;
360
361 // Fix old names (map to new names)
362 $arrayconv = MODULE_MAPPING;
363 $arrayconvbis = array_flip(MODULE_MAPPING);
364
365 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
366 // Special cases: both use the same module.
367 $arrayconv['supplier_order'] = 'fournisseur';
368 $arrayconv['supplier_invoice'] = 'fournisseur';
369 }
370 // Special case.
371 // @TODO Replace isModEnabled('delivery_note') with
372 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
373 if ($module == 'delivery_note') {
374 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
375 return false;
376 } else {
377 $module = 'shipping';
378 }
379 }
380
381 $module_alt = $module;
382 if (!empty($arrayconv[$module])) {
383 $module_alt = $arrayconv[$module];
384 }
385 $module_bis = $module;
386 if (!empty($arrayconvbis[$module])) {
387 $module_bis = $arrayconvbis[$module];
388 }
389
390 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
391 //return !empty($conf->$module->enabled);
392}
393
400function isDolTms($timestamp)
401{
402 if ($timestamp === '') {
403 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
404 return false;
405 }
406 if (is_null($timestamp) || !is_numeric($timestamp)) {
407 return false;
408 }
409
410 return true;
411}
412
424function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
425{
426 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
427
428 $class = 'DoliDB'.ucfirst($type);
429 $db = new $class($type, $host, $user, $pass, $name, $port);
430 return $db;
431}
432
450function getEntity($element, $shared = 1, $currentobject = null)
451{
452 global $conf, $mc, $hookmanager, $object, $action, $db;
453
454 if (!is_object($hookmanager)) {
455 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
456 $hookmanager = new HookManager($db);
457 }
458
459 // fix different element names (France to English)
460 switch ($element) {
461 case 'projet':
462 $element = 'project';
463 break;
464 case 'contrat':
465 $element = 'contract';
466 break; // "/contrat/class/contrat.class.php"
467 case 'order_supplier':
468 $element = 'supplier_order';
469 break; // "/fourn/class/fournisseur.commande.class.php"
470 case 'invoice_supplier':
471 $element = 'supplier_invoice';
472 break; // "/fourn/class/fournisseur.facture.class.php"
473 }
474
475 if (is_object($mc)) {
476 $out = $mc->getEntity($element, $shared, $currentobject);
477 } else {
478 $out = '';
479 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
480 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
481 $addzero[] = 'c_holiday_types';
482 }
483 if (in_array($element, $addzero)) {
484 $out .= '0,';
485 }
486 $out .= ((int) $conf->entity);
487 }
488
489 // Manipulate entities to query on the fly
490 $parameters = array(
491 'element' => $element,
492 'shared' => $shared,
493 'object' => $object,
494 'currentobject' => $currentobject,
495 'out' => $out
496 );
497 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
498
499 if (is_numeric($reshook)) {
500 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
501 $out .= ','.$hookmanager->resPrint; // add
502 } elseif ($reshook == 1) {
503 $out = $hookmanager->resPrint; // replace
504 }
505 }
506
507 return $out;
508}
509
516function setEntity($currentobject)
517{
518 global $conf, $mc;
519
520 if (is_object($mc) && method_exists($mc, 'setEntity')) {
521 return $mc->setEntity($currentobject);
522 } else {
523 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
524 }
525}
526
533function isASecretKey($keyname)
534{
535 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
536}
537
538
545function num2Alpha($n)
546{
547 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
548 $r = chr($n % 26 + 0x41) . $r;
549 }
550 return $r;
551}
552
553
570function getBrowserInfo($user_agent)
571{
572 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
573
574 $name = 'unknown';
575 $version = '';
576 $os = 'unknown';
577 $phone = '';
578
579 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
580
581 $detectmobile = new Mobile_Detect(null, $user_agent);
582 $tablet = $detectmobile->isTablet();
583
584 if ($detectmobile->isMobile()) {
585 $phone = 'unknown';
586
587 // If phone/smartphone, we set phone os name.
588 if ($detectmobile->is('AndroidOS')) {
589 $os = $phone = 'android';
590 } elseif ($detectmobile->is('BlackBerryOS')) {
591 $os = $phone = 'blackberry';
592 } elseif ($detectmobile->is('iOS')) {
593 $os = 'ios';
594 $phone = 'iphone';
595 } elseif ($detectmobile->is('PalmOS')) {
596 $os = $phone = 'palm';
597 } elseif ($detectmobile->is('SymbianOS')) {
598 $os = 'symbian';
599 } elseif ($detectmobile->is('webOS')) {
600 $os = 'webos';
601 } elseif ($detectmobile->is('MaemoOS')) {
602 $os = 'maemo';
603 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
604 $os = 'windows';
605 }
606 }
607
608 // OS
609 if (preg_match('/linux/i', $user_agent)) {
610 $os = 'linux';
611 } elseif (preg_match('/macintosh/i', $user_agent)) {
612 $os = 'macintosh';
613 } elseif (preg_match('/windows/i', $user_agent)) {
614 $os = 'windows';
615 }
616
617 // Name
618 $reg = array();
619 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
620 $name = 'firefox';
621 $version = empty($reg[2]) ? '' : $reg[2];
622 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
623 $name = 'edge';
624 $version = empty($reg[2]) ? '' : $reg[2];
625 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
626 $name = 'chrome';
627 $version = empty($reg[2]) ? '' : $reg[2];
628 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
629 // we can have 'chrome (Mozilla...) chrome x.y' in one string
630 $name = 'chrome';
631 } elseif (preg_match('/iceweasel/i', $user_agent)) {
632 $name = 'iceweasel';
633 } elseif (preg_match('/epiphany/i', $user_agent)) {
634 $name = 'epiphany';
635 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
636 $name = 'safari';
637 $version = empty($reg[2]) ? '' : $reg[2];
638 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
639 // Safari is often present in string for mobile but its not.
640 $name = 'opera';
641 $version = empty($reg[2]) ? '' : $reg[2];
642 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
643 $name = 'ie';
644 $version = end($reg);
645 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
646 // MS products at end
647 $name = 'ie';
648 $version = end($reg);
649 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
650 // MS products at end
651 $name = 'textbrowser';
652 $version = empty($reg[3]) ? '' : $reg[3];
653 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
654 // MS products at end
655 $name = 'textbrowser';
656 $version = empty($reg[1]) ? '' : $reg[1];
657 }
658
659 if ($tablet) {
660 $layout = 'tablet';
661 } elseif ($phone) {
662 $layout = 'phone';
663 } else {
664 $layout = 'classic';
665 }
666
667 return array(
668 'browsername' => $name,
669 'browserversion' => $version,
670 'browseros' => $os,
671 'browserua' => $user_agent,
672 'layout' => $layout, // tablet, phone, classic
673 'phone' => $phone, // deprecated
674 'tablet' => $tablet // deprecated
675 );
676}
677
683function dol_shutdown()
684{
685 global $db;
686 $disconnectdone = false;
687 $depth = 0;
688 if (is_object($db) && !empty($db->connected)) {
689 $depth = $db->transaction_opened;
690 $disconnectdone = $db->close();
691 }
692 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));
693}
694
704function GETPOSTISSET($paramname)
705{
706 $isset = false;
707
708 $relativepathstring = $_SERVER["PHP_SELF"];
709 // Clean $relativepathstring
710 if (constant('DOL_URL_ROOT')) {
711 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
712 }
713 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
714 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
715 //var_dump($relativepathstring);
716 //var_dump($user->default_values);
717
718 // Code for search criteria persistence.
719 // Retrieve values if restore_lastsearch_values
720 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
721 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
722 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
723 if (is_array($tmp)) {
724 foreach ($tmp as $key => $val) {
725 if ($key == $paramname) { // We are on the requested parameter
726 $isset = true;
727 break;
728 }
729 }
730 }
731 }
732 // If there is saved contextpage, limit, page or mode
733 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
734 $isset = true;
735 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
736 $isset = true;
737 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
738 $isset = true;
739 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
740 $isset = true;
741 }
742 } else {
743 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
744 }
745
746 return $isset;
747}
748
757function GETPOSTISARRAY($paramname, $method = 0)
758{
759 // for $method test need return the same $val as GETPOST
760 if (empty($method)) {
761 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
762 } elseif ($method == 1) {
763 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
764 } elseif ($method == 2) {
765 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
766 } elseif ($method == 3) {
767 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
768 } else {
769 $val = 'BadFirstParameterForGETPOST';
770 }
771
772 return is_array($val);
773}
774
805function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
806{
807 global $mysoc, $user, $conf;
808
809 if (empty($paramname)) { // Explicit test for null for phan.
810 return 'BadFirstParameterForGETPOST';
811 }
812 if (empty($check)) {
813 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);
814 // Enable this line to know who call the GETPOST with '' $check parameter.
815 //var_dump(debug_backtrace()[0]);
816 }
817
818 if (empty($method)) {
819 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
820 } elseif ($method == 1) {
821 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
822 } elseif ($method == 2) {
823 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
824 } elseif ($method == 3) {
825 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
826 } else {
827 return 'BadThirdParameterForGETPOST';
828 }
829
830 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
831
832 if (empty($method) || $method == 3 || $method == 4) {
833 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
834 // Clean $relativepathstring
835 if (constant('DOL_URL_ROOT')) {
836 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
837 }
838 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
839 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
840 //var_dump($relativepathstring);
841 //var_dump($user->default_values);
842
843 // Code for search criteria persistence.
844 // Retrieve saved values if restore_lastsearch_values is set
845 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
846 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
847 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
848 if (is_array($tmp)) {
849 foreach ($tmp as $key => $val) {
850 if ($key == $paramname) { // We are on the requested parameter
851 $out = $val;
852 break;
853 }
854 }
855 }
856 }
857 // If there is saved contextpage, page or limit
858 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
859 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
860 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
861 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
862 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
863 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
864 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
865 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
866 }
867 } elseif (!isset($_GET['sortfield'])) {
868 // Else, retrieve default values if we are not doing a sort
869 // 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
870 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
871 // Search default value from $object->field
872 global $object;
873 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
874 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
875 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
876 $out = $object->fields[$paramname]['default'];
877 }
878 }
879 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
880 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
881 // Now search in setup to overwrite default values
882 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
883 if (isset($user->default_values[$relativepathstring]['createform'])) {
884 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
885 $qualified = 0;
886 if ($defkey != '_noquery_') {
887 $tmpqueryarraytohave = explode('&', $defkey);
888 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
889 $foundintru = 0;
890 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
891 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
892 $foundintru = 1;
893 }
894 }
895 if (!$foundintru) {
896 $qualified = 1;
897 }
898 //var_dump($defkey.'-'.$qualified);
899 } else {
900 $qualified = 1;
901 }
902
903 if ($qualified) {
904 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
905 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
906 break;
907 }
908 }
909 }
910 }
911 }
912 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
913 // Management of default search_filters and sort order
914 if (!empty($user->default_values)) {
915 // $user->default_values defined from menu 'Setup - Default values'
916 //var_dump($user->default_values[$relativepathstring]);
917 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
918 // Sorted on which fields ? ASC or DESC ?
919 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
920 // Even if paramname is sortfield, data are stored into ['sortorder...']
921 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
922 $qualified = 0;
923 if ($defkey != '_noquery_') {
924 $tmpqueryarraytohave = explode('&', $defkey);
925 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
926 $foundintru = 0;
927 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
928 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
929 $foundintru = 1;
930 }
931 }
932 if (!$foundintru) {
933 $qualified = 1;
934 }
935 //var_dump($defkey.'-'.$qualified);
936 } else {
937 $qualified = 1;
938 }
939
940 if ($qualified) {
941 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
942 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
943 if ($out) {
944 $out .= ', ';
945 }
946 if ($paramname == 'sortfield') {
947 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
948 }
949 if ($paramname == 'sortorder') {
950 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
951 }
952 }
953 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
954 }
955 }
956 }
957 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
958 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
959 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
960 continue;
961 }
962 $qualified = 0;
963 if ($defkey != '_noquery_') {
964 $tmpqueryarraytohave = explode('&', $defkey);
965 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
966 $foundintru = 0;
967 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
968 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
969 $foundintru = 1;
970 }
971 }
972 if (!$foundintru) {
973 $qualified = 1;
974 }
975 //var_dump($defkey.'-'.$qualified);
976 } else {
977 $qualified = 1;
978 }
979
980 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
981 // We must keep $_POST and $_GET here
982 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
983 // We made a search from quick search menu, do we still use default filter ?
984 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
985 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
986 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
987 }
988 } else {
989 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
990 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
991 }
992 break;
993 }
994 }
995 }
996 }
997 }
998 }
999 }
1000 }
1001
1002 // 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)
1003 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1004 // 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.
1005 '@phan-var-force string $paramname';
1006 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1007 $reg = array();
1008 $maxloop = 20;
1009 $loopnb = 0; // Protection against infinite loop
1010
1011 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.
1012 $loopnb++;
1013 $newout = '';
1014
1015 if ($reg[1] == 'DAY') {
1016 $tmp = dol_getdate(dol_now(), true);
1017 $newout = $tmp['mday'];
1018 } elseif ($reg[1] == 'MONTH') {
1019 $tmp = dol_getdate(dol_now(), true);
1020 $newout = $tmp['mon'];
1021 } elseif ($reg[1] == 'YEAR') {
1022 $tmp = dol_getdate(dol_now(), true);
1023 $newout = $tmp['year'];
1024 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1025 $tmp = dol_getdate(dol_now(), true);
1026 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1027 $newout = $tmp2['day'];
1028 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1029 $tmp = dol_getdate(dol_now(), true);
1030 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1031 $newout = $tmp2['month'];
1032 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1033 $tmp = dol_getdate(dol_now(), true);
1034 $newout = ($tmp['year'] - 1);
1035 } elseif ($reg[1] == 'NEXT_DAY') {
1036 $tmp = dol_getdate(dol_now(), true);
1037 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1038 $newout = $tmp2['day'];
1039 } elseif ($reg[1] == 'NEXT_MONTH') {
1040 $tmp = dol_getdate(dol_now(), true);
1041 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1042 $newout = $tmp2['month'];
1043 } elseif ($reg[1] == 'NEXT_YEAR') {
1044 $tmp = dol_getdate(dol_now(), true);
1045 $newout = ($tmp['year'] + 1);
1046 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1047 $newout = $mysoc->country_id;
1048 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1049 $newout = $user->id;
1050 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1051 $newout = $user->fk_user;
1052 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1053 $newout = $conf->entity;
1054 } elseif ($reg[1] == 'ID') {
1055 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1056 } else {
1057 $newout = ''; // Key not found, we replace with empty string
1058 }
1059 //var_dump('__'.$reg[1].'__ -> '.$newout);
1060 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1061 }
1062 }
1063
1064 // Check type of variable and make sanitization according to this
1065 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1066 if (!is_array($out) || empty($out)) {
1067 $out = array();
1068 } else {
1069 $tmparray = explode(':', $check);
1070 if (!empty($tmparray[1])) {
1071 $tmpcheck = $tmparray[1];
1072 } else {
1073 $tmpcheck = 'alphanohtml';
1074 }
1075 foreach ($out as $outkey => $outval) {
1076 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1077 }
1078 }
1079 } else {
1080 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1081 // 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
1082 if (strpos($paramname, 'search_') === 0) {
1083 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1084 }
1085
1086 // @phan-suppress-next-line UnknownSanitizeType
1087 $out = sanitizeVal($out, $check, $filter, $options);
1088 }
1089
1090 // Sanitizing for special parameters.
1091 // 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.
1092 // @TODO Merge backtopage with backtourl
1093 // @TODO Rename backtolist into backtopagelist
1094 if (preg_match('/^backto/i', $paramname)) {
1095 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1096 $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.
1097 do {
1098 $oldstringtoclean = $out;
1099 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1100 $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'
1101 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1102 } while ($oldstringtoclean != $out);
1103 }
1104
1105 // Code for search criteria persistence.
1106 // Save data into session if key start with 'search_'
1107 if (empty($method) || $method == 3 || $method == 4) {
1108 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1109 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1110
1111 // We save search key only if $out not empty that means:
1112 // - posted value not empty, or
1113 // - 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).
1114
1115 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1116 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1117 }
1118 }
1119 }
1120
1121 return $out;
1122}
1123
1133function GETPOSTINT($paramname, $method = 0)
1134{
1135 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1136}
1137
1138
1147function GETPOSTFLOAT($paramname, $rounding = '')
1148{
1149 // 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.)
1150 return (float) price2num(GETPOST($paramname), $rounding, 2);
1151}
1152
1153
1167function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
1168{
1169 $m = array();
1170 if ($hourTime === 'getpost') {
1171 $hour = GETPOSTINT($prefix . 'hour');
1172 $minute = GETPOSTINT($prefix . 'minute');
1173 $second = GETPOSTINT($prefix . 'second');
1174 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1175 $hour = intval($m[1]);
1176 $minute = intval($m[2]);
1177 $second = intval($m[3]);
1178 } else {
1179 $hour = $minute = $second = 0;
1180 }
1181 // normalize out of range values
1182 $hour = (int) min($hour, 23);
1183 $minute = (int) min($minute, 59);
1184 $second = (int) min($second, 59);
1185
1186 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
1187}
1188
1189
1200function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1201{
1202 return sanitizeVal($out, $check, $filter, $options);
1203}
1204
1214function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1215{
1216 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1217 // Check is done after replacement
1218 switch ($check) {
1219 case 'none':
1220 case 'password':
1221 break;
1222 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1223 if (!is_numeric($out)) {
1224 $out = '';
1225 }
1226 break;
1227 case 'intcomma':
1228 if (is_array($out)) {
1229 $out = implode(',', $out);
1230 }
1231 if (preg_match('/[^0-9,-]+/i', $out)) {
1232 $out = '';
1233 }
1234 break;
1235 case 'san_alpha':
1236 $out = filter_var($out, FILTER_SANITIZE_STRING);
1237 break;
1238 case 'email':
1239 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1240 break;
1241 case 'aZ':
1242 if (!is_array($out)) {
1243 $out = trim($out);
1244 if (preg_match('/[^a-z]+/i', $out)) {
1245 $out = '';
1246 }
1247 }
1248 break;
1249 case 'aZ09':
1250 if (!is_array($out)) {
1251 $out = trim($out);
1252 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1253 $out = '';
1254 }
1255 }
1256 break;
1257 case 'aZ09arobase': // great to sanitize $objecttype parameter
1258 if (!is_array($out)) {
1259 $out = trim($out);
1260 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1261 $out = '';
1262 }
1263 }
1264 break;
1265 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1266 if (!is_array($out)) {
1267 $out = trim($out);
1268 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1269 $out = '';
1270 }
1271 }
1272 break;
1273 case 'alpha': // No html and no ../ and "
1274 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1275 if (!is_array($out)) {
1276 $out = trim($out);
1277 do {
1278 $oldstringtoclean = $out;
1279 // Remove html tags
1280 $out = dol_string_nohtmltag($out, 0);
1281 // 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).
1282 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1283 // Remove also other dangerous string sequences
1284 // '../' or '..\' is dangerous because it allows dir transversals
1285 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1286 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1287 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1288 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1289 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1290 } while ($oldstringtoclean != $out);
1291 // keep lines feed
1292 }
1293 break;
1294 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'
1295 if (!is_array($out)) {
1296 $out = trim($out);
1297 do {
1298 $oldstringtoclean = $out;
1299 // Decode html entities
1300 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1301 // 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).
1302 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1303 // Remove also other dangerous string sequences
1304 // '../' or '..\' is dangerous because it allows dir transversals
1305 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1306 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1307 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1308 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1309 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1310 } while ($oldstringtoclean != $out);
1311 }
1312 break;
1313 case 'nohtml': // No html
1314 $out = dol_string_nohtmltag($out, 0);
1315 break;
1316 case 'restricthtmlnolink':
1317 case 'restricthtml': // Recommended for most html textarea
1318 case 'restricthtmlallowclass':
1319 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1320 case 'restricthtmlallowunvalid':
1321 $out = dol_htmlwithnojs($out, 1, $check);
1322 break;
1323 case 'custom':
1324 if (!empty($out)) {
1325 if (empty($filter)) {
1326 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1327 }
1328 if (is_null($options)) {
1329 $options = 0;
1330 }
1331 $out = filter_var($out, $filter, $options);
1332 }
1333 break;
1334 default:
1335 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1336 $out = GETPOST($out, 'alphanohtml');
1337 break;
1338 }
1339
1340 return $out;
1341}
1342
1343
1344if (!function_exists('dol_getprefix')) {
1355 function dol_getprefix($mode = '')
1356 {
1357 // If prefix is for email (we need to have $conf already loaded for this case)
1358 if ($mode == 'email') {
1359 global $conf;
1360
1361 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1362 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1363 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1364 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1365 return $_SERVER["SERVER_NAME"];
1366 }
1367 }
1368
1369 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1370 if (!empty($conf->file->instance_unique_id)) {
1371 return sha1('dolibarr'.$conf->file->instance_unique_id);
1372 }
1373
1374 // For backward compatibility when instance_unique_id is not set
1375 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1376 }
1377
1378 // If prefix is for session (no need to have $conf loaded)
1379 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1380 $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
1381
1382 // The recommended value (may be not defined for old versions)
1383 if (!empty($tmp_instance_unique_id)) {
1384 return sha1('dolibarr'.$tmp_instance_unique_id);
1385 }
1386
1387 // For backward compatibility when instance_unique_id is not set
1388 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1389 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1390 } else {
1391 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1392 }
1393 }
1394}
1395
1406function dol_include_once($relpath, $classname = '')
1407{
1408 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']
1409
1410 $fullpath = dol_buildpath($relpath);
1411
1412 if (!file_exists($fullpath)) {
1413 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1414 return false;
1415 }
1416
1417 if (!empty($classname) && !class_exists($classname)) {
1418 return include $fullpath;
1419 } else {
1420 return include_once $fullpath;
1421 }
1422}
1423
1424
1438function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1439{
1440 global $conf;
1441
1442 $path = preg_replace('/^\//', '', $path);
1443
1444 if (empty($type)) { // For a filesystem path
1445 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1446 if (is_array($conf->file->dol_document_root)) {
1447 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1448 if ($key == 'main') {
1449 continue;
1450 }
1451 // if (@file_exists($dirroot.'/'.$path)) {
1452 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1453 $res = $dirroot.'/'.$path;
1454 return $res;
1455 }
1456 }
1457 }
1458 if ($returnemptyifnotfound) {
1459 // Not found into alternate dir
1460 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1461 return '';
1462 }
1463 }
1464 } else {
1465 // For an url path
1466 // We try to get local path of file on filesystem from url
1467 // Note that trying to know if a file on disk exist by forging path on disk from url
1468 // works only for some web server and some setup. This is bugged when
1469 // using proxy, rewriting, virtual path, etc...
1470 $res = '';
1471 if ($type == 1) {
1472 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1473 }
1474 if ($type == 2) {
1475 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1476 }
1477 if ($type == 3) {
1478 $res = DOL_URL_ROOT.'/'.$path;
1479 }
1480
1481 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1482 if ($key == 'main') {
1483 if ($type == 3) {
1484 /*global $dolibarr_main_url_root;*/
1485
1486 // Define $urlwithroot
1487 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1488 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1489 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1490
1491 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1492 }
1493 continue;
1494 }
1495 $regs = array();
1496 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1497 if (!empty($regs[1])) {
1498 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1499 //if (file_exists($dirroot.'/'.$regs[1])) {
1500 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1501 if ($type == 1) {
1502 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1503 } elseif ($type == 2) {
1504 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1505 } elseif ($type == 3) {
1506 /*global $dolibarr_main_url_root;*/
1507
1508 // Define $urlwithroot
1509 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1510 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1511 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1512
1513 $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
1514 }
1515 break;
1516 }
1517 }
1518 }
1519 }
1520
1521 return $res;
1522}
1523
1534function dol_get_object_properties($obj, $properties = [])
1535{
1536 // Get real properties using get_object_vars() if $properties is empty
1537 if (empty($properties)) {
1538 return get_object_vars($obj);
1539 }
1540
1541 $existingProperties = [];
1542 $realProperties = get_object_vars($obj);
1543
1544 // Get the real or magic property values
1545 foreach ($properties as $property) {
1546 if (array_key_exists($property, $realProperties)) {
1547 // Real property, add the value
1548 $existingProperties[$property] = $obj->{$property};
1549 } elseif (property_exists($obj, $property)) {
1550 // Magic property
1551 $existingProperties[$property] = $obj->{$property};
1552 }
1553 }
1554
1555 return $existingProperties;
1556}
1557
1558
1574function dol_clone($object, $native = 2)
1575{
1576 if ($native == 0) {
1577 // deprecated method, use the method with native = 2 instead
1578 $tmpsavdb = null;
1579 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1580 $tmpsavdb = $object->db;
1581 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1582 }
1583
1584 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1585
1586 if (!empty($tmpsavdb)) {
1587 $object->db = $tmpsavdb;
1588 }
1589 } elseif ($native == 2) {
1590 // recommended method to have a full isolated cloned object
1591 $myclone = new stdClass();
1592 $tmparray = get_object_vars($object); // return only public properties
1593
1594 if (is_array($tmparray)) {
1595 foreach ($tmparray as $propertykey => $propertyval) {
1596 if (is_scalar($propertyval) || is_array($propertyval)) {
1597 $myclone->$propertykey = $propertyval;
1598 }
1599 }
1600 }
1601 } else {
1602 $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)
1603 }
1604
1605 return $myclone;
1606}
1607
1617function dol_size($size, $type = '')
1618{
1619 global $conf;
1620 if (empty($conf->dol_optimize_smallscreen)) {
1621 return $size;
1622 }
1623 if ($type == 'width' && $size > 250) {
1624 return 250;
1625 } else {
1626 return 10;
1627 }
1628}
1629
1630
1642function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1643{
1644 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1645 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1646 // Char '/' and '\' are file delimiters.
1647 // 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
1648 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1649 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1650 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1651 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1652 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1653 $tmp = str_replace('..', '', $tmp);
1654 return $tmp;
1655}
1656
1657
1669function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1670{
1671 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1672 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1673 // 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
1674 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1675
1676 $tmp = $str;
1677 if ($unaccent) {
1678 $tmp = dol_string_unaccent($tmp);
1679 }
1680 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1681 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1682 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1683 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1684 $tmp = str_replace('..', '', $tmp);
1685 return $tmp;
1686}
1687
1695function dol_sanitizeUrl($stringtoclean, $type = 1)
1696{
1697 // 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)
1698 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1699 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1700 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1701 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1702
1703 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1704 if ($type == 1) {
1705 // removing : should disable links to external url like http:aaa)
1706 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1707 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1708 }
1709
1710 do {
1711 $oldstringtoclean = $stringtoclean;
1712 // removing '&colon' should disable links to external url like http:aaa)
1713 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1714 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1715 } while ($oldstringtoclean != $stringtoclean);
1716
1717 if ($type == 1) {
1718 // removing '//' should disable links to external url like //aaa or http//)
1719 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1720 }
1721
1722 return $stringtoclean;
1723}
1724
1731function dol_sanitizeEmail($stringtoclean)
1732{
1733 do {
1734 $oldstringtoclean = $stringtoclean;
1735 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1736 } while ($oldstringtoclean != $stringtoclean);
1737
1738 return $stringtoclean;
1739}
1740
1749function dol_sanitizeKeyCode($str)
1750{
1751 return preg_replace('/[^\w]+/', '', $str);
1752}
1753
1754
1763function dol_string_unaccent($str)
1764{
1765 if (is_null($str)) {
1766 return '';
1767 }
1768
1769 if (utf8_check($str)) {
1770 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1771 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1772 return $transliterator->transliterate($str);
1773 }
1774 // See http://www.utf8-chartable.de/
1775 $string = rawurlencode($str);
1776 $replacements = array(
1777 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1778 '%C3%87' => 'C',
1779 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1780 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1781 '%C3%91' => 'N',
1782 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1783 '%C5%A0' => 'S',
1784 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1785 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1786 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1787 '%C3%A7' => 'c',
1788 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1789 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1790 '%C3%B1' => 'n',
1791 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1792 '%C5%A1' => 's',
1793 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1794 '%C3%BD' => 'y', '%C3%BF' => 'y'
1795 );
1796 $string = strtr($string, $replacements);
1797 return rawurldecode($string);
1798 } else {
1799 // See http://www.ascii-code.com/
1800 $string = strtr(
1801 $str,
1802 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1803 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1804 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1805 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1806 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1807 \xF9\xFA\xFB\xFC\xFD\xFF",
1808 "AAAAAAC
1809 EEEEIIIIDN
1810 OOOOOUUUY
1811 aaaaaaceeee
1812 iiiidnooooo
1813 uuuuyy"
1814 );
1815 $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"));
1816 return $string;
1817 }
1818}
1819
1833function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1834{
1835 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1836 if (empty($keepspaces)) {
1837 $forbidden_chars_to_replace[] = " ";
1838 }
1839 $forbidden_chars_to_remove = array();
1840 //$forbidden_chars_to_remove=array("(",")");
1841
1842 if (is_array($badcharstoreplace)) {
1843 $forbidden_chars_to_replace = $badcharstoreplace;
1844 }
1845 if (is_array($badcharstoremove)) {
1846 $forbidden_chars_to_remove = $badcharstoremove;
1847 }
1848
1849 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1850 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1851}
1852
1853
1867function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1868{
1869 if ($removetabcrlf) {
1870 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1871 } else {
1872 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
1873 }
1874}
1875
1884function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1885{
1886 if (is_null($stringtoescape)) {
1887 return '';
1888 }
1889
1890 // escape quotes and backslashes, newlines, etc.
1891 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1892 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1893 if (empty($noescapebackslashn)) {
1894 $substitjs["\n"] = '\\n';
1895 $substitjs['\\'] = '\\\\';
1896 }
1897 if (empty($mode)) {
1898 $substitjs["'"] = "\\'";
1899 $substitjs['"'] = "\\'";
1900 } elseif ($mode == 1) {
1901 $substitjs["'"] = "\\'";
1902 } elseif ($mode == 2) {
1903 $substitjs['"'] = '\\"';
1904 } elseif ($mode == 3) {
1905 $substitjs["'"] = "\\'";
1906 $substitjs['"'] = "\\\"";
1907 }
1908 return strtr($stringtoescape, $substitjs);
1909}
1910
1920function dol_escape_uri($stringtoescape)
1921{
1922 return rawurlencode($stringtoescape);
1923}
1924
1931function dol_escape_json($stringtoescape)
1932{
1933 return str_replace('"', '\"', $stringtoescape);
1934}
1935
1943function dol_escape_php($stringtoescape, $stringforquotes = 2)
1944{
1945 if (is_null($stringtoescape)) {
1946 return '';
1947 }
1948
1949 if ($stringforquotes == 2) {
1950 return str_replace('"', "'", $stringtoescape);
1951 } elseif ($stringforquotes == 1) {
1952 // We remove the \ char.
1953 // If we allow the \ char, we can have $stringtoescape =
1954 // abc\';phpcodedanger; so the escapement will become
1955 // abc\\';phpcodedanger; and injecting this into
1956 // $a='...' will give $ac='abc\\';phpcodedanger;
1957 $stringtoescape = str_replace('\\', '', $stringtoescape);
1958 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1959 }
1960
1961 return 'Bad parameter for stringforquotes in dol_escape_php';
1962}
1963
1970function dol_escape_all($stringtoescape)
1971{
1972 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1973}
1974
1981function dol_escape_xml($stringtoescape)
1982{
1983 return $stringtoescape;
1984}
1985
1993function dolPrintLabel($s)
1994{
1995 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1996}
1997
2005function dolPrintText($s)
2006{
2007 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2008}
2009
2019function dolPrintHTML($s, $allowiframe = 0)
2020{
2021 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2022}
2023
2031function dolPrintHTMLForAttribute($s)
2032{
2033 // The dol_htmlentitiesbr will convert simple text into html
2034 // The dol_escape_htmltag will escape html chars.
2035 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
2036}
2037
2047function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2048{
2049 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2050}
2051
2058function dolPrintPassword($s)
2059{
2060 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
2061}
2062
2063
2080function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2081{
2082 if ($noescapetags == 'common') {
2083 $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';
2084 // Add also html5 tags
2085 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2086 }
2087 if ($cleanalsojavascript) {
2088 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2089 }
2090
2091 // escape quotes and backslashes, newlines, etc.
2092 if ($escapeonlyhtmltags) {
2093 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2094 } else {
2095 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
2096 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
2097 }
2098 if (!$keepb) {
2099 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2100 }
2101 if (!$keepn) {
2102 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2103 } elseif ($keepn == -1) {
2104 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2105 }
2106
2107 if ($escapeonlyhtmltags) {
2108 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2109 } else {
2110 // Escape tags to keep
2111 $tmparrayoftags = array();
2112 if ($noescapetags) {
2113 $tmparrayoftags = explode(',', $noescapetags);
2114 }
2115 if (count($tmparrayoftags)) {
2116 $reg = array();
2117 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2118
2119 foreach ($tmparrayoftags as $tagtoreplace) {
2120 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2121 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2122 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2123
2124 // For case of tag with attribute
2125 do {
2126 $tmpold = $tmp;
2127
2128 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', $tmp, $reg)) {
2129 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must never have [ ] inside the attribute string
2130 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes);
2131 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2132 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2133 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2134 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2135 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2136 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2137 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2138 }
2139 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)\s+\/>/', $tmp, $reg)) {
2140 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
2141 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2142 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2143 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2144 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2145 }
2146
2147 $diff = strcmp($tmpold, $tmp);
2148 } while ($diff);
2149 }
2150 }
2151
2152 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2153
2154 //print $result;
2155
2156 if (count($tmparrayoftags)) {
2157 foreach ($tmparrayoftags as $tagtoreplace) {
2158 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2159 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2160 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2161 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2162 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2163 }
2164
2165 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2166 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2167 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2168 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2169 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2170 }
2171
2172 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2173
2174 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2175
2176 return $result;
2177 }
2178}
2179
2187function dol_strtolower($string, $encoding = "UTF-8")
2188{
2189 if (function_exists('mb_strtolower')) {
2190 return mb_strtolower($string, $encoding);
2191 } else {
2192 return strtolower($string);
2193 }
2194}
2195
2204function dol_strtoupper($string, $encoding = "UTF-8")
2205{
2206 if (function_exists('mb_strtoupper')) {
2207 return mb_strtoupper($string, $encoding);
2208 } else {
2209 return strtoupper($string);
2210 }
2211}
2212
2221function dol_ucfirst($string, $encoding = "UTF-8")
2222{
2223 if (function_exists('mb_substr')) {
2224 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2225 } else {
2226 return ucfirst($string);
2227 }
2228}
2229
2238function dol_ucwords($string, $encoding = "UTF-8")
2239{
2240 if (function_exists('mb_convert_case')) {
2241 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2242 } else {
2243 return ucwords($string);
2244 }
2245}
2246
2247
2253function getCallerInfoString()
2254{
2255 $backtrace = debug_backtrace();
2256 $msg = "";
2257 if (count($backtrace) >= 2) {
2258 $trace = $backtrace[1];
2259 if (isset($trace['file'], $trace['line'])) {
2260 $msg = " From {$trace['file']}:{$trace['line']}.";
2261 }
2262 }
2263 return $msg;
2264}
2265
2288function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2289{
2290 global $conf, $user, $debugbar;
2291
2292 // If syslog module enabled
2293 if (!isModEnabled('syslog')) {
2294 return;
2295 }
2296
2297 // Check if we are into execution of code of a website
2298 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2299 global $website, $websitekey;
2300 if (is_object($website) && !empty($website->ref)) {
2301 $suffixinfilename .= '_website_'.$website->ref;
2302 } elseif (!empty($websitekey)) {
2303 $suffixinfilename .= '_website_'.$websitekey;
2304 }
2305 }
2306
2307 // Check if we have a forced suffix
2308 if (defined('USESUFFIXINLOG')) {
2309 $suffixinfilename .= constant('USESUFFIXINLOG');
2310 }
2311
2312 if ($ident < 0) {
2313 foreach ($conf->loghandlers as $loghandlerinstance) {
2314 $loghandlerinstance->setIdent($ident);
2315 }
2316 }
2317
2318 if (!empty($message)) {
2319 // Test log level @phan-suppress-next-line PhanPluginDuplicateArrayKey
2320 $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');
2321 if (!array_key_exists($level, $logLevels)) {
2322 throw new Exception('Incorrect log level');
2323 }
2324 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2325 return;
2326 }
2327
2328 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2329 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2330 }
2331
2332 // If adding log inside HTML page is required
2333 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2334 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2335 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2336 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2337
2338 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2339 }
2340
2341 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2342 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2343 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2344 print "\n\n<!-- Log start\n";
2345 print dol_escape_htmltag($message)."\n";
2346 print "Log end -->\n";
2347 }
2348
2349 $data = array(
2350 'message' => $message,
2351 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2352 'level' => $level,
2353 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2354 'ip' => false,
2355 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2356 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2357 );
2358
2359 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2360 if (!empty($remoteip)) {
2361 $data['ip'] = $remoteip;
2362 // This is when server run behind a reverse proxy
2363 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2364 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2365 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2366 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2367 }
2368 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2369 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2370 $data['ip'] = $_SERVER['SERVER_ADDR'];
2371 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2372 // 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).
2373 $data['ip'] = $_SERVER['COMPUTERNAME'];
2374 } else {
2375 $data['ip'] = '???';
2376 }
2377
2378 if (!empty($_SERVER['USERNAME'])) {
2379 // 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).
2380 $data['osuser'] = $_SERVER['USERNAME'];
2381 } elseif (!empty($_SERVER['LOGNAME'])) {
2382 // 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).
2383 $data['osuser'] = $_SERVER['LOGNAME'];
2384 }
2385
2386 // Loop on each log handler and send output
2387 foreach ($conf->loghandlers as $loghandlerinstance) {
2388 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2389 continue;
2390 }
2391 $loghandlerinstance->export($data, $suffixinfilename);
2392 }
2393 unset($data);
2394 }
2395
2396 if ($ident > 0) {
2397 foreach ($conf->loghandlers as $loghandlerinstance) {
2398 $loghandlerinstance->setIdent($ident);
2399 }
2400 }
2401}
2402
2414function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2415{
2416 global $langs, $db;
2417
2418 $form = new Form($db);
2419
2420 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2421 if (empty($templatenameforexport)) {
2422 $templatenameforexport = 'website_'.$website->ref;
2423 }
2424
2425 $out = '';
2426 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2427
2428 // for generate popup
2429 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2430 $out .= 'jQuery(document).ready(function () {';
2431 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2432 $out .= ' var dialogHtml = \'';
2433
2434 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2435 $dialogcontent .= ' <div style="margin-top: 20px;">';
2436 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2437 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2438 $dialogcontent .= ' </div>';
2439 $dialogcontent .= ' <br>';
2440 $dialogcontent .= ' <div style="margin-top: 20px;">';
2441 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2442 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2443 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2444 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2445 $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>';
2446 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2447 $dialogcontent .= ' </form>';
2448 $dialogcontent .= ' </div>';
2449 $dialogcontent .= ' </div>';
2450
2451 $out .= dol_escape_js($dialogcontent);
2452
2453 $out .= '\';';
2454
2455
2456 // Add the content of the dialog to the body of the page
2457 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2458 $out .= ' if ($dialog.length > 0) {
2459 $dialog.remove();
2460 }
2461 jQuery("body").append(dialogHtml);';
2462
2463 // Configuration of popup
2464 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2465 $out .= ' autoOpen: false,';
2466 $out .= ' modal: true,';
2467 $out .= ' height: 290,';
2468 $out .= ' width: "40%",';
2469 $out .= ' title: "' . dol_escape_js($label) . '",';
2470 $out .= ' });';
2471
2472 // Simulate a click on the original "submit" input to export the site.
2473 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2474 $out .= ' console.log("Clic on exportsite.");';
2475 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2476 $out .= ' console.log("element founded:", target.length > 0);';
2477 $out .= ' if (target.length > 0) { target.click(); }';
2478 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2479 $out .= ' });';
2480
2481 // open popup
2482 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2483 $out .= ' return false;';
2484 $out .= ' });';
2485 $out .= '});';
2486 $out .= '</script>';
2487
2488 return $out;
2489}
2490
2491
2508function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2509{
2510 global $conf;
2511
2512 if (strpos($url, '?') > 0) {
2513 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2514 } else {
2515 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2516 }
2517
2518 $out = '';
2519
2520 $backtopagejsfieldsid = '';
2521 $backtopagejsfieldslabel = '';
2522 if ($backtopagejsfields) {
2523 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2524 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2525 $backtopagejsfields = $name.":".$backtopagejsfields;
2526 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2527 } else {
2528 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2529 }
2530 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2531 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2532 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2533 }
2534
2535 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2536 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2537 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2538 if (empty($conf->use_javascript_ajax)) {
2539 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2540 } elseif ($jsonopen) {
2541 $out .= ' href="#" onclick="'.$jsonopen.'"';
2542 } else {
2543 $out .= ' href="#"';
2544 }
2545 $out .= '>'.$buttonstring.'</a>';
2546
2547 if (!empty($conf->use_javascript_ajax)) {
2548 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2549 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2550 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2551 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2552 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2553
2554 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2555 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2556 jQuery(document).ready(function () {
2557 jQuery(".button_'.$name.'").click(function () {
2558 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2559 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2560 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2561 $tmpdialog.dialog({
2562 autoOpen: false,
2563 modal: true,
2564 height: (window.innerHeight - 150),
2565 width: \'80%\',
2566 title: \''.dol_escape_js($label).'\',
2567 open: function (event, ui) {
2568 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2569 },
2570 close: function (event, ui) {
2571 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2572 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2573 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2574 if (returnedid != "" && returnedid != "div for returned id") {
2575 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2576 }
2577 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2578 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2579 }
2580 }
2581 });
2582
2583 $tmpdialog.dialog(\'open\');
2584 return false;
2585 });
2586 });
2587 </script>';
2588 }
2589 return $out;
2590}
2591
2608function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2609{
2610 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2611}
2612
2629function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2630{
2631 global $conf, $langs, $hookmanager;
2632
2633 // Show title
2634 $showtitle = 1;
2635 if (!empty($conf->dol_optimize_smallscreen)) {
2636 $showtitle = 0;
2637 }
2638
2639 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2640
2641 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2642 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2643 }
2644
2645 // Show right part
2646 if ($morehtmlright) {
2647 $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.
2648 }
2649
2650 // Show title
2651 /*
2652 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2653 $limittitle = 30;
2654 $out .= '<a class="tabTitle">';
2655 if ($picto) {
2656 $noprefix = $pictoisfullpath;
2657 if (strpos($picto, 'fontawesome_') !== false) {
2658 $noprefix = 1;
2659 }
2660 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2661 }
2662 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2663 $out .= '</a>';
2664 }
2665 */
2666
2667 // Show tabs
2668
2669 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2670 $maxkey = -1;
2671 if (is_array($links) && !empty($links)) {
2672 $keys = array_keys($links);
2673 if (count($keys)) {
2674 $maxkey = max($keys);
2675 }
2676 }
2677
2678 // Show tabs
2679 // if =0 we don't use the feature
2680 if (empty($limittoshow)) {
2681 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2682 }
2683 if (!empty($conf->dol_optimize_smallscreen)) {
2684 $limittoshow = 2;
2685 }
2686
2687 $displaytab = 0;
2688 $nbintab = 0;
2689 $popuptab = 0;
2690 $outmore = '';
2691 for ($i = 0; $i <= $maxkey; $i++) {
2692 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2693 // If active tab is already present
2694 if ($i >= $limittoshow) {
2695 $limittoshow--;
2696 }
2697 }
2698 }
2699
2700 for ($i = 0; $i <= $maxkey; $i++) {
2701 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2702 $isactive = true;
2703 } else {
2704 $isactive = false;
2705 }
2706
2707 if ($i < $limittoshow || $isactive) {
2708 // Output entry with a visible tab
2709 $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])).' -->';
2710
2711 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2712 if (!empty($links[$i][0])) {
2713 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2714 } else {
2715 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2716 }
2717 } elseif (!empty($links[$i][1])) {
2718 //print "x $i $active ".$links[$i][2]." z";
2719 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2720
2721 if (!empty($links[$i][0])) {
2722 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2723 $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).'">';
2724 }
2725
2726 if ($displaytab == 0 && $picto) {
2727 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2728 }
2729
2730 $out .= $links[$i][1];
2731 if (!empty($links[$i][0])) {
2732 $out .= '</a>'."\n";
2733 }
2734 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2735 $out .= '</div>';
2736 }
2737
2738 $out .= '</div>';
2739 } else {
2740 // Add entry into the combo popup with the other tabs
2741 if (!$popuptab) {
2742 $popuptab = 1;
2743 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2744 }
2745 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2746 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2747 if (!empty($links[$i][0])) {
2748 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2749 } else {
2750 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2751 }
2752 } elseif (!empty($links[$i][1])) {
2753 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2754 $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.
2755 $outmore .= '</a>'."\n";
2756 }
2757 $outmore .= '</div>';
2758
2759 $nbintab++;
2760 }
2761
2762 $displaytab = $i + 1;
2763 }
2764 if ($popuptab) {
2765 $outmore .= '</div>';
2766 }
2767
2768 if ($popuptab) { // If there is some tabs not shown
2769 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2770 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2771 $widthofpopup = 200;
2772
2773 $tabsname = $moretabssuffix;
2774 if (empty($tabsname)) {
2775 $tabsname = str_replace("@", "", $picto);
2776 }
2777 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2778 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2779 $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".
2780 }
2781 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2782 $out .= $outmore;
2783 $out .= '</div>';
2784 $out .= '<div></div>';
2785 $out .= "</div>\n";
2786
2787 $out .= '<script nonce="'.getNonce().'">';
2788 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2789 var x = this.offsetLeft, y = this.offsetTop;
2790 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2791 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2792 $('#moretabsList".$tabsname."').css('".$right."','8px');
2793 }
2794 $('#moretabsList".$tabsname."').css('".$left."','auto');
2795 });
2796 ";
2797 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2798 $out .= "</script>";
2799 }
2800
2801 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2802 $out .= "</div>\n";
2803 }
2804
2805 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
2806 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
2807 $out .= '">'."\n";
2808 }
2809 if (!empty($dragdropfile)) {
2810 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2811 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2812 }
2813 $parameters = array('tabname' => $active, 'out' => $out);
2814 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2815 if ($reshook > 0) {
2816 $out = $hookmanager->resPrint;
2817 }
2818
2819 return $out;
2820}
2821
2829function dol_fiche_end($notab = 0)
2830{
2831 print dol_get_fiche_end($notab);
2832}
2833
2840function dol_get_fiche_end($notab = 0)
2841{
2842 if (!$notab || $notab == -1) {
2843 return "\n</div>\n";
2844 } else {
2845 return '';
2846 }
2847}
2848
2868function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2869{
2870 global $conf, $form, $user, $langs, $hookmanager, $action;
2871
2872 $error = 0;
2873
2874 $maxvisiblephotos = 1;
2875 $showimage = 1;
2876 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2877 // @phan-suppress-next-line PhanUndeclaredMethod
2878 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2879 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2880 $showbarcode = 0;
2881 }
2882 $modulepart = 'unknown';
2883
2884 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2885 $modulepart = $object->element;
2886 } elseif ($object->element == 'member') {
2887 $modulepart = 'memberphoto';
2888 } elseif ($object->element == 'user') {
2889 $modulepart = 'userphoto';
2890 }
2891
2892 if (class_exists("Imagick")) {
2893 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2894 $modulepart = $object->element;
2895 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2896 $modulepart = 'ficheinter';
2897 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2898 $modulepart = 'contract';
2899 } elseif ($object->element == 'order_supplier') {
2900 $modulepart = 'supplier_order';
2901 } elseif ($object->element == 'invoice_supplier') {
2902 $modulepart = 'supplier_invoice';
2903 }
2904 }
2905
2906 if ($object->element == 'product') {
2908 '@phan-var-force Product $object';
2909 $width = 80;
2910 $cssclass = 'photowithmargin photoref';
2911 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2912 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2913 if ($conf->browser->layout == 'phone') {
2914 $maxvisiblephotos = 1;
2915 }
2916 if ($showimage) {
2917 $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>';
2918 } else {
2919 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2920 $nophoto = '';
2921 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2922 } else { // Show no photo link
2923 $nophoto = '/public/theme/common/nophoto.png';
2924 $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>';
2925 }
2926 }
2927 } elseif ($object->element == 'category') {
2929 '@phan-var-force Categorie $object';
2930 $width = 80;
2931 $cssclass = 'photowithmargin photoref';
2932 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2933 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2934 if ($conf->browser->layout == 'phone') {
2935 $maxvisiblephotos = 1;
2936 }
2937 if ($showimage) {
2938 $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>';
2939 } else {
2940 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2941 $nophoto = '';
2942 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2943 } else { // Show no photo link
2944 $nophoto = '/public/theme/common/nophoto.png';
2945 $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>';
2946 }
2947 }
2948 } elseif ($object->element == 'bom') {
2950 '@phan-var-force Bom $object';
2951 $width = 80;
2952 $cssclass = 'photowithmargin photoref';
2953 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2954 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2955 if ($conf->browser->layout == 'phone') {
2956 $maxvisiblephotos = 1;
2957 }
2958 if ($showimage) {
2959 $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>';
2960 } else {
2961 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2962 $nophoto = '';
2963 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2964 } else { // Show no photo link
2965 $nophoto = '/public/theme/common/nophoto.png';
2966 $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>';
2967 }
2968 }
2969 } elseif ($object->element == 'ticket') {
2970 $width = 80;
2971 $cssclass = 'photoref';
2973 '@phan-var-force Ticket $object';
2974 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2975 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2976 if ($conf->browser->layout == 'phone') {
2977 $maxvisiblephotos = 1;
2978 }
2979
2980 if ($showimage) {
2981 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2982 if ($object->nbphoto > 0) {
2983 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2984 } else {
2985 $showimage = 0;
2986 }
2987 }
2988 if (!$showimage) {
2989 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2990 $nophoto = '';
2991 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2992 } else { // Show no photo link
2993 $nophoto = img_picto('No photo', 'object_ticket');
2994 $morehtmlleft .= '<!-- No photo to show -->';
2995 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2996 $morehtmlleft .= $nophoto;
2997 $morehtmlleft .= '</div></div>';
2998 }
2999 }
3000 } else {
3001 if ($showimage) {
3002 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3003 $phototoshow = '';
3004 // Check if a preview file is available
3005 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3006 $objectref = dol_sanitizeFileName($object->ref);
3007 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
3008 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3009 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3010 $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
3011 } else {
3012 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3013 }
3014 if (empty($subdir)) {
3015 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3016 }
3017
3018 $filepath = $dir_output.$subdir."/";
3019
3020 $filepdf = $filepath.$objectref.".pdf";
3021 $relativepath = $subdir.'/'.$objectref.'.pdf';
3022
3023 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3024 $fileimage = $filepdf.'_preview.png';
3025 $relativepathimage = $relativepath.'_preview.png';
3026
3027 $pdfexists = file_exists($filepdf);
3028
3029 // If PDF file exists
3030 if ($pdfexists) {
3031 // Conversion du PDF en image png si fichier png non existent
3032 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3033 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3034 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3035 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3036 if ($ret < 0) {
3037 $error++;
3038 }
3039 }
3040 }
3041 }
3042
3043 if ($pdfexists && !$error) {
3044 $heightforphotref = 80;
3045 if (!empty($conf->dol_optimize_smallscreen)) {
3046 $heightforphotref = 60;
3047 }
3048 // If the preview file is found
3049 if (file_exists($fileimage)) {
3050 $phototoshow = '<div class="photoref">';
3051 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3052 $phototoshow .= '</div>';
3053 }
3054 }
3055 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3056 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
3057 }
3058
3059 if ($phototoshow) {
3060 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3061 $morehtmlleft .= $phototoshow;
3062 $morehtmlleft .= '</div>';
3063 }
3064 }
3065
3066 if (empty($phototoshow)) { // Show No photo link (picto of object)
3067 if ($object->element == 'action') {
3068 $width = 80;
3069 $cssclass = 'photorefcenter';
3070 $nophoto = img_picto('No photo', 'title_agenda');
3071 } else {
3072 $width = 14;
3073 $cssclass = 'photorefcenter';
3074 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3075 $prefix = 'object_';
3076 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3077 $picto = 'project'; // instead of projectpub
3078 }
3079 if (strpos($picto, 'fontawesome_') !== false) {
3080 $prefix = '';
3081 }
3082 $nophoto = img_picto('No photo', $prefix.$picto);
3083 }
3084 $morehtmlleft .= '<!-- No photo to show -->';
3085 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3086 $morehtmlleft .= $nophoto;
3087 $morehtmlleft .= '</div></div>';
3088 }
3089 }
3090 }
3091
3092 // Show barcode
3093 if ($showbarcode) {
3094 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3095 }
3096
3097 if ($object->element == 'societe') {
3098 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3099 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3100 } else {
3101 $morehtmlstatus .= $object->getLibStatut(6);
3102 }
3103 } elseif ($object->element == 'product') {
3104 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3105 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3106 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3107 } else {
3108 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3109 }
3110 $morehtmlstatus .= ' &nbsp; ';
3111 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3112 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3113 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3114 } else {
3115 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3116 }
3117 } elseif (in_array($object->element, array('salary'))) {
3118 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3119 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3120 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3121 }
3122 $morehtmlstatus .= $tmptxt;
3123 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3124 $totalallpayments = $object->getSommePaiement(0);
3125 $totalallpayments += $object->getSumCreditNotesUsed(0);
3126 $totalallpayments += $object->getSumDepositsUsed(0);
3127 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3128 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3129 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3130 }
3131 $morehtmlstatus .= $tmptxt;
3132 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3133 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3134 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3135 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3136 }
3137 $morehtmlstatus .= $tmptxt;
3138 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3139 if ($object->statut == 0) {
3140 $morehtmlstatus .= $object->getLibStatut(5);
3141 } else {
3142 $morehtmlstatus .= $object->getLibStatut(4);
3143 }
3144 } elseif ($object->element == 'facturerec') {
3145 '@phan-var-force FactureRec $object';
3146 if ($object->frequency == 0) {
3147 $morehtmlstatus .= $object->getLibStatut(2);
3148 } else {
3149 $morehtmlstatus .= $object->getLibStatut(5);
3150 }
3151 } elseif ($object->element == 'project_task') {
3152 $object->fk_statut = 1;
3153 $object->status = 1;
3154 if ($object->progress > 0) {
3155 $object->fk_statut = 2;
3156 $object->status = 2;
3157 }
3158 if ($object->progress >= 100) {
3159 $object->fk_statut = 3;
3160 $object->status = 3;
3161 }
3162 $tmptxt = $object->getLibStatut(5);
3163 $morehtmlstatus .= $tmptxt; // No status on task
3164 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3165 $tmptxt = $object->getLibStatut(6);
3166 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3167 $tmptxt = $object->getLibStatut(5);
3168 }
3169 $morehtmlstatus .= $tmptxt;
3170 }
3171
3172 // Add if object was dispatched "into accountancy"
3173 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3174 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3175 if (method_exists($object, 'getVentilExportCompta')) {
3176 $accounted = $object->getVentilExportCompta();
3177 $langs->load("accountancy");
3178 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3179 }
3180 }
3181
3182 // Add alias for thirdparty
3183 if (!empty($object->name_alias)) {
3184 '@phan-var-force Societe $object';
3185 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3186 }
3187
3188 // Add label
3189 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3190 if (!empty($object->label)) {
3191 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3192 }
3193 }
3194 // Show address and email
3195 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3196 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3197 if ($moreaddress) {
3198 $morehtmlref .= '<div class="refidno refaddress">';
3199 $morehtmlref .= $moreaddress;
3200 $morehtmlref .= '</div>';
3201 }
3202 }
3203 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)) {
3204 $morehtmlref .= '<div style="clear: both;"></div>';
3205 $morehtmlref .= '<div class="refidno opacitymedium">';
3206 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3207 $morehtmlref .= '</div>';
3208 }
3209
3210 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3211 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3212 if ($reshook < 0) {
3213 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3214 } elseif (empty($reshook)) {
3215 $morehtmlref .= $hookmanager->resPrint;
3216 } elseif ($reshook > 0) {
3217 $morehtmlref = $hookmanager->resPrint;
3218 }
3219
3220 // $morehtml is the right part (link "Back to list")
3221 // $morehtmlleft is the picto or photo of banner
3222 // $morehtmlstatus is part under the status
3223 // $morehtmlright is part of htmlright
3224
3225 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3226 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3227 print '</div>';
3228 print '<div class="underrefbanner clearboth"></div>';
3229}
3230
3240function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3241{
3242 global $langs;
3243 $ret = '';
3244 if ($fieldrequired) {
3245 $ret .= '<span class="fieldrequired">';
3246 }
3247 $ret .= '<label for="'.$fieldkey.'">';
3248 $ret .= $langs->trans($langkey);
3249 $ret .= '</label>';
3250 if ($fieldrequired) {
3251 $ret .= '</span>';
3252 }
3253 return $ret;
3254}
3255
3269function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3270{
3271 global $langs, $hookmanager;
3272
3273 $ret = '';
3274 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3275
3276 // See format of addresses on https://en.wikipedia.org/wiki/Address
3277 // Address
3278 if (empty($mode)) {
3279 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3280 }
3281 // Zip/Town/State
3282 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3283 // US: title firstname name \n address lines \n town, state, zip \n country
3284 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3285 $ret .= (($ret && $town) ? $sep : '').$town;
3286
3287 if (!empty($object->state)) {
3288 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3289 }
3290 if (!empty($object->zip)) {
3291 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3292 }
3293 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3294 // UK: title firstname name \n address lines \n town state \n zip \n country
3295 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3296 $ret .= ($ret ? $sep : '').$town;
3297 if (!empty($object->state)) {
3298 $ret .= ($ret ? ", " : '').$object->state;
3299 }
3300 if (!empty($object->zip)) {
3301 $ret .= ($ret ? $sep : '').$object->zip;
3302 }
3303 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3304 // ES: title firstname name \n address lines \n zip town \n state \n country
3305 $ret .= ($ret ? $sep : '').$object->zip;
3306 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3307 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3308 if (!empty($object->state)) {
3309 $ret .= $sep.$object->state;
3310 }
3311 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3312 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3313 // See https://www.sljfaq.org/afaq/addresses.html
3314 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3315 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3316 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3317 // IT: title firstname name\n address lines \n zip town state_code \n country
3318 $ret .= ($ret ? $sep : '').$object->zip;
3319 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3320 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3321 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3322 } else {
3323 // Other: title firstname name \n address lines \n zip town[, state] \n country
3324 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3325 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3326 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3327 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3328 $ret .= ($ret ? ", " : '').$object->state;
3329 }
3330 }
3331
3332 if (!is_object($outputlangs)) {
3333 $outputlangs = $langs;
3334 }
3335 if ($withcountry) {
3336 $langs->load("dict");
3337 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3338 }
3339 if ($hookmanager) {
3340 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3341 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3342 if ($reshook > 0) {
3343 $ret = '';
3344 }
3345 $ret .= $hookmanager->resPrint;
3346 }
3347
3348 return $ret;
3349}
3350
3351
3352
3362function dol_strftime($fmt, $ts = false, $is_gmt = false)
3363{
3364 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3365 return dol_print_date($ts, $fmt, $is_gmt);
3366 } else {
3367 return 'Error date outside supported range';
3368 }
3369}
3370
3392function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3393{
3394 global $conf, $langs;
3395
3396 // If date undefined or "", we return ""
3397 if (dol_strlen($time) == 0) {
3398 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3399 }
3400
3401 if ($tzoutput === 'auto') {
3402 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3403 }
3404
3405 // Clean parameters
3406 $to_gmt = false;
3407 $offsettz = $offsetdst = 0;
3408 if ($tzoutput) {
3409 $to_gmt = true; // For backward compatibility
3410 if (is_string($tzoutput)) {
3411 if ($tzoutput == 'tzserver') {
3412 $to_gmt = false;
3413 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3414 // @phan-suppress-next-line PhanPluginRedundantAssignment
3415 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3416 // @phan-suppress-next-line PhanPluginRedundantAssignment
3417 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3418 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3419 $to_gmt = true;
3420 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3421
3422 if (class_exists('DateTimeZone')) {
3423 $user_date_tz = new DateTimeZone($offsettzstring);
3424 $user_dt = new DateTime();
3425 $user_dt->setTimezone($user_date_tz);
3426 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3427 $offsettz = $user_dt->getOffset(); // should include dst ?
3428 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3429 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3430 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3431 }
3432 }
3433 }
3434 }
3435 if (!is_object($outputlangs)) {
3436 $outputlangs = $langs;
3437 }
3438 if (!$format) {
3439 $format = 'daytextshort';
3440 }
3441
3442 // Do we have to reduce the length of date (year on 2 chars) to save space.
3443 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3444 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3445 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3446 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3447 if ($formatwithoutreduce != $format) {
3448 $format = $formatwithoutreduce;
3449 $reduceformat = 1;
3450 } // so format 'dayreduceformat' is processed like day
3451
3452 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3453 // TODO Add format daysmallyear and dayhoursmallyear
3454 if ($format == 'day') {
3455 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3456 } elseif ($format == 'hour') {
3457 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3458 } elseif ($format == 'hourduration') {
3459 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3460 } elseif ($format == 'daytext') {
3461 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3462 } elseif ($format == 'daytextshort') {
3463 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3464 } elseif ($format == 'dayhour') {
3465 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3466 } elseif ($format == 'dayhoursec') {
3467 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3468 } elseif ($format == 'dayhourtext') {
3469 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3470 } elseif ($format == 'dayhourtextshort') {
3471 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3472 } elseif ($format == 'dayhourlog') {
3473 // Format not sensitive to language
3474 $format = '%Y%m%d%H%M%S';
3475 } elseif ($format == 'dayhourlogsmall') {
3476 // Format not sensitive to language
3477 $format = '%y%m%d%H%M';
3478 } elseif ($format == 'dayhourldap') {
3479 $format = '%Y%m%d%H%M%SZ';
3480 } elseif ($format == 'dayhourxcard') {
3481 $format = '%Y%m%dT%H%M%SZ';
3482 } elseif ($format == 'dayxcard') {
3483 $format = '%Y%m%d';
3484 } elseif ($format == 'dayrfc') {
3485 $format = '%Y-%m-%d'; // DATE_RFC3339
3486 } elseif ($format == 'dayhourrfc') {
3487 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3488 } elseif ($format == 'standard') {
3489 $format = '%Y-%m-%d %H:%M:%S';
3490 }
3491
3492 if ($reduceformat) {
3493 $format = str_replace('%Y', '%y', $format);
3494 $format = str_replace('yyyy', 'yy', $format);
3495 }
3496
3497 // Clean format
3498 if (preg_match('/%b/i', $format)) { // There is some text to translate
3499 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3500 $format = str_replace('%b', '__b__', $format);
3501 $format = str_replace('%B', '__B__', $format);
3502 }
3503 if (preg_match('/%a/i', $format)) { // There is some text to translate
3504 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3505 $format = str_replace('%a', '__a__', $format);
3506 $format = str_replace('%A', '__A__', $format);
3507 }
3508
3509 // Analyze date
3510 $reg = array();
3511 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
3512 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"]));
3513 return '';
3514 } 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
3515 // This part of code should not be used anymore.
3516 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);
3517 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3518 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3519 $syear = (!empty($reg[1]) ? $reg[1] : '');
3520 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3521 $sday = (!empty($reg[3]) ? $reg[3] : '');
3522 $shour = (!empty($reg[4]) ? $reg[4] : '');
3523 $smin = (!empty($reg[5]) ? $reg[5] : '');
3524 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3525
3526 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3527
3528 if ($to_gmt) {
3529 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3530 } else {
3531 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3532 }
3533 $dtts = new DateTime();
3534 $dtts->setTimestamp($time);
3535 $dtts->setTimezone($tzo);
3536 $newformat = str_replace(
3537 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3538 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3539 $format
3540 );
3541 $ret = $dtts->format($newformat);
3542 $ret = str_replace(
3543 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3544 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3545 $ret
3546 );
3547 } else {
3548 // Date is a timestamps
3549 if ($time < 100000000000) { // Protection against bad date values
3550 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3551
3552 if ($to_gmt) {
3553 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3554 } else {
3555 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3556 }
3557 $dtts = new DateTime();
3558 $dtts->setTimestamp($timetouse);
3559 $dtts->setTimezone($tzo);
3560 $newformat = str_replace(
3561 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3562 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3563 $format
3564 );
3565 $ret = $dtts->format($newformat);
3566 $ret = str_replace(
3567 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3568 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3569 $ret
3570 );
3571 //var_dump($ret);exit;
3572 } else {
3573 $ret = 'Bad value '.$time.' for date';
3574 }
3575 }
3576
3577 if (preg_match('/__b__/i', $format)) {
3578 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3579
3580 if ($to_gmt) {
3581 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3582 } else {
3583 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3584 }
3585 $dtts = new DateTime();
3586 $dtts->setTimestamp($timetouse);
3587 $dtts->setTimezone($tzo);
3588 $month = (int) $dtts->format("m");
3589 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3590 if ($encodetooutput) {
3591 $monthtext = $outputlangs->transnoentities('Month'.$month);
3592 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3593 } else {
3594 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3595 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3596 }
3597 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3598 $ret = str_replace('__b__', $monthtextshort, $ret);
3599 $ret = str_replace('__B__', $monthtext, $ret);
3600 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3601 //return $ret;
3602 }
3603 if (preg_match('/__a__/i', $format)) {
3604 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3605 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3606
3607 if ($to_gmt) {
3608 $tzo = new DateTimeZone('UTC');
3609 } else {
3610 $tzo = new DateTimeZone(date_default_timezone_get());
3611 }
3612 $dtts = new DateTime();
3613 $dtts->setTimestamp($timetouse);
3614 $dtts->setTimezone($tzo);
3615 $w = $dtts->format("w");
3616 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3617
3618 $ret = str_replace('__A__', $dayweek, $ret);
3619 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3620 }
3621
3622 return $ret;
3623}
3624
3625
3646function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3647{
3648 if ($timestamp === '') {
3649 return array();
3650 }
3651
3652 $datetimeobj = new DateTime();
3653 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3654 if ($forcetimezone) {
3655 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3656 }
3657 $arrayinfo = array(
3658 'year' => ((int) date_format($datetimeobj, 'Y')),
3659 'mon' => ((int) date_format($datetimeobj, 'm')),
3660 'mday' => ((int) date_format($datetimeobj, 'd')),
3661 'wday' => ((int) date_format($datetimeobj, 'w')),
3662 'yday' => ((int) date_format($datetimeobj, 'z')),
3663 'hours' => ((int) date_format($datetimeobj, 'H')),
3664 'minutes' => ((int) date_format($datetimeobj, 'i')),
3665 'seconds' => ((int) date_format($datetimeobj, 's')),
3666 '0' => $timestamp
3667 );
3668
3669 return $arrayinfo;
3670}
3671
3693function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3694{
3695 global $conf;
3696 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3697
3698 if ($gm === 'auto') {
3699 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3700 }
3701 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3702
3703 // Clean parameters
3704 if ($hour == -1 || empty($hour)) {
3705 $hour = 0;
3706 }
3707 if ($minute == -1 || empty($minute)) {
3708 $minute = 0;
3709 }
3710 if ($second == -1 || empty($second)) {
3711 $second = 0;
3712 }
3713
3714 // Check parameters
3715 if ($check) {
3716 if (!$month || !$day) {
3717 return '';
3718 }
3719 if ($day > 31) {
3720 return '';
3721 }
3722 if ($month > 12) {
3723 return '';
3724 }
3725 if ($hour < 0 || $hour > 24) {
3726 return '';
3727 }
3728 if ($minute < 0 || $minute > 60) {
3729 return '';
3730 }
3731 if ($second < 0 || $second > 60) {
3732 return '';
3733 }
3734 }
3735
3736 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3737 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3738 $localtz = new DateTimeZone($default_timezone);
3739 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3740 // We use dol_tz_string first because it is more reliable.
3741 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3742 try {
3743 $localtz = new DateTimeZone($default_timezone);
3744 } catch (Exception $e) {
3745 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3746 $default_timezone = @date_default_timezone_get();
3747 }
3748 } elseif (strrpos($gm, "tz,") !== false) {
3749 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3750 try {
3751 $localtz = new DateTimeZone($timezone);
3752 } catch (Exception $e) {
3753 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3754 }
3755 }
3756
3757 if (empty($localtz)) {
3758 $localtz = new DateTimeZone('UTC');
3759 }
3760 //var_dump($localtz);
3761 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3762 $dt = new DateTime('now', $localtz);
3763 $dt->setDate((int) $year, (int) $month, (int) $day);
3764 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3765 $date = $dt->getTimestamp(); // should include daylight saving time
3766 //var_dump($date);
3767 return $date;
3768}
3769
3770
3781function dol_now($mode = 'auto')
3782{
3783 $ret = 0;
3784
3785 if ($mode === 'auto') {
3786 $mode = 'gmt';
3787 }
3788
3789 if ($mode == 'gmt') {
3790 $ret = time(); // Time for now at greenwich.
3791 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3792 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3793 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3794 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3795 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3796 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3797 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3798 // $ret=dol_now('gmt')+($tzsecond*3600);
3799 //}
3800 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3801 // Time for now with user timezone added
3802 //print 'time: '.time();
3803 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3804 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3805 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3806 }
3807
3808 return $ret;
3809}
3810
3811
3820function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3821{
3822 global $conf, $langs;
3823 $level = 1024;
3824
3825 if (!empty($conf->dol_optimize_smallscreen)) {
3826 $shortunit = 1;
3827 }
3828
3829 // Set value text
3830 if (empty($shortvalue) || $size < ($level * 10)) {
3831 $ret = $size;
3832 $textunitshort = $langs->trans("b");
3833 $textunitlong = $langs->trans("Bytes");
3834 } else {
3835 $ret = round($size / $level, 0);
3836 $textunitshort = $langs->trans("Kb");
3837 $textunitlong = $langs->trans("KiloBytes");
3838 }
3839 // Use long or short text unit
3840 if (empty($shortunit)) {
3841 $ret .= ' '.$textunitlong;
3842 } else {
3843 $ret .= ' '.$textunitshort;
3844 }
3845
3846 return $ret;
3847}
3848
3859function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3860{
3861 global $langs;
3862
3863 if (empty($url)) {
3864 return '';
3865 }
3866
3867 $linkstart = '<a href="';
3868 if (!preg_match('/^http/i', $url)) {
3869 $linkstart .= 'http://';
3870 }
3871 $linkstart .= $url;
3872 $linkstart .= '"';
3873 if ($target) {
3874 $linkstart .= ' target="'.$target.'"';
3875 }
3876 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3877 $linkstart .= '>';
3878
3879 $link = '';
3880 if (!preg_match('/^http/i', $url)) {
3881 $link .= 'http://';
3882 }
3883 $link .= dol_trunc($url, $max);
3884
3885 $linkend = '</a>';
3886
3887 if ($morecss == 'float') { // deprecated
3888 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3889 } else {
3890 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3891 }
3892}
3893
3907function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
3908{
3909 global $user, $langs, $hookmanager;
3910
3911 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3912 //$showinvalid = 1; $email = 'rrrrr';
3913
3914 $newemail = dol_escape_htmltag($email);
3915
3916 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3917 $withpicto = 0;
3918 }
3919
3920 if (empty($email)) {
3921 return '&nbsp;';
3922 }
3923
3924 if ($addlink == 1) {
3925 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
3926 if (!preg_match('/^mailto:/i', $email)) {
3927 $newemail .= 'mailto:';
3928 }
3929 $newemail .= $email;
3930 $newemail .= '" target="_blank">';
3931
3932 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3933
3934 if ($max > 0) {
3935 $newemail .= dol_trunc($email, $max);
3936 } else {
3937 $newemail .= $email;
3938 }
3939 $newemail .= '</a>';
3940 if ($showinvalid && !isValidEmail($email)) {
3941 $langs->load("errors");
3942 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3943 }
3944
3945 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3946 $type = 'AC_EMAIL';
3947 $linktoaddaction = '';
3948 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3949 $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>';
3950 }
3951 if ($linktoaddaction) {
3952 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3953 }
3954 }
3955 } elseif ($addlink === 'thirdparty') {
3956 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
3957 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3958 if ($withpicto == 1) {
3959 $tmpnewemail .= $newemail;
3960 }
3961 $tmpnewemail .= '</a>';
3962
3963 $newemail = $tmpnewemail;
3964 } else {
3965 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3966
3967 if ($showinvalid && !isValidEmail($email)) {
3968 $langs->load("errors");
3969 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3970 }
3971 }
3972
3973 //$rep = '<div class="nospan" style="margin-right: 10px">';
3974 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3975 //$rep .= '</div>';
3976 $rep = $newemail;
3977
3978 if ($hookmanager) {
3979 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3980
3981 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3982 if ($reshook > 0) {
3983 $rep = '';
3984 }
3985 $rep .= $hookmanager->resPrint;
3986 }
3987
3988 return $rep;
3989}
3990
3996function getArrayOfSocialNetworks()
3997{
3998 global $conf, $db;
3999
4000 $socialnetworks = array();
4001 // Enable caching of array
4002 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
4003 $cachekey = 'socialnetworks_' . $conf->entity;
4004 $dataretrieved = dol_getcache($cachekey);
4005 if (!is_null($dataretrieved)) {
4006 $socialnetworks = $dataretrieved;
4007 } else {
4008 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
4009 $sql .= " WHERE entity=".$conf->entity;
4010 $resql = $db->query($sql);
4011 if ($resql) {
4012 while ($obj = $db->fetch_object($resql)) {
4013 $socialnetworks[$obj->code] = array(
4014 'rowid' => $obj->rowid,
4015 'label' => $obj->label,
4016 'url' => $obj->url,
4017 'icon' => $obj->icon,
4018 'active' => $obj->active,
4019 );
4020 }
4021 }
4022 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4023 }
4024 return $socialnetworks;
4025}
4026
4037function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
4038{
4039 global $hookmanager, $langs, $user;
4040
4041 $htmllink = $value;
4042
4043 if (empty($value)) {
4044 return '&nbsp;';
4045 }
4046
4047 if (!empty($type)) {
4048 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4049 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4050 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
4051 if ($type == 'skype') {
4052 $htmllink .= dol_escape_htmltag($value);
4053 $htmllink .= '&nbsp; <a href="skype:';
4054 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4055 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
4056 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
4057 $htmllink .= '</a><a href="skype:';
4058 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4059 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
4060 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
4061 $htmllink .= '</a>';
4062 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4063 $addlink = 'AC_SKYPE';
4064 $link = '';
4065 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4066 $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>';
4067 }
4068 $htmllink .= ($link ? ' '.$link : '');
4069 }
4070 } else {
4071 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
4072 if (getDolGlobalString($networkconstname)) {
4073 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
4074 if (preg_match('/^https?:\/\//i', $link)) {
4075 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4076 } elseif ($link) {
4077 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4078 }
4079 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
4080 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4081 if ($tmpvirginurl) {
4082 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4083 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4084
4085 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4086 if ($tmpvirginurl3) {
4087 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4088 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4089 }
4090
4091 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4092 if ($tmpvirginurl2) {
4093 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4094 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4095 }
4096 }
4097 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4098 if (preg_match('/^https?:\/\//i', $link)) {
4099 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4100 } else {
4101 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4102 }
4103 } else {
4104 $htmllink .= dol_escape_htmltag($value);
4105 }
4106 }
4107 $htmllink .= '</div>';
4108 } else {
4109 $langs->load("errors");
4110 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4111 }
4112
4113 if ($hookmanager) {
4114 $parameters = array(
4115 'value' => $value,
4116 'cid' => $cid,
4117 'socid' => $socid,
4118 'type' => $type,
4119 'dictsocialnetworks' => $dictsocialnetworks,
4120 );
4121
4122 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4123 if ($reshook > 0) {
4124 $htmllink = '';
4125 }
4126 $htmllink .= $hookmanager->resPrint;
4127 }
4128
4129 return $htmllink;
4130}
4131
4141function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4142{
4143 global $mysoc;
4144
4145 if (empty($profID) || empty($profIDtype)) {
4146 return '';
4147 }
4148 if (empty($countrycode)) {
4149 $countrycode = $mysoc->country_code;
4150 }
4151 $newProfID = $profID;
4152 $id = substr($profIDtype, -1);
4153 $ret = '';
4154 if (strtoupper($countrycode) == 'FR') {
4155 // France
4156 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4157
4158 if ($id == 1 && dol_strlen($newProfID) == 9) {
4159 // SIREN (ex: 123 123 123)
4160 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4161 }
4162 if ($id == 2 && dol_strlen($newProfID) == 14) {
4163 // SIRET (ex: 123 123 123 12345)
4164 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4165 }
4166 if ($id == 3 && dol_strlen($newProfID) == 5) {
4167 // NAF/APE (ex: 69.20Z)
4168 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4169 }
4170 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4171 // TVA intracommunautaire (ex: FR12 123 123 123)
4172 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4173 }
4174 }
4175 if (!empty($addcpButton)) {
4176 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4177 } else {
4178 $ret = $newProfID;
4179 }
4180 return $ret;
4181}
4182
4198function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4199{
4200 global $conf, $user, $langs, $mysoc, $hookmanager;
4201
4202 // Clean phone parameter
4203 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4204 if (empty($phone)) {
4205 return '';
4206 }
4207 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4208 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4209 }
4210 if (empty($countrycode) && is_object($mysoc)) {
4211 $countrycode = $mysoc->country_code;
4212 }
4213
4214 // Short format for small screens
4215 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4216 $separ = '';
4217 }
4218
4219 $newphone = $phone;
4220 $newphonewa = $phone;
4221 if (strtoupper($countrycode) == "FR") {
4222 // France
4223 if (dol_strlen($phone) == 10) {
4224 $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);
4225 } elseif (dol_strlen($phone) == 7) {
4226 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4227 } elseif (dol_strlen($phone) == 9) {
4228 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4229 } elseif (dol_strlen($phone) == 11) {
4230 $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);
4231 } elseif (dol_strlen($phone) == 12) {
4232 $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);
4233 } elseif (dol_strlen($phone) == 13) {
4234 $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);
4235 }
4236 } elseif (strtoupper($countrycode) == "CA") {
4237 if (dol_strlen($phone) == 10) {
4238 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4239 }
4240 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4241 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4242 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4243 }
4244 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4245 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4246 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4247 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4248 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4249 }
4250 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4251 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4252 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4253 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4254 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4255 }
4256 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4257 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4258 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4259 }
4260 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4261 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4262 $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);
4263 }
4264 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4265 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4266 $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);
4267 }
4268 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4269 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4270 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4271 }
4272 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4273 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4274 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4275 }
4276 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4277 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4278 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4279 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4280 $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);
4281 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4282 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4283 }
4284 } elseif (strtoupper($countrycode) == "ML") {//Mali
4285 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4286 $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);
4287 }
4288 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4289 if (dol_strlen($phone) == 11) {//ex: +66_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: +66_A_BCD_EF_GHI
4292 $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);
4293 }
4294 } elseif (strtoupper($countrycode) == "MU") {
4295 //Maurice
4296 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4297 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4298 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4299 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4300 }
4301 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4302 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4303 $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);
4304 }
4305 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4306 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4307 $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);
4308 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4309 $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);
4310 }
4311 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4312 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4313 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4314 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4315 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4316 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4317 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4318 }
4319 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4320 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4321 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4322 }
4323 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4324 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4325 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4326 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4327 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4328 }
4329 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4330 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4331 $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);
4332 }
4333 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4334 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4335 $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);
4336 }
4337 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4338 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4339 $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);
4340 }
4341 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4342 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4343 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4344 }
4345 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4346 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4347 $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);
4348 }
4349 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4350 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4351 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4352 }
4353 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4354 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4355 $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);
4356 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4357 $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);
4358 }
4359 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4360 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4361 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4362 }
4363 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4364 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4365 $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);
4366 }
4367 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4368 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4369 $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);
4370 }
4371 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4372 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4373 $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);
4374 }
4375 } elseif (strtoupper($countrycode) == "IT") {//Italie
4376 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4377 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4378 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4379 $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);
4380 }
4381 } elseif (strtoupper($countrycode) == "AU") {
4382 //Australie
4383 if (dol_strlen($phone) == 12) {
4384 //ex: +61_A_BCDE_FGHI
4385 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4386 }
4387 } elseif (strtoupper($countrycode) == "LU") {
4388 // Luxembourg
4389 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4390 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4391 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4392 $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);
4393 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4394 $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);
4395 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4396 $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);
4397 }
4398 } elseif (strtoupper($countrycode) == "PE") {
4399 // Peru
4400 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4401 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4402 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4403 $newphonewa = '+51'.$newphone;
4404 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4405 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4406 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4407 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4408 $newphonewa = $newphone;
4409 $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);
4410 }
4411 }
4412
4413 $newphoneastart = $newphoneaend = '';
4414 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4415 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
4416 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4417 $newphoneaend .= '</a>';
4418 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4419 if (empty($user->clicktodial_loaded)) {
4420 $user->fetch_clicktodial();
4421 }
4422
4423 // Define urlmask
4424 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4425 if (!empty($user->clicktodial_url)) {
4426 $urlmask = $user->clicktodial_url;
4427 }
4428
4429 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4430 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4431 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4432 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4433 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4434 // Those lines are for substitution
4435 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4436 '__PHONETO__' => urlencode($phone),
4437 '__LOGIN__' => $clicktodial_login,
4438 '__PASS__' => $clicktodial_password);
4439 $url = make_substitutions($url, $substitarray);
4440 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4441 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4442 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4443 $newphoneaend = '</a>';
4444 } else {
4445 // Old method
4446 $newphoneastart = '<a href="'.$url.'"';
4447 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4448 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4449 }
4450 $newphoneastart .= '>';
4451 $newphoneaend .= '</a>';
4452 }
4453 }
4454
4455 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4456 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4457 $type = 'AC_TEL';
4458 $addlinktoagenda = '';
4459 if ($addlink == 'AC_FAX') {
4460 $type = 'AC_FAX';
4461 }
4462 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4463 $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>';
4464 }
4465 if ($addlinktoagenda) {
4466 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4467 }
4468 }
4469 }
4470
4471 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4472 // Link to Whatsapp
4473 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4474 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4475 }
4476
4477 if (empty($titlealt)) {
4478 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4479 }
4480 $rep = '';
4481
4482 if ($hookmanager) {
4483 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4484 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4485 $rep .= $hookmanager->resPrint;
4486 }
4487 if (empty($reshook)) {
4488 $picto = '';
4489 if ($withpicto) {
4490 if ($withpicto == 'fax') {
4491 $picto = 'phoning_fax';
4492 } elseif ($withpicto == 'phone') {
4493 $picto = 'phoning';
4494 } elseif ($withpicto == 'mobile') {
4495 $picto = 'phoning_mobile';
4496 } else {
4497 $picto = '';
4498 }
4499 }
4500 if ($adddivfloat == 1) {
4501 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4502 } elseif (empty($adddivfloat)) {
4503 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4504 }
4505
4506 $rep .= $newphoneastart;
4507 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4508 if ($separ != 'hidenum') {
4509 $rep .= ($withpicto ? ' ' : '').$newphone;
4510 }
4511 $rep .= $newphoneaend;
4512
4513 if ($adddivfloat == 1) {
4514 $rep .= '</div>';
4515 } elseif (empty($adddivfloat)) {
4516 $rep .= '</span>';
4517 }
4518 }
4519
4520 return $rep;
4521}
4522
4530function dol_print_ip($ip, $mode = 0)
4531{
4532 global $langs;
4533
4534 $ret = '';
4535
4536 if (empty($mode)) {
4537 $ret .= $ip;
4538 }
4539
4540 if ($mode != 2) {
4541 $countrycode = dolGetCountryCodeFromIp($ip);
4542 if ($countrycode) { // If success, countrycode is us, fr, ...
4543 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4544 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4545 } else {
4546 $ret .= ' ('.$countrycode.')';
4547 }
4548 } else {
4549 // Nothing
4550 }
4551 }
4552
4553 return $ret;
4554}
4555
4564function getUserRemoteIP()
4565{
4566 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4567 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4568 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4569 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4570 } else {
4571 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4572 }
4573 } else {
4574 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4575 }
4576 } else {
4577 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4578 }
4579 return $ip;
4580}
4581
4590function isHTTPS()
4591{
4592 $isSecure = false;
4593 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4594 $isSecure = true;
4595 } 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') {
4596 $isSecure = true;
4597 }
4598 return $isSecure;
4599}
4600
4607function dolGetCountryCodeFromIp($ip)
4608{
4609 global $conf;
4610
4611 $countrycode = '';
4612
4613 if (isModEnabled('geoipmaxmind')) {
4614 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4615 //$ip='24.24.24.24';
4616 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4617 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4618 $geoip = new DolGeoIP('country', $datafile);
4619 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4620 $countrycode = $geoip->getCountryCodeFromIP($ip);
4621 }
4622
4623 return $countrycode;
4624}
4625
4626
4633function dol_user_country()
4634{
4635 global $conf, $langs, $user;
4636
4637 //$ret=$user->xxx;
4638 $ret = '';
4639 if (isModEnabled('geoipmaxmind')) {
4640 $ip = getUserRemoteIP();
4641 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4642 //$ip='24.24.24.24';
4643 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4644 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4645 $geoip = new DolGeoIP('country', $datafile);
4646 $countrycode = $geoip->getCountryCodeFromIP($ip);
4647 $ret = $countrycode;
4648 }
4649 return $ret;
4650}
4651
4664function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4665{
4666 global $conf, $user, $langs, $hookmanager;
4667
4668 $out = '';
4669
4670 if ($address) {
4671 if ($hookmanager) {
4672 $parameters = array('element' => $element, 'id' => $id);
4673 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4674 $out .= $hookmanager->resPrint;
4675 }
4676 if (empty($reshook)) {
4677 if (empty($charfornl)) {
4678 $out .= nl2br($address);
4679 } else {
4680 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4681 }
4682
4683 // TODO Remove this block, we can add this using the hook now
4684 $showgmap = $showomap = 0;
4685 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4686 $showgmap = 1;
4687 }
4688 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4689 $showgmap = 1;
4690 }
4691 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4692 $showgmap = 1;
4693 }
4694 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4695 $showgmap = 1;
4696 }
4697 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4698 $showomap = 1;
4699 }
4700 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4701 $showomap = 1;
4702 }
4703 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4704 $showomap = 1;
4705 }
4706 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4707 $showomap = 1;
4708 }
4709 if ($showgmap) {
4710 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4711 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4712 }
4713 if ($showomap) {
4714 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4715 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4716 }
4717 }
4718 }
4719 if ($noprint) {
4720 return $out;
4721 } else {
4722 print $out;
4723 return null;
4724 }
4725}
4726
4727
4737function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4738{
4739 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4740 return true;
4741 }
4742 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4743 return true;
4744 }
4745 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4746 return true;
4747 }
4748
4749 return false;
4750}
4751
4761function isValidMXRecord($domain)
4762{
4763 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4764 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4765 return 0;
4766 }
4767 if (function_exists('getmxrr')) {
4768 $mxhosts = array();
4769 $weight = array();
4770 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4771 if (count($mxhosts) > 1) {
4772 return 1;
4773 }
4774 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4775 return 1;
4776 }
4777
4778 return 0;
4779 }
4780 }
4781
4782 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4783 return -1;
4784}
4785
4793function isValidPhone($phone)
4794{
4795 return true;
4796}
4797
4798
4808function dolGetFirstLetters($s, $nbofchar = 1)
4809{
4810 $ret = '';
4811 $tmparray = explode(' ', $s);
4812 foreach ($tmparray as $tmps) {
4813 $ret .= dol_substr($tmps, 0, $nbofchar);
4814 }
4815
4816 return $ret;
4817}
4818
4819
4827function dol_strlen($string, $stringencoding = 'UTF-8')
4828{
4829 if (is_null($string)) {
4830 return 0;
4831 }
4832
4833 if (function_exists('mb_strlen')) {
4834 return mb_strlen($string, $stringencoding);
4835 } else {
4836 return strlen($string);
4837 }
4838}
4839
4850function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4851{
4852 global $langs;
4853
4854 if (empty($stringencoding)) {
4855 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4856 }
4857
4858 $ret = '';
4859 if (empty($trunconbytes)) {
4860 if (function_exists('mb_substr')) {
4861 $ret = mb_substr($string, $start, $length, $stringencoding);
4862 } else {
4863 $ret = substr($string, $start, $length);
4864 }
4865 } else {
4866 if (function_exists('mb_strcut')) {
4867 $ret = mb_strcut($string, $start, $length, $stringencoding);
4868 } else {
4869 $ret = substr($string, $start, $length);
4870 }
4871 }
4872 return $ret;
4873}
4874
4875
4889function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4890{
4891 global $conf;
4892
4893 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4894 return $string;
4895 }
4896
4897 if (empty($stringencoding)) {
4898 $stringencoding = 'UTF-8';
4899 }
4900 // reduce for small screen
4901 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4902 $size = round($size / 3);
4903 }
4904
4905 // We go always here
4906 if ($trunc == 'right') {
4907 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4908 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4909 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4910 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4911 } else {
4912 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4913 return $string;
4914 }
4915 } elseif ($trunc == 'middle') {
4916 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4917 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4918 $size1 = (int) round($size / 2);
4919 $size2 = (int) round($size / 2);
4920 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4921 } else {
4922 return $string;
4923 }
4924 } elseif ($trunc == 'left') {
4925 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4926 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4927 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4928 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4929 } else {
4930 return $string;
4931 }
4932 } elseif ($trunc == 'wrap') {
4933 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4934 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4935 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4936 } else {
4937 return $string;
4938 }
4939 } else {
4940 return 'BadParam3CallingDolTrunc';
4941 }
4942}
4943
4951function getPictoForType($key, $morecss = '')
4952{
4953 // Set array with type -> picto
4954 $type2picto = array(
4955 'varchar' => 'font',
4956 'text' => 'font',
4957 'html' => 'code',
4958 'int' => 'sort-numeric-down',
4959 'double' => 'sort-numeric-down',
4960 'price' => 'currency',
4961 'pricecy' => 'multicurrency',
4962 'password' => 'key',
4963 'boolean' => 'check-square',
4964 'date' => 'calendar',
4965 'datetime' => 'calendar',
4966 'duration' => 'hourglass',
4967 'phone' => 'phone',
4968 'mail' => 'email',
4969 'url' => 'url',
4970 'ip' => 'country',
4971 'select' => 'list',
4972 'sellist' => 'list',
4973 'stars' => 'fontawesome_star_fas',
4974 'radio' => 'check-circle',
4975 'checkbox' => 'list',
4976 'chkbxlst' => 'list',
4977 'link' => 'link',
4978 'icon' => "question",
4979 'point' => "country",
4980 'multipts' => 'country',
4981 'linestrg' => "country",
4982 'polygon' => "country",
4983 'separate' => 'minus'
4984 );
4985
4986 if (!empty($type2picto[$key])) {
4987 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4988 }
4989
4990 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4991}
4992
4993
5015function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
5016{
5017 global $conf;
5018
5019 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5020 $url = DOL_URL_ROOT;
5021 $theme = isset($conf->theme) ? $conf->theme : null;
5022 $path = 'theme/'.$theme;
5023 if (empty($picto)) {
5024 $picto = 'generic';
5025 }
5026
5027 // Define fullpathpicto to use into src
5028 if ($pictoisfullpath) {
5029 // Clean parameters
5030 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5031 $picto .= '.png';
5032 }
5033 $fullpathpicto = $picto;
5034 $reg = array();
5035 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5036 $morecss .= ($morecss ? ' ' : '').$reg[1];
5037 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5038 }
5039 } else {
5040 // $picto can not be null since replaced with 'generic' in that case
5041 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5042 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5043 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5044 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5045
5046 // Fix some values of $pictowithouttext
5047 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5048 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5049 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5050 }
5051
5052 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5053 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5054 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5055 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5056
5057 // Compatibility with old fontawesome versions
5058 if ($pictowithouttext == 'file-o') {
5059 $pictowithouttext = 'file';
5060 }
5061
5062 $pictowithouttextarray = explode('_', $pictowithouttext);
5063 $marginleftonlyshort = 0;
5064
5065 if (!empty($pictowithouttextarray[1])) {
5066 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5067 $fakey = 'fa-'.$pictowithouttextarray[0];
5068 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5069 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5070 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5071 } else {
5072 $fakey = 'fa-'.$pictowithouttext;
5073 $faprefix = 'fas';
5074 $facolor = '';
5075 $fasize = '';
5076 }
5077
5078 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5079 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5080 $morestyle = '';
5081 $reg = array();
5082 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5083 $morecss .= ($morecss ? ' ' : '').$reg[1];
5084 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5085 }
5086 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5087 $morestyle = $reg[1];
5088 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5089 }
5090 $moreatt = trim($moreatt);
5091
5092 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5093 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5094 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5095 $enabledisablehtml .= $titlealt;
5096 }*/
5097 $enabledisablehtml .= '</span>';
5098
5099 return $enabledisablehtml;
5100 }
5101
5102 if (empty($srconly) && in_array($pictowithouttext, array(
5103 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5104 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5105 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5106 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5107 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5108 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5109 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
5110 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5111 'commercial', 'companies',
5112 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5113 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5114 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5115 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5116 'hands-helping', 'help', 'holiday',
5117 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5118 'key', 'knowledgemanagement',
5119 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5120 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5121 'off', 'on', 'order',
5122 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5123 'stock', 'resize', 'service', 'stats',
5124 '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',
5125 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
5126 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5127 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5128 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5129 'technic', 'ticket',
5130 'error', 'warning',
5131 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
5132 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5133 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5134 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5135 'conferenceorbooth', 'eventorganization',
5136 'stamp', 'signature',
5137 'webportal'
5138 ))) {
5139 $fakey = $pictowithouttext;
5140 $facolor = '';
5141 $fasize = '';
5142 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5143 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'))) {
5144 $fa = 'far';
5145 }
5146 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
5147 $fa = 'fab';
5148 }
5149
5150 $arrayconvpictotofa = array(
5151 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5152 'asset' => 'money-check-alt', 'autofill' => 'fill',
5153 'back' => 'arrow-left', 'bank_account' => 'university',
5154 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5155 'bookcal' => 'calendar-check',
5156 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5157 'bom' => 'shapes',
5158 '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',
5159 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5160 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5161 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5162 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5163 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5164 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5165 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5166 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5167 'generic' => 'file', 'holiday' => 'umbrella-beach',
5168 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5169 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5170 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5171 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5172 'sign-out' => 'sign-out-alt',
5173 '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',
5174 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5175 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5176 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5177 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5178 'other' => 'square',
5179 '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',
5180 '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',
5181 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5182 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5183 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5184 'service' => 'concierge-bell',
5185 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5186 'status' => 'stop-circle',
5187 'stripe' => 'stripe-s', 'supplier' => 'building',
5188 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5189 'title_agenda' => 'calendar-alt',
5190 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5191 'jabber' => 'comment-o',
5192 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5193 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5194 'webportal' => 'door-open'
5195 );
5196 if ($conf->currency == 'EUR') {
5197 $arrayconvpictotofa['currency'] = 'euro-sign';
5198 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5199 } else {
5200 $arrayconvpictotofa['currency'] = 'dollar-sign';
5201 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5202 }
5203 if ($pictowithouttext == 'off') {
5204 $fakey = 'fa-square';
5205 $fasize = '1.3em';
5206 } elseif ($pictowithouttext == 'on') {
5207 $fakey = 'fa-check-square';
5208 $fasize = '1.3em';
5209 } elseif ($pictowithouttext == 'listlight') {
5210 $fakey = 'fa-download';
5211 $marginleftonlyshort = 1;
5212 } elseif ($pictowithouttext == 'printer') {
5213 $fakey = 'fa-print';
5214 $fasize = '1.2em';
5215 } elseif ($pictowithouttext == 'note') {
5216 $fakey = 'fa-sticky-note';
5217 $marginleftonlyshort = 1;
5218 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5219 $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');
5220 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5221 if (preg_match('/selected/', $pictowithouttext)) {
5222 $facolor = '#888';
5223 }
5224 $marginleftonlyshort = 1;
5225 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5226 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5227 } else {
5228 $fakey = 'fa-'.$pictowithouttext;
5229 }
5230
5231 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5232 $morecss .= ' em092';
5233 }
5234 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5235 $morecss .= ' em088';
5236 }
5237 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5238 $morecss .= ' em080';
5239 }
5240
5241 // Define $marginleftonlyshort
5242 $arrayconvpictotomarginleftonly = array(
5243 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5244 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5245 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5246 );
5247 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5248 $marginleftonlyshort = 0;
5249 }
5250
5251 // Add CSS
5252 $arrayconvpictotomorcess = array(
5253 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5254 'bank_account' => 'infobox-bank_account',
5255 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5256 'bookcal' => 'infobox-action',
5257 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5258 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5259 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5260 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5261 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5262 'incoterm' => 'infobox-supplier_proposal',
5263 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5264 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5265 'order' => 'infobox-commande',
5266 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5267 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5268 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5269 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5270 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5271 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5272 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5273 'resource' => 'infobox-action',
5274 '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',
5275 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5276 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5277 'vat' => 'infobox-bank_account',
5278 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5279 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5280 );
5281 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5282 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5283 }
5284
5285 // Define $color
5286 $arrayconvpictotocolor = array(
5287 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5288 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5289 'dynamicprice' => '#a69944',
5290 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5291 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5292 'lock' => '#ddd', 'lot' => '#a69944',
5293 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5294 'other' => '#ddd', 'world' => '#986c6a',
5295 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5296 //'shipment'=>'#a69944',
5297 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5298 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5299 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5300 'website' => '#304', 'workstation' => '#a69944'
5301 );
5302 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5303 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5304 }
5305
5306 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5307 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5308 $morestyle = '';
5309 $reg = array();
5310 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5311 $morecss .= ($morecss ? ' ' : '').$reg[1];
5312 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5313 }
5314 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5315 $morestyle = $reg[1];
5316 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5317 }
5318 $moreatt = trim($moreatt);
5319
5320 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5321 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5322 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5323 $enabledisablehtml .= $titlealt;
5324 }*/
5325 $enabledisablehtml .= '</span>';
5326
5327 return $enabledisablehtml;
5328 }
5329
5330 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5331 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5332 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5333 $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
5334 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5335 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5336 }
5337
5338 // If we ask an image into $url/$mymodule/img (instead of default path)
5339 $regs = array();
5340 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5341 $picto = $regs[1];
5342 $path = $regs[2]; // $path is $mymodule
5343 }
5344
5345 // Clean parameters
5346 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5347 $picto .= '.png';
5348 }
5349 // If alt path are defined, define url where img file is, according to physical path
5350 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5351 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5352 if ($type == 'main') {
5353 continue;
5354 }
5355 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5356 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5357 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5358 break;
5359 }
5360 }
5361
5362 // $url is '' or '/custom', $path is current theme or
5363 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5364 }
5365
5366 if ($srconly) {
5367 return $fullpathpicto;
5368 }
5369
5370 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5371 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
5372}
5373
5387function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5388{
5389 if (strpos($picto, '^') === 0) {
5390 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5391 } else {
5392 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5393 }
5394}
5395
5407function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5408{
5409 global $conf;
5410
5411 if (is_numeric($picto)) {
5412 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5413 //$picto = $leveltopicto[$picto];
5414 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5415 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5416 $picto .= '.png';
5417 }
5418
5419 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5420
5421 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5422}
5423
5435function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5436{
5437 global $conf;
5438
5439 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5440 $picto .= '.png';
5441 }
5442
5443 if ($pictoisfullpath) {
5444 $path = $picto;
5445 } else {
5446 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5447
5448 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5449 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5450
5451 if (file_exists($themepath)) {
5452 $path = $themepath;
5453 }
5454 }
5455 }
5456
5457 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5458}
5459
5473function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5474{
5475 global $langs;
5476
5477 if (empty($titlealt) || $titlealt == 'default') {
5478 if ($numaction == '-1' || $numaction == 'ST_NO') {
5479 $numaction = -1;
5480 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5481 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5482 $numaction = 0;
5483 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5484 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5485 $numaction = 1;
5486 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5487 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5488 $numaction = 2;
5489 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5490 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5491 $numaction = 3;
5492 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5493 } else {
5494 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5495 $numaction = 0;
5496 }
5497 }
5498 if (!is_numeric($numaction)) {
5499 $numaction = 0;
5500 }
5501
5502 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5503}
5504
5512function img_pdf($titlealt = 'default', $size = 3)
5513{
5514 global $langs;
5515
5516 if ($titlealt == 'default') {
5517 $titlealt = $langs->trans('Show');
5518 }
5519
5520 return img_picto($titlealt, 'pdf'.$size.'.png');
5521}
5522
5530function img_edit_add($titlealt = 'default', $other = '')
5531{
5532 global $langs;
5533
5534 if ($titlealt == 'default') {
5535 $titlealt = $langs->trans('Add');
5536 }
5537
5538 return img_picto($titlealt, 'edit_add.png', $other);
5539}
5547function img_edit_remove($titlealt = 'default', $other = '')
5548{
5549 global $langs;
5550
5551 if ($titlealt == 'default') {
5552 $titlealt = $langs->trans('Remove');
5553 }
5554
5555 return img_picto($titlealt, 'edit_remove.png', $other);
5556}
5557
5566function img_edit($titlealt = 'default', $float = 0, $other = '')
5567{
5568 global $langs;
5569
5570 if ($titlealt == 'default') {
5571 $titlealt = $langs->trans('Modify');
5572 }
5573
5574 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5575}
5576
5585function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5586{
5587 global $langs;
5588
5589 if ($titlealt == 'default') {
5590 $titlealt = $langs->trans('View');
5591 }
5592
5593 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5594
5595 return img_picto($titlealt, 'eye', $moreatt);
5596}
5597
5606function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5607{
5608 global $langs;
5609
5610 if ($titlealt == 'default') {
5611 $titlealt = $langs->trans('Delete');
5612 }
5613
5614 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5615}
5616
5624function img_printer($titlealt = "default", $other = '')
5625{
5626 global $langs;
5627 if ($titlealt == "default") {
5628 $titlealt = $langs->trans("Print");
5629 }
5630 return img_picto($titlealt, 'printer.png', $other);
5631}
5632
5640function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5641{
5642 global $langs;
5643
5644 if ($titlealt == 'default') {
5645 $titlealt = $langs->trans('Split');
5646 }
5647
5648 return img_picto($titlealt, 'split.png', $other);
5649}
5650
5658function img_help($usehelpcursor = 1, $usealttitle = 1)
5659{
5660 global $langs;
5661
5662 if ($usealttitle) {
5663 if (is_string($usealttitle)) {
5664 $usealttitle = dol_escape_htmltag($usealttitle);
5665 } else {
5666 $usealttitle = $langs->trans('Info');
5667 }
5668 }
5669
5670 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5671}
5672
5679function img_info($titlealt = 'default')
5680{
5681 global $langs;
5682
5683 if ($titlealt == 'default') {
5684 $titlealt = $langs->trans('Informations');
5685 }
5686
5687 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5688}
5689
5698function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5699{
5700 global $langs;
5701
5702 if ($titlealt == 'default') {
5703 $titlealt = $langs->trans('Warning');
5704 }
5705
5706 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5707 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5708}
5709
5716function img_error($titlealt = 'default')
5717{
5718 global $langs;
5719
5720 if ($titlealt == 'default') {
5721 $titlealt = $langs->trans('Error');
5722 }
5723
5724 return img_picto($titlealt, 'error.png');
5725}
5726
5734function img_next($titlealt = 'default', $moreatt = '')
5735{
5736 global $langs;
5737
5738 if ($titlealt == 'default') {
5739 $titlealt = $langs->trans('Next');
5740 }
5741
5742 //return img_picto($titlealt, 'next.png', $moreatt);
5743 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5744}
5745
5753function img_previous($titlealt = 'default', $moreatt = '')
5754{
5755 global $langs;
5756
5757 if ($titlealt == 'default') {
5758 $titlealt = $langs->trans('Previous');
5759 }
5760
5761 //return img_picto($titlealt, 'previous.png', $moreatt);
5762 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5763}
5764
5773function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5774{
5775 global $langs;
5776
5777 if ($titlealt == 'default') {
5778 $titlealt = $langs->trans('Down');
5779 }
5780
5781 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5782}
5783
5792function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5793{
5794 global $langs;
5795
5796 if ($titlealt == 'default') {
5797 $titlealt = $langs->trans('Up');
5798 }
5799
5800 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5801}
5802
5811function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5812{
5813 global $langs;
5814
5815 if ($titlealt == 'default') {
5816 $titlealt = $langs->trans('Left');
5817 }
5818
5819 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5820}
5821
5830function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5831{
5832 global $langs;
5833
5834 if ($titlealt == 'default') {
5835 $titlealt = $langs->trans('Right');
5836 }
5837
5838 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5839}
5840
5848function img_allow($allow, $titlealt = 'default')
5849{
5850 global $langs;
5851
5852 if ($titlealt == 'default') {
5853 $titlealt = $langs->trans('Active');
5854 }
5855
5856 if ($allow == 1) {
5857 return img_picto($titlealt, 'tick.png');
5858 }
5859
5860 return '-';
5861}
5862
5870function img_credit_card($brand, $morecss = null)
5871{
5872 if (is_null($morecss)) {
5873 $morecss = 'fa-2x';
5874 }
5875
5876 if ($brand == 'visa' || $brand == 'Visa') {
5877 $brand = 'cc-visa';
5878 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5879 $brand = 'cc-mastercard';
5880 } elseif ($brand == 'amex' || $brand == 'American Express') {
5881 $brand = 'cc-amex';
5882 } elseif ($brand == 'discover' || $brand == 'Discover') {
5883 $brand = 'cc-discover';
5884 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5885 $brand = 'cc-jcb';
5886 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5887 $brand = 'cc-diners-club';
5888 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5889 $brand = 'credit-card';
5890 }
5891
5892 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5893}
5894
5903function img_mime($file, $titlealt = '', $morecss = '')
5904{
5905 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5906
5907 $mimetype = dol_mimetype($file, '', 1);
5908 $mimeimg = dol_mimetype($file, '', 2);
5909 $mimefa = dol_mimetype($file, '', 4);
5910
5911 if (empty($titlealt)) {
5912 $titlealt = 'Mime type: '.$mimetype;
5913 }
5914
5915 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5916 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5917}
5918
5919
5927function img_search($titlealt = 'default', $other = '')
5928{
5929 global $langs;
5930
5931 if ($titlealt == 'default') {
5932 $titlealt = $langs->trans('Search');
5933 }
5934
5935 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5936
5937 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5938 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5939
5940 return $input;
5941}
5942
5950function img_searchclear($titlealt = 'default', $other = '')
5951{
5952 global $langs;
5953
5954 if ($titlealt == 'default') {
5955 $titlealt = $langs->trans('Search');
5956 }
5957
5958 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5959
5960 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5961 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5962
5963 return $input;
5964}
5965
5978function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5979{
5980 global $conf, $langs;
5981
5982 if ($infoonimgalt) {
5983 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5984 } else {
5985 if (empty($conf->use_javascript_ajax)) {
5986 $textfordropdown = '';
5987 }
5988
5989 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5990 $fa = 'info-circle';
5991 if ($picto == 'warning') {
5992 $fa = 'exclamation-triangle';
5993 }
5994 $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> ';
5995 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5996 $result .= ($nodiv ? '' : '</div>');
5997
5998 if ($textfordropdown) {
5999 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
6000 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
6001 jQuery(document).ready(function() {
6002 jQuery(".'.$class.'text").click(function() {
6003 console.log("toggle text");
6004 jQuery(".'.$class.'").toggle();
6005 });
6006 });
6007 </script>';
6008
6009 $result = $tmpresult.$result;
6010 }
6011 }
6012
6013 return $result;
6014}
6015
6016
6028function dol_print_error($db = null, $error = '', $errors = null)
6029{
6030 global $conf, $langs, $user, $argv;
6031 global $dolibarr_main_prod;
6032
6033 $out = '';
6034 $syslog = '';
6035
6036 // If error occurs before the $lang object was loaded
6037 if (!$langs) {
6038 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6039 $langs = new Translate('', $conf);
6040 $langs->load("main");
6041 }
6042
6043 // Load translation files required by the error messages
6044 $langs->loadLangs(array('main', 'errors'));
6045
6046 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6047 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6048 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6049 $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";
6050 }
6051 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6052
6053 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6054 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6055 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6056 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6057 }
6058 if ($user instanceof User) {
6059 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6060 }
6061 if (function_exists("phpversion")) {
6062 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6063 }
6064 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6065 if (function_exists("php_uname")) {
6066 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6067 }
6068 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6069 $out .= "<br>\n";
6070 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
6071 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
6072 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
6073 $out .= "<br>\n";
6074 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
6075 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
6076 } else { // Mode CLI
6077 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6078 $syslog .= "pid=".dol_getmypid();
6079 }
6080
6081 if (!empty($conf->modules)) {
6082 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6083 }
6084
6085 if (is_object($db)) {
6086 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6087 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6088 $lastqueryerror = $db->lastqueryerror();
6089 if (!utf8_check($lastqueryerror)) {
6090 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6091 }
6092 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6093 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6094 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6095 $out .= "<br>\n";
6096 } else { // Mode CLI
6097 // No dol_escape_htmltag for output, we are in CLI mode
6098 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6099 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6100 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6101 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6102 }
6103 $syslog .= ", sql=".$db->lastquery();
6104 $syslog .= ", db_error=".$db->lasterror();
6105 }
6106
6107 if ($error || $errors) {
6108 // Merge all into $errors array
6109 if (is_array($error) && is_array($errors)) {
6110 $errors = array_merge($error, $errors);
6111 } elseif (is_array($error)) { // deprecated, use second parameters
6112 $errors = $error;
6113 } elseif (is_array($errors) && !empty($error)) {
6114 $errors = array_merge(array($error), $errors);
6115 } elseif (!empty($error)) {
6116 $errors = array_merge(array($error), array($errors));
6117 }
6118
6119 $langs->load("errors");
6120
6121 foreach ($errors as $msg) {
6122 if (empty($msg)) {
6123 continue;
6124 }
6125 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6126 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6127 } else { // Mode CLI
6128 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6129 }
6130 $syslog .= ", msg=".$msg;
6131 }
6132 }
6133 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6134 xdebug_print_function_stack();
6135 $out .= '<b>XDebug information:</b>'."<br>\n";
6136 $out .= 'File: '.xdebug_call_file()."<br>\n";
6137 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6138 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6139 $out .= "<br>\n";
6140 }
6141
6142 // Return a http header with error code if possible
6143 if (!headers_sent()) {
6144 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6145 top_httphead();
6146 }
6147 //http_response_code(500); // If we use 500, message is not output with some command line tools
6148 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
6149 }
6150
6151 if (empty($dolibarr_main_prod)) {
6152 print $out;
6153 } else {
6154 if (empty($langs->defaultlang)) {
6155 $langs->setDefaultLang();
6156 }
6157 $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.
6158 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6159 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";
6160 print $langs->trans("DolibarrHasDetectedError").'. ';
6161 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6162 if (!defined("MAIN_CORE_ERROR")) {
6163 define("MAIN_CORE_ERROR", 1);
6164 }
6165 }
6166
6167 dol_syslog("Error ".$syslog, LOG_ERR);
6168}
6169
6180function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6181{
6182 global $langs;
6183
6184 if (empty($email)) {
6185 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6186 }
6187
6188 $langs->load("errors");
6189 $now = dol_now();
6190
6191 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6192 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6193 if ($errormessage) {
6194 print '<br><br>'.$errormessage;
6195 }
6196 if (is_array($errormessages) && count($errormessages)) {
6197 foreach ($errormessages as $mesgtoshow) {
6198 print '<br><br>'.$mesgtoshow;
6199 }
6200 }
6201 print '</div></div>';
6202}
6203
6220function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6221{
6222 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6223}
6224
6243function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6244{
6245 global $langs, $form;
6246 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6247
6248 if ($moreattrib == 'class="right"') {
6249 $prefix .= 'right '; // For backward compatibility
6250 }
6251
6252 $sortorder = strtoupper($sortorder);
6253 $out = '';
6254 $sortimg = '';
6255
6256 $tag = 'th';
6257 if ($thead == 2) {
6258 $tag = 'div';
6259 }
6260
6261 $tmpsortfield = explode(',', $sortfield);
6262 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6263 $tmpfield = explode(',', $field);
6264 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6265
6266 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6267 $prefix = 'wrapcolumntitle '.$prefix;
6268 }
6269
6270 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6271 // If field is used as sort criteria we use a specific css class liste_titre_sel
6272 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6273 $liste_titre = 'liste_titre';
6274 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6275 $liste_titre = 'liste_titre_sel';
6276 }
6277
6278 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6279 //$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)).'"' : '');
6280 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6281 $tagstart .= '>';
6282
6283 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6284 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6285 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6286 $options = preg_replace('/&+/i', '&', $options);
6287 if (!preg_match('/^&/', $options)) {
6288 $options = '&'.$options;
6289 }
6290
6291 $sortordertouseinlink = '';
6292 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6293 if (preg_match('/^DESC/i', $sortorder)) {
6294 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6295 } else { // We reverse the var $sortordertouseinlink
6296 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6297 }
6298 } else { // We are on field that is the first current sorting criteria
6299 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6300 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6301 } else {
6302 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6303 }
6304 }
6305 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6306 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6307 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6308 $out .= '>';
6309 }
6310 if ($tooltip) {
6311 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6312 if (preg_match('/:\w+$/', $tooltip)) {
6313 $tmptooltip = explode(':', $tooltip);
6314 } else {
6315 $tmptooltip = array($tooltip);
6316 }
6317 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6318 } else {
6319 $out .= $langs->trans($name);
6320 }
6321
6322 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6323 $out .= '</a>';
6324 }
6325
6326 if (empty($thead) && $field) { // If this is a sort field
6327 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6328 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6329 $options = preg_replace('/&+/i', '&', $options);
6330 if (!preg_match('/^&/', $options)) {
6331 $options = '&'.$options;
6332 }
6333
6334 if (!$sortorder || ($field1 != $sortfield1)) {
6335 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6336 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6337 } else {
6338 if (preg_match('/^DESC/', $sortorder)) {
6339 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6340 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6341 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6342 }
6343 if (preg_match('/^ASC/', $sortorder)) {
6344 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6345 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6346 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6347 }
6348 }
6349 }
6350
6351 $tagend = '</'.$tag.'>';
6352
6353 $out = $tagstart.$sortimg.$out.$tagend;
6354
6355 return $out;
6356}
6357
6366function print_titre($title)
6367{
6368 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6369
6370 print '<div class="titre">'.$title.'</div>';
6371}
6372
6384function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6385{
6386 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6387}
6388
6402function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6403{
6404 $return = '';
6405
6406 if ($picto == 'setup') {
6407 $picto = 'generic';
6408 }
6409
6410 $return .= "\n";
6411 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6412 $return .= '<tr class="toptitle">';
6413 if ($picto) {
6414 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6415 }
6416 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6417 $return .= '<div class="titre inline-block">';
6418 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6419 $return .= '</div>';
6420 $return .= '</td>';
6421 if (dol_strlen($morehtmlcenter)) {
6422 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6423 }
6424 if (dol_strlen($morehtmlright)) {
6425 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6426 }
6427 $return .= '</tr></table>'."\n";
6428
6429 return $return;
6430}
6431
6455function 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 = '')
6456{
6457 global $conf, $langs;
6458
6459 $savlimit = $limit;
6460 $savtotalnboflines = $totalnboflines;
6461 if (is_numeric($totalnboflines)) {
6462 $totalnboflines = abs($totalnboflines);
6463 }
6464
6465 // Detect if there is a subtitle
6466 $subtitle = '';
6467 $tmparray = preg_split('/<br>/i', $title, 2);
6468 if (!empty($tmparray[1])) {
6469 $title = $tmparray[0];
6470 $subtitle = $tmparray[1];
6471 }
6472
6473 $page = (int) $page;
6474
6475 if ($picto == 'setup') {
6476 $picto = 'title_setup.png';
6477 }
6478 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6479 $picto = 'title.gif';
6480 }
6481 if ($limit < 0) {
6482 $limit = $conf->liste_limit;
6483 }
6484
6485 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6486 $nextpage = 1;
6487 } else {
6488 $nextpage = 0;
6489 }
6490 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6491
6492 print "\n";
6493 print "<!-- Begin print_barre_liste -->\n";
6494 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6495 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6496
6497 // Left
6498
6499 if ($picto && $title) {
6500 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6501 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6502 print '</td>';
6503 }
6504
6505 print '<td class="nobordernopadding valignmiddle col-title">';
6506 print '<div class="titre inline-block">';
6507 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()
6508 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6509 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6510 }
6511 print '</div>';
6512 if (!empty($subtitle)) {
6513 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6514 }
6515 print '</td>';
6516
6517 // Center
6518 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6519 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6520 }
6521
6522 // Right
6523 print '<td class="nobordernopadding valignmiddle right col-right">';
6524 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6525 if ($sortfield) {
6526 $options .= "&sortfield=".urlencode($sortfield);
6527 }
6528 if ($sortorder) {
6529 $options .= "&sortorder=".urlencode($sortorder);
6530 }
6531 // Show navigation bar
6532 $pagelist = '';
6533 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6534 if ($totalnboflines) { // If we know total nb of lines
6535 // Define nb of extra page links before and after selected page + ... + first or last
6536 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6537
6538 if ($limit > 0) {
6539 $nbpages = ceil($totalnboflines / $limit);
6540 } else {
6541 $nbpages = 1;
6542 }
6543 $cpt = ($page - $maxnbofpage);
6544 if ($cpt < 0) {
6545 $cpt = 0;
6546 }
6547
6548 if ($cpt >= 1) {
6549 if (empty($pagenavastextinput)) {
6550 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6551 if ($cpt > 2) {
6552 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6553 } elseif ($cpt == 2) {
6554 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6555 }
6556 }
6557 }
6558
6559 do {
6560 if ($pagenavastextinput) {
6561 if ($cpt == $page) {
6562 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6563 $pagelist .= '/';
6564 }
6565 } else {
6566 if ($cpt == $page) {
6567 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6568 } else {
6569 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6570 }
6571 }
6572 $cpt++;
6573 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6574
6575 if (empty($pagenavastextinput)) {
6576 if ($cpt < $nbpages) {
6577 if ($cpt < $nbpages - 2) {
6578 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6579 } elseif ($cpt == $nbpages - 2) {
6580 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6581 }
6582 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6583 }
6584 } else {
6585 //var_dump($page.' '.$cpt.' '.$nbpages);
6586 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6587 }
6588 } else {
6589 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6590 }
6591 }
6592
6593 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6594 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
6595 }
6596
6597 // js to autoselect page field on focus
6598 if ($pagenavastextinput) {
6599 print ajax_autoselect('.pageplusone');
6600 }
6601
6602 print '</td>';
6603 print '</tr>';
6604
6605 print '</table>'."\n";
6606
6607 // Center
6608 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6609 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6610 }
6611
6612 print "<!-- End title -->\n\n";
6613}
6614
6631function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6632{
6633 global $conf, $langs;
6634
6635 print '<div class="pagination"><ul>';
6636 if ($beforearrows) {
6637 print '<li class="paginationbeforearrows">';
6638 print $beforearrows;
6639 print '</li>';
6640 }
6641
6642 if (empty($hidenavigation)) {
6643 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6644 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6645 $pagesizechoices .= ',5000:5000';
6646 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6647 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6648 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6649 //$pagesizechoices .= ',2:2';
6650 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6651 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6652 }
6653
6654 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6655 print '<li class="pagination">';
6656 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.'">';
6657 print '<datalist id="limitlist">';
6658 } else {
6659 print '<li class="paginationcombolimit valignmiddle">';
6660 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")).'">';
6661 }
6662 $tmpchoice = explode(',', $pagesizechoices);
6663 $tmpkey = $limit.':'.$limit;
6664 if (!in_array($tmpkey, $tmpchoice)) {
6665 $tmpchoice[$tmpkey] = $tmpkey;
6666 }
6667 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6668 if (!in_array($tmpkey, $tmpchoice)) {
6669 $tmpchoice[$tmpkey] = $tmpkey;
6670 }
6671 asort($tmpchoice, SORT_NUMERIC);
6672 foreach ($tmpchoice as $val) {
6673 $selected = '';
6674 $tmp = explode(':', $val);
6675 $key = $tmp[0];
6676 $val = $tmp[1];
6677 if ($key != '' && $val != '') {
6678 if ((int) $key == (int) $limit) {
6679 $selected = ' selected="selected"';
6680 }
6681 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6682 }
6683 }
6684 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6685 print '</datalist>';
6686 } else {
6687 print '</select>';
6688 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
6689 //print ajax_combobox("limit");
6690 }
6691
6692 if ($conf->use_javascript_ajax) {
6693 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6694 <script>
6695 jQuery(document).ready(function () {
6696 jQuery(".selectlimit").change(function() {
6697 console.log("We change limit so we submit the form");
6698 $(this).parents(\'form:first\').submit();
6699 });
6700 });
6701 </script>
6702 ';
6703 }
6704 print '</li>';
6705 }
6706 if ($page > 0) {
6707 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>';
6708 }
6709 if ($betweenarrows) {
6710 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6711 print $betweenarrows;
6712 print '<!--</div>-->';
6713 }
6714 if ($nextpage > 0) {
6715 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>';
6716 }
6717 if ($afterarrows) {
6718 print '<li class="paginationafterarrows">';
6719 print $afterarrows;
6720 print '</li>';
6721 }
6722 }
6723 print '</ul></div>'."\n";
6724}
6725
6726
6738function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6739{
6740 $morelabel = '';
6741
6742 if (preg_match('/%/', $rate)) {
6743 $rate = str_replace('%', '', $rate);
6744 $addpercent = true;
6745 }
6746 $reg = array();
6747 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6748 $morelabel = ' ('.$reg[1].')';
6749 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6750 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6751 }
6752 if (preg_match('/\*/', $rate)) {
6753 $rate = str_replace('*', '', $rate);
6754 $info_bits |= 1;
6755 }
6756
6757 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6758 if (!preg_match('/\//', $rate)) {
6759 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6760 } else {
6761 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6762 $ret = $rate.($addpercent ? '%' : '');
6763 }
6764 if (($info_bits & 1) && $usestarfornpr >= 0) {
6765 $ret .= ' *';
6766 }
6767 $ret .= $morelabel;
6768 return $ret;
6769}
6770
6771
6787function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6788{
6789 global $langs, $conf;
6790
6791 // Clean parameters
6792 if (empty($amount)) {
6793 $amount = 0; // To have a numeric value if amount not defined or = ''
6794 }
6795 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6796 if ($rounding == -1) {
6797 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6798 }
6799 $nbdecimal = $rounding;
6800
6801 if ($outlangs === 'none') {
6802 // Use international separators
6803 $dec = '.';
6804 $thousand = '';
6805 } else {
6806 // Output separators by default (french)
6807 $dec = ',';
6808 $thousand = ' ';
6809
6810 // If $outlangs not forced, we use use language
6811 if (!($outlangs instanceof Translate)) {
6812 $outlangs = $langs;
6813 }
6814
6815 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6816 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6817 }
6818 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6819 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6820 }
6821 if ($thousand == 'None') {
6822 $thousand = '';
6823 } elseif ($thousand == 'Space') {
6824 $thousand = ' ';
6825 }
6826 }
6827 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6828
6829 //print "amount=".$amount."-";
6830 $amount = str_replace(',', '.', $amount); // should be useless
6831 //print $amount."-";
6832 $data = explode('.', $amount);
6833 $decpart = isset($data[1]) ? $data[1] : '';
6834 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6835 //print "decpart=".$decpart."<br>";
6836 $end = '';
6837
6838 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6839 if (dol_strlen($decpart) > $nbdecimal) {
6840 $nbdecimal = dol_strlen($decpart);
6841 }
6842
6843 // If nbdecimal is higher than max to show
6844 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6845 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6846 $nbdecimal = $nbdecimalmaxshown;
6847 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6848 // If output is truncated, we show ...
6849 $end = '...';
6850 }
6851 }
6852
6853 // If force rounding
6854 if ((string) $forcerounding != '-1') {
6855 if ($forcerounding === 'MU') {
6856 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6857 } elseif ($forcerounding === 'MT') {
6858 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
6859 } elseif ($forcerounding >= 0) {
6860 $nbdecimal = $forcerounding;
6861 }
6862 }
6863
6864 // Format number
6865 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6866 if ($form) {
6867 $output = preg_replace('/\s/', '&nbsp;', $output);
6868 $output = preg_replace('/\'/', '&#039;', $output);
6869 }
6870 // Add symbol of currency if requested
6871 $cursymbolbefore = $cursymbolafter = '';
6872 if ($currency_code && is_object($outlangs)) {
6873 if ($currency_code == 'auto') {
6874 $currency_code = $conf->currency;
6875 }
6876
6877 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6878 $listoflanguagesbefore = array('nl_NL');
6879 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6880 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6881 } else {
6882 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6883 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6884 }
6885 }
6886 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6887
6888 return $output;
6889}
6890
6915function price2num($amount, $rounding = '', $option = 0)
6916{
6917 global $langs, $conf;
6918
6919 // Clean parameters
6920 if (is_null($amount)) {
6921 $amount = '';
6922 }
6923
6924 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6925 // Numbers must be '1234.56'
6926 // Decimal delimiter for PHP and database SQL requests must be '.'
6927 $dec = ',';
6928 $thousand = ' ';
6929 if (is_null($langs)) { // $langs is not defined, we use english values.
6930 $dec = '.';
6931 $thousand = ',';
6932 } else {
6933 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6934 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6935 }
6936 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6937 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6938 }
6939 }
6940 if ($thousand == 'None') {
6941 $thousand = '';
6942 } elseif ($thousand == 'Space') {
6943 $thousand = ' ';
6944 }
6945 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6946
6947 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6948 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6949 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6950 if (!is_numeric($amount)) {
6951 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6952 }
6953
6954 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
6955 $amount = str_replace($thousand, '', $amount);
6956 }
6957
6958 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6959 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6960 // So if number was already a good number, it is converted into local Dolibarr setup.
6961 if (is_numeric($amount)) {
6962 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6963 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6964 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6965 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6966 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6967 }
6968 //print "QQ".$amount."<br>\n";
6969
6970 // Now make replace (the main goal of function)
6971 if ($thousand != ',' && $thousand != '.') {
6972 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6973 }
6974
6975 $amount = str_replace(' ', '', $amount); // To avoid spaces
6976 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6977 $amount = str_replace($dec, '.', $amount);
6978
6979 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6980 }
6981 //print ' XX'.$amount.' '.$rounding;
6982
6983 // Now, $amount is a real PHP float number. We make a rounding if required.
6984 if ($rounding) {
6985 $nbofdectoround = '';
6986 if ($rounding == 'MU') {
6987 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6988 } elseif ($rounding == 'MT') {
6989 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6990 } elseif ($rounding == 'MS') {
6991 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6992 } elseif ($rounding == 'CU') {
6993 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6994 } elseif ($rounding == 'CT') {
6995 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6996 } elseif (is_numeric($rounding)) {
6997 $nbofdectoround = (int) $rounding;
6998 }
6999
7000 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7001 if (dol_strlen($nbofdectoround)) {
7002 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7003 } else {
7004 return 'ErrorBadParameterProvidedToFunction';
7005 }
7006 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7007
7008 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7009 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7010 if (is_numeric($amount)) {
7011 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7012 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7013 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7014 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7015 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7016 }
7017 //print "TT".$amount.'<br>';
7018
7019 // Always make replace because each math function (like round) replace
7020 // with local values and we want a number that has a SQL string format x.y
7021 if ($thousand != ',' && $thousand != '.') {
7022 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7023 }
7024
7025 $amount = str_replace(' ', '', $amount); // To avoid spaces
7026 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7027 $amount = str_replace($dec, '.', $amount);
7028
7029 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7030 }
7031
7032 return $amount;
7033}
7034
7047function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7048{
7049 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7050
7051 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7052 $dimension *= 1000000;
7053 $unit -= 6;
7054 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7055 $dimension *= 1000;
7056 $unit -= 3;
7057 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7058 $dimension /= 1000000;
7059 $unit += 6;
7060 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7061 $dimension /= 1000;
7062 $unit += 3;
7063 }
7064 // Special case when we want output unit into pound or ounce
7065 /* TODO
7066 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7067 {
7068 $dimension = // convert dimension from standard unit into ounce or pound
7069 $unit = $forceunitoutput;
7070 }
7071 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7072 {
7073 $dimension = // convert dimension from standard unit into ounce or pound
7074 $unit = $forceunitoutput;
7075 }*/
7076
7077 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7078 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7079 $ret .= ' '.measuringUnitString(0, $type, (string) $unit, $use_short_label, $outputlangs);
7080
7081 return $ret;
7082}
7083
7084
7097function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7098{
7099 global $db, $conf, $mysoc;
7100
7101 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7102 $thirdparty_seller = $mysoc;
7103 }
7104
7105 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);
7106
7107 $vatratecleaned = $vatrate;
7108 $reg = array();
7109 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7110 $vatratecleaned = trim($reg[1]);
7111 $vatratecode = $reg[2];
7112 }
7113
7114 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7115 {
7116 return 0;
7117 }*/
7118
7119 // Some test to guess with no need to make database access
7120 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7121 if ($local == 1) {
7122 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7123 return 0;
7124 }
7125 if ($thirdparty_seller->id == $mysoc->id) {
7126 if (!$thirdparty_buyer->localtax1_assuj) {
7127 return 0;
7128 }
7129 } else {
7130 if (!$thirdparty_seller->localtax1_assuj) {
7131 return 0;
7132 }
7133 }
7134 }
7135
7136 if ($local == 2) {
7137 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7138 if (!$mysoc->localtax2_assuj) {
7139 return 0; // If main vat is 0, IRPF may be different than 0.
7140 }
7141 if ($thirdparty_seller->id == $mysoc->id) {
7142 if (!$thirdparty_buyer->localtax2_assuj) {
7143 return 0;
7144 }
7145 } else {
7146 if (!$thirdparty_seller->localtax2_assuj) {
7147 return 0;
7148 }
7149 }
7150 }
7151 } else {
7152 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7153 return 0;
7154 }
7155 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7156 return 0;
7157 }
7158 }
7159
7160 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7161 if (in_array($mysoc->country_code, array('ES'))) {
7162 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7163 }
7164
7165 // Search local taxes
7166 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7167 if ($local == 1) {
7168 if ($thirdparty_seller != $mysoc) {
7169 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7170 return $thirdparty_seller->localtax1_value;
7171 }
7172 } else { // i am the seller
7173 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7174 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7175 }
7176 }
7177 }
7178 if ($local == 2) {
7179 if ($thirdparty_seller != $mysoc) {
7180 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7181 // TODO We should also return value defined on thirdparty only if defined
7182 return $thirdparty_seller->localtax2_value;
7183 }
7184 } else { // i am the seller
7185 if (in_array($mysoc->country_code, array('ES'))) {
7186 return $thirdparty_buyer->localtax2_value;
7187 } else {
7188 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7189 }
7190 }
7191 }
7192 }
7193
7194 // By default, search value of local tax on line of common tax
7195 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7196 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7197 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7198 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7199 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7200 if (!empty($vatratecode)) {
7201 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7202 } else {
7203 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7204 }
7205
7206 $resql = $db->query($sql);
7207
7208 if ($resql) {
7209 $obj = $db->fetch_object($resql);
7210 if ($obj) {
7211 if ($local == 1) {
7212 return $obj->localtax1;
7213 } elseif ($local == 2) {
7214 return $obj->localtax2;
7215 }
7216 }
7217 }
7218
7219 return 0;
7220}
7221
7222
7231function isOnlyOneLocalTax($local)
7232{
7233 $tax = get_localtax_by_third($local);
7234
7235 $valors = explode(":", $tax);
7236
7237 if (count($valors) > 1) {
7238 return false;
7239 } else {
7240 return true;
7241 }
7242}
7243
7250function get_localtax_by_third($local)
7251{
7252 global $db, $mysoc;
7253
7254 $sql = " SELECT t.localtax".$local." as localtax";
7255 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7256 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7257 $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";
7258 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7259 $sql .= " AND t.localtax".$local."_type <> '0'";
7260 $sql .= " ORDER BY t.rowid DESC";
7261
7262 $resql = $db->query($sql);
7263 if ($resql) {
7264 $obj = $db->fetch_object($resql);
7265 if ($obj) {
7266 return $obj->localtax;
7267 } else {
7268 return '0';
7269 }
7270 }
7271
7272 return 'Error';
7273}
7274
7275
7287function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7288{
7289 global $db;
7290
7291 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7292
7293 // Search local taxes
7294 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7295 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7296 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7297 if ($firstparamisid) {
7298 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7299 } else {
7300 $vatratecleaned = $vatrate;
7301 $vatratecode = '';
7302 $reg = array();
7303 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7304 $vatratecleaned = $reg[1];
7305 $vatratecode = $reg[2];
7306 }
7307
7308 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7309 /*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 ??
7310 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7311 $sql .= " WHERE t.fk_pays = c.rowid";
7312 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7313 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7314 } else {
7315 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7316 }
7317 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7318 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7319 if ($vatratecode) {
7320 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7321 }
7322 }
7323
7324 $resql = $db->query($sql);
7325 if ($resql) {
7326 $obj = $db->fetch_object($resql);
7327 if ($obj) {
7328 return array(
7329 'rowid' => $obj->rowid,
7330 'code' => $obj->code,
7331 'rate' => $obj->rate,
7332 'localtax1' => $obj->localtax1,
7333 'localtax1_type' => $obj->localtax1_type,
7334 'localtax2' => $obj->localtax2,
7335 'localtax2_type' => $obj->localtax2_type,
7336 'npr' => $obj->npr,
7337 'accountancy_code_sell' => $obj->accountancy_code_sell,
7338 'accountancy_code_buy' => $obj->accountancy_code_buy
7339 );
7340 } else {
7341 return array();
7342 }
7343 } else {
7344 dol_print_error($db);
7345 }
7346
7347 return array();
7348}
7349
7366function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7367{
7368 global $db, $mysoc;
7369
7370 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7371
7372 // Search local taxes
7373 $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";
7374 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7375 if ($firstparamisid) {
7376 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7377 } else {
7378 $vatratecleaned = $vatrate;
7379 $vatratecode = '';
7380 $reg = array();
7381 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7382 $vatratecleaned = $reg[1];
7383 $vatratecode = $reg[2];
7384 }
7385
7386 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7387 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7388 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7389 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7390 } else {
7391 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7392 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7393 }
7394 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7395 if ($vatratecode) {
7396 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7397 }
7398 }
7399
7400 $resql = $db->query($sql);
7401 if ($resql) {
7402 $obj = $db->fetch_object($resql);
7403
7404 if ($obj) {
7405 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7406
7407 if ($local == 1) {
7408 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7409 } elseif ($local == 2) {
7410 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7411 } else {
7412 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);
7413 }
7414 }
7415 }
7416
7417 return array();
7418}
7419
7430function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7431{
7432 global $db, $mysoc;
7433
7434 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7435
7436 $ret = 0;
7437 $found = 0;
7438
7439 if ($idprod > 0) {
7440 // Load product
7441 $product = new Product($db);
7442 $product->fetch($idprod);
7443
7444 if (($mysoc->country_code == $thirdpartytouse->country_code)
7445 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7446 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7447 ) {
7448 // If country of thirdparty to consider is ours
7449 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7450 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7451 if ($result > 0) {
7452 $ret = $product->vatrate_supplier;
7453 if ($product->default_vat_code_supplier) {
7454 $ret .= ' ('.$product->default_vat_code_supplier.')';
7455 }
7456 $found = 1;
7457 }
7458 }
7459 if (!$found) {
7460 $ret = $product->tva_tx; // Default sales vat of product
7461 if ($product->default_vat_code) {
7462 $ret .= ' ('.$product->default_vat_code.')';
7463 }
7464 $found = 1;
7465 }
7466 } else {
7467 // TODO Read default product vat according to product and an other countrycode.
7468 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7469 }
7470 }
7471
7472 if (!$found) {
7473 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7474 // 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).
7475 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7476 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7477 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7478 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7479 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7480 $sql .= $db->plimit(1);
7481
7482 $resql = $db->query($sql);
7483 if ($resql) {
7484 $obj = $db->fetch_object($resql);
7485 if ($obj) {
7486 $ret = $obj->vat_rate;
7487 if ($obj->default_vat_code) {
7488 $ret .= ' ('.$obj->default_vat_code.')';
7489 }
7490 }
7491 $db->free($resql);
7492 } else {
7493 dol_print_error($db);
7494 }
7495 } else {
7496 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7497 // '1.23'
7498 // or '1.23 (CODE)'
7499 $defaulttx = '';
7500 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7501 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7502 }
7503 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7504 $defaultcode = $reg[1];
7505 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7506 }*/
7507
7508 $ret = $defaulttx;
7509 }
7510 }
7511
7512 dol_syslog("get_product_vat_for_country: ret=".$ret);
7513 return $ret;
7514}
7515
7525function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7526{
7527 global $db, $mysoc;
7528
7529 if (!class_exists('Product')) {
7530 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7531 }
7532
7533 $ret = 0;
7534 $found = 0;
7535
7536 if ($idprod > 0) {
7537 // Load product
7538 $product = new Product($db);
7539 $result = $product->fetch($idprod);
7540
7541 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7542 /* Not defined yet, so we don't use this
7543 if ($local==1) $ret=$product->localtax1_tx;
7544 elseif ($local==2) $ret=$product->localtax2_tx;
7545 $found=1;
7546 */
7547 } else {
7548 // TODO Read default product vat according to product and another countrycode.
7549 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7550 }
7551 }
7552
7553 if (!$found) {
7554 // If vat of product for the country not found or not defined, we return higher vat of country.
7555 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7556 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7557 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7558 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7559 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7560 $sql .= $db->plimit(1);
7561
7562 $resql = $db->query($sql);
7563 if ($resql) {
7564 $obj = $db->fetch_object($resql);
7565 if ($obj) {
7566 if ($local == 1) {
7567 $ret = $obj->localtax1;
7568 } elseif ($local == 2) {
7569 $ret = $obj->localtax2;
7570 }
7571 }
7572 } else {
7573 dol_print_error($db);
7574 }
7575 }
7576
7577 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7578 return $ret;
7579}
7580
7598function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7599{
7600 global $conf, $db;
7601
7602 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7603
7604 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7605 $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;
7606
7607 $seller_country_code = $thirdparty_seller->country_code;
7608 $seller_in_cee = isInEEC($thirdparty_seller);
7609
7610 $buyer_country_code = $thirdparty_buyer->country_code;
7611 $buyer_in_cee = isInEEC($thirdparty_buyer);
7612
7613 dol_syslog("get_default_tva: seller use vat=".$seller_use_vat.", seller country=".$seller_country_code.", seller in cee=".((string) (int) $seller_in_cee).", buyer vat number=".$thirdparty_buyer->tva_intra." buyer country=".$buyer_country_code.", buyer in cee=".((string) (int) $buyer_in_cee).", idprod=".$idprod.", idprodfournprice=".$idprodfournprice.", SERVICE_ARE_ECOMMERCE_200238EC=".(getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC') ? $conf->global->SERVICE_ARE_ECOMMERCE_200238EC : ''));
7614
7615 // 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)
7616 // we use the buyer VAT.
7617 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7618 if ($seller_in_cee && $buyer_in_cee) {
7619 $isacompany = $thirdparty_buyer->isACompany();
7620 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7621 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7622 if (!isValidVATID($thirdparty_buyer)) {
7623 $isacompany = 0;
7624 }
7625 }
7626
7627 if (!$isacompany) {
7628 //print 'VATRULE 0';
7629 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7630 }
7631 }
7632 }
7633
7634 // If seller does not use VAT, default VAT is 0. End of rule.
7635 if (!$seller_use_vat) {
7636 //print 'VATRULE 1';
7637 return 0;
7638 }
7639
7640 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
7641 if (!empty($thirdparty_buyer->state_id)) {
7642 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
7643 $sql .= " FROM ".$db->prefix()."c_tva as t";
7644 $sql .= " INNER JOIN ".$db->prefix()."c_departements as d ON t.fk_department_buyer = d.rowid";
7645 $sql .= " WHERE d.rowid = ".((int) $thirdparty_buyer->state_id);
7646 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7647
7648 $res = $db->query($sql);
7649 if ($res) {
7650 if ($db->num_rows($res)) {
7651 $obj = $db->fetch_object($res);
7652 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7653 }
7654 $db->free($res);
7655 }
7656 }
7657
7658 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7659 if (($seller_country_code == $buyer_country_code)
7660 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7661 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7662 ) { // Warning ->country_code not always defined
7663 //print 'VATRULE 3';
7664 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7665
7666 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7667 // Special case for india.
7668 //print 'VATRULE 3b';
7669 $reg = array();
7670 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7671 // we must revert the C+S into I
7672 $tmpvat = str_replace("C+S", "I", $tmpvat);
7673 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7674 // we must revert the I into C+S
7675 $tmpvat = str_replace("I", "C+S", $tmpvat);
7676 }
7677 }
7678
7679 return $tmpvat;
7680 }
7681
7682 // 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.
7683 // 'VATRULE 4' - Not supported
7684
7685 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7686 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7687 if (($seller_in_cee && $buyer_in_cee)) {
7688 $isacompany = $thirdparty_buyer->isACompany();
7689 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7690 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7691 if (!isValidVATID($thirdparty_buyer)) {
7692 $isacompany = 0;
7693 }
7694 }
7695
7696 if (!$isacompany) {
7697 //print 'VATRULE 5';
7698 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7699 } else {
7700 //print 'VATRULE 6';
7701 return 0;
7702 }
7703 }
7704
7705 // 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
7706 // I don't see any use case that need this rule.
7707 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7708 $isacompany = $thirdparty_buyer->isACompany();
7709 if (!$isacompany) {
7710 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7711 //print 'VATRULE extra';
7712 }
7713 }
7714
7715 // Otherwise the VAT proposed by default=0. End of rule.
7716 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7717 //print 'VATRULE 7';
7718 return 0;
7719}
7720
7721
7732function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7733{
7734 global $db;
7735
7736 if ($idprodfournprice > 0) {
7737 if (!class_exists('ProductFournisseur')) {
7738 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7739 }
7740 $prodprice = new ProductFournisseur($db);
7741 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7742 return $prodprice->fourn_tva_npr;
7743 } elseif ($idprod > 0) {
7744 if (!class_exists('Product')) {
7745 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7746 }
7747 $prod = new Product($db);
7748 $prod->fetch($idprod);
7749 return $prod->tva_npr;
7750 }
7751
7752 return 0;
7753}
7754
7768function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7769{
7770 global $mysoc;
7771
7772 if (!is_object($thirdparty_seller)) {
7773 return -1;
7774 }
7775 if (!is_object($thirdparty_buyer)) {
7776 return -1;
7777 }
7778
7779 if ($local == 1) { // Localtax 1
7780 if ($mysoc->country_code == 'ES') {
7781 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7782 return 0;
7783 }
7784 } else {
7785 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7786 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7787 return 0;
7788 }
7789 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7790 return 0;
7791 }
7792 }
7793 } elseif ($local == 2) { //I Localtax 2
7794 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7795 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7796 return 0;
7797 }
7798 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7799 return 0;
7800 }
7801 }
7802
7803 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7804 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7805 }
7806
7807 return 0;
7808}
7809
7818function yn($yesno, $format = 1, $color = 0)
7819{
7820 global $langs;
7821
7822 $result = 'unknown';
7823 $classname = '';
7824 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7825 $result = $langs->trans('yes');
7826 if ($format == 1 || $format == 3) {
7827 $result = $langs->trans("Yes");
7828 }
7829 if ($format == 2) {
7830 $result = '<input type="checkbox" value="1" checked disabled>';
7831 }
7832 if ($format == 3) {
7833 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7834 }
7835 if ($format == 4 || !is_numeric($format)) {
7836 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
7837 }
7838
7839 $classname = 'ok';
7840 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7841 $result = $langs->trans("no");
7842 if ($format == 1 || $format == 3) {
7843 $result = $langs->trans("No");
7844 }
7845 if ($format == 2) {
7846 $result = '<input type="checkbox" value="0" disabled>';
7847 }
7848 if ($format == 3) {
7849 $result = '<input type="checkbox" value="0" disabled> '.$result;
7850 }
7851 if ($format == 4 || !is_numeric($format)) {
7852 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
7853 }
7854
7855 if ($color == 2) {
7856 $classname = 'ok';
7857 } else {
7858 $classname = 'error';
7859 }
7860 }
7861 if ($color) {
7862 return '<span class="'.$classname.'">'.$result.'</span>';
7863 }
7864 return $result;
7865}
7866
7885function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7886{
7887 if (empty($modulepart) && is_object($object)) {
7888 if (!empty($object->module)) {
7889 $modulepart = $object->module;
7890 } elseif (!empty($object->element)) {
7891 $modulepart = $object->element;
7892 }
7893 }
7894
7895 $path = '';
7896
7897 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7898 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7899 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7900 $arrayforoldpath['product'] = 2;
7901 }
7902
7903 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7904 $level = $arrayforoldpath[$modulepart];
7905 }
7906
7907 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7908 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7909 if (empty($num) && is_object($object)) {
7910 $num = $object->id;
7911 }
7912 if (empty($alpha)) {
7913 $num = preg_replace('/([^0-9])/i', '', $num);
7914 } else {
7915 $num = preg_replace('/^.*\-/i', '', $num);
7916 }
7917 $num = substr("000".$num, -$level);
7918 if ($level == 1) {
7919 $path = substr($num, 0, 1);
7920 }
7921 if ($level == 2) {
7922 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7923 }
7924 if ($level == 3) {
7925 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7926 }
7927 } else {
7928 // We will enhance here a common way of forging path for document storage.
7929 // In a future, we may distribute directories on several levels depending on setup and object.
7930 // Here, $object->id, $object->ref and $modulepart are required.
7931 //var_dump($modulepart);
7932 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7933 }
7934
7935 if (empty($withoutslash) && !empty($path)) {
7936 $path .= '/';
7937 }
7938
7939 return $path;
7940}
7941
7950function dol_mkdir($dir, $dataroot = '', $newmask = '')
7951{
7952 global $conf;
7953
7954 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7955
7956 $dir = dol_sanitizePathName($dir, '_', 0);
7957
7958 $dir_osencoded = dol_osencode($dir);
7959 if (@is_dir($dir_osencoded)) {
7960 return 0;
7961 }
7962
7963 $nberr = 0;
7964 $nbcreated = 0;
7965
7966 $ccdir = '';
7967 if (!empty($dataroot)) {
7968 // Remove data root from loop
7969 $dir = str_replace($dataroot.'/', '', $dir);
7970 $ccdir = $dataroot.'/';
7971 }
7972
7973 $cdir = explode("/", $dir);
7974 $num = count($cdir);
7975 for ($i = 0; $i < $num; $i++) {
7976 if ($i > 0) {
7977 $ccdir .= '/'.$cdir[$i];
7978 } else {
7979 $ccdir .= $cdir[$i];
7980 }
7981 $regs = array();
7982 if (preg_match("/^.:$/", $ccdir, $regs)) {
7983 continue; // If the Windows path is incomplete, continue with next directory
7984 }
7985
7986 // Attention, is_dir() can fail event if the directory exists
7987 // (i.e. according the open_basedir configuration)
7988 if ($ccdir) {
7989 $ccdir_osencoded = dol_osencode($ccdir);
7990 if (!@is_dir($ccdir_osencoded)) {
7991 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7992
7993 umask(0);
7994 $dirmaskdec = octdec((string) $newmask);
7995 if (empty($newmask)) {
7996 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7997 }
7998 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7999 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8000 // If the is_dir has returned a false information, we arrive here
8001 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
8002 $nberr++;
8003 } else {
8004 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
8005 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8006 $nbcreated++;
8007 }
8008 } else {
8009 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8010 }
8011 }
8012 }
8013 return ($nberr ? -$nberr : $nbcreated);
8014}
8015
8016
8024function dolChmod($filepath, $newmask = '')
8025{
8026 global $conf;
8027
8028 if (!empty($newmask)) {
8029 @chmod($filepath, octdec($newmask));
8030 } elseif (getDolGlobalString('MAIN_UMASK')) {
8031 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
8032 }
8033}
8034
8035
8041function picto_required()
8042{
8043 return '<span class="fieldrequired">*</span>';
8044}
8045
8046
8063function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8064{
8065 if (is_null($stringtoclean)) {
8066 return '';
8067 }
8068
8069 if ($removelinefeed == 2) {
8070 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8071 }
8072 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8073
8074 // 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)
8075 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8076
8077 $temp = str_replace('< ', '__ltspace__', $temp);
8078 $temp = str_replace('<:', '__lttwopoints__', $temp);
8079
8080 if ($strip_tags) {
8081 $temp = strip_tags($temp);
8082 } else {
8083 // 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).
8084 $pattern = "/<[^<>]+>/";
8085 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8086 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8087 // pass 2 - $temp after pass 2: 0000-021
8088 $tempbis = $temp;
8089 do {
8090 $temp = $tempbis;
8091 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8092 $tempbis = preg_replace($pattern, '', $tempbis);
8093 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8094 } while ($tempbis != $temp);
8095
8096 $temp = $tempbis;
8097
8098 // 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).
8099 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8100 }
8101
8102 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8103
8104 // Remove also carriage returns
8105 if ($removelinefeed == 1) {
8106 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8107 }
8108
8109 // And double quotes
8110 if ($removedoublespaces) {
8111 while (strpos($temp, " ")) {
8112 $temp = str_replace(" ", " ", $temp);
8113 }
8114 }
8115
8116 $temp = str_replace('__ltspace__', '< ', $temp);
8117 $temp = str_replace('__lttwopoints__', '<:', $temp);
8118
8119 return trim($temp);
8120}
8121
8140function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
8141{
8142 if (empty($allowed_tags)) {
8143 $allowed_tags = array(
8144 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8145 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8146 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8147 );
8148 }
8149 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8150 if ($allowiframe) {
8151 if (!in_array('iframe', $allowed_tags)) {
8152 $allowed_tags[] = "iframe";
8153 }
8154 }
8155 if ($allowlink) {
8156 if (!in_array('link', $allowed_tags)) {
8157 $allowed_tags[] = "link";
8158 }
8159 }
8160 if ($allowscript) {
8161 if (!in_array('script', $allowed_tags)) {
8162 $allowed_tags[] = "script";
8163 }
8164 }
8165 if ($allowstyle) {
8166 if (!in_array('style', $allowed_tags)) {
8167 $allowed_tags[] = "style";
8168 }
8169 }
8170
8171 $allowed_tags_string = implode("><", $allowed_tags);
8172 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8173
8174 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8175
8176 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8177
8178 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8179 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8180
8181 if ($allowphp) {
8182 $allowed_tags[] = "commentphp";
8183 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
8184 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
8185 }
8186
8187 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8188 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8189
8190 // Remove all HTML tags
8191 $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
8192
8193 if ($cleanalsosomestyles) { // Clean for remaining html tags
8194 $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
8195 }
8196 if ($removeclassattribute) { // Clean for remaining html tags
8197 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8198 }
8199
8200 // Remove 'javascript:' that we should not find into a text
8201 // 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)).
8202 if ($cleanalsojavascript) {
8203 $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);
8204 }
8205
8206 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8207
8208 if ($allowphp) {
8209 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
8210 }
8211
8212 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8213
8214
8215 return $temp;
8216}
8217
8218
8231function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8232{
8233 if (is_null($allowed_attributes)) {
8234 $allowed_attributes = array(
8235 "allow", "allowfullscreen", "alt", "async", "class", "content", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8236 // HTML5
8237 "header", "footer", "nav", "section", "menu", "menuitem"
8238 );
8239 }
8240
8241 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8242 $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
8243
8244 // Warning: loadHTML does not support HTML5 on old libxml versions.
8245 $dom = new DOMDocument('', 'UTF-8');
8246 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8247 $savwarning = error_reporting();
8248 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8249 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8250 error_reporting($savwarning);
8251
8252 if ($dom instanceof DOMDocument) {
8253 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8254 $el = $els->item($i);
8255 if (!$el instanceof DOMElement) {
8256 continue;
8257 }
8258 $attrs = $el->attributes;
8259 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8260 //var_dump($attrs->item($ii));
8261 if (!empty($attrs->item($ii)->name)) {
8262 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8263 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8264 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8265 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8266 // If attribute is 'style'
8267 $valuetoclean = $attrs->item($ii)->value;
8268
8269 if (isset($valuetoclean)) {
8270 do {
8271 $oldvaluetoclean = $valuetoclean;
8272 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8273 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8274 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8275 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8276 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8277 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8278 }
8279
8280 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8281 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8282 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8283 } while ($oldvaluetoclean != $valuetoclean);
8284 }
8285
8286 $attrs->item($ii)->value = $valuetoclean;
8287 }
8288 }
8289 }
8290 }
8291 }
8292
8293 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8294 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8295
8296 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8297 $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
8298 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
8299 return trim($return);
8300 } else {
8301 return $stringtoclean;
8302 }
8303}
8304
8316function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8317{
8318 $temp = $stringtoclean;
8319 foreach ($disallowed_tags as $tagtoremove) {
8320 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8321 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8322 }
8323
8324 if ($cleanalsosomestyles) {
8325 $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
8326 }
8327
8328 return $temp;
8329}
8330
8331
8341function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8342{
8343 if ($nboflines == 1) {
8344 if (dol_textishtml($text)) {
8345 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8346 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8347 } else {
8348 if (isset($text)) {
8349 $firstline = preg_replace('/[\n\r].*/', '', $text);
8350 } else {
8351 $firstline = '';
8352 }
8353 }
8354 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8355 } else {
8356 $ishtml = 0;
8357 if (dol_textishtml($text)) {
8358 $text = preg_replace('/\n/', '', $text);
8359 $ishtml = 1;
8360 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8361 } else {
8362 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8363 }
8364
8365 $text = strtr($text, $repTable);
8366 if ($charset == 'UTF-8') {
8367 $pattern = '/(<br[^>]*>)/Uu';
8368 } else {
8369 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8370 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8371 }
8372 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8373
8374 $firstline = '';
8375 $i = 0;
8376 $countline = 0;
8377 $lastaddediscontent = 1;
8378 while ($countline < $nboflines && isset($a[$i])) {
8379 if (preg_match('/<br[^>]*>/', $a[$i])) {
8380 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8381 $firstline .= ($ishtml ? "<br>\n" : "\n");
8382 // Is it a br for a new line of after a printed line ?
8383 if (!$lastaddediscontent) {
8384 $countline++;
8385 }
8386 $lastaddediscontent = 0;
8387 }
8388 } else {
8389 $firstline .= $a[$i];
8390 $lastaddediscontent = 1;
8391 $countline++;
8392 }
8393 $i++;
8394 }
8395
8396 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8397 //unset($a);
8398 $ret = $firstline.($adddots ? '...' : '');
8399 //exit;
8400 return $ret;
8401 }
8402}
8403
8404
8416function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8417{
8418 if (is_null($stringtoencode)) {
8419 return '';
8420 }
8421
8422 if (!$nl2brmode) {
8423 return nl2br($stringtoencode, $forxml);
8424 } else {
8425 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8426 return $ret;
8427 }
8428}
8429
8439function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8440{
8441 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8442 // TODO using sandbox on inline html content is not possible yet with current browsers
8443 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8444 //$s .= $stringtoencode;
8445 //$s .= '</body></html></iframe>';
8446 return $stringtoencode;
8447 } else {
8448 $out = $stringtoencode;
8449
8450 // First clean HTML content
8451 do {
8452 $oldstringtoclean = $out;
8453
8454 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8455 try {
8456 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8457 if (LIBXML_VERSION < 20900) {
8458 // Avoid load of external entities (security problem).
8459 // Required only if LIBXML_VERSION < 20900
8460 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8461 libxml_disable_entity_loader(true);
8462 }
8463
8464 $dom = new DOMDocument();
8465 // Add a trick to solve pb with text without parent tag
8466 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8467 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8468
8469 if (dol_textishtml($out)) {
8470 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.$out.'</div>';
8471 } else {
8472 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.dol_nl2br($out).'</div>';
8473 }
8474
8475 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8476 $out = trim($dom->saveHTML());
8477
8478 // Remove the trick added to solve pb with text without parent tag
8479 $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8480 $out = preg_replace('/<\/div>$/', '', $out);
8481 } catch (Exception $e) {
8482 // If error, invalid HTML string with no way to clean it
8483 //print $e->getMessage();
8484 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8485 }
8486 }
8487
8488 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript'))) {
8489 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8490 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8491 try {
8492 // Try cleaning using tidy
8493 if (extension_loaded('tidy') && class_exists("tidy")) {
8494 //print "aaa".$out."\n";
8495
8496 // See options at https://tidy.sourceforge.net/docs/quickref.html
8497 $config = array(
8498 'clean' => false,
8499 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8500 'doctype' => 'strict',
8501 'show-body-only' => true,
8502 "indent-attributes" => false,
8503 "vertical-space" => false,
8504 //'ident' => false, // Not always supported
8505 "wrap" => 0
8506 // HTML5 tags
8507 //'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',
8508 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8509 //'new-empty-tags' => 'command embed keygen source track wbr',
8510 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8511 );
8512
8513 // Tidy
8514 $tidy = new tidy();
8515 $out = $tidy->repairString($out, $config, 'utf8');
8516
8517 //print "xxx".$out;exit;
8518 }
8519 } catch (Exception $e) {
8520 // If error, invalid HTML string with no way to clean it
8521 //print $e->getMessage();
8522 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8523 }
8524 }
8525
8526 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8527 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8528
8529 // Clean some html entities that are useless so text is cleaner
8530 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8531
8532 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8533 // encoded using text entities) so we can then exclude all numeric entities.
8534 $out = preg_replace('/&#39;/i', '&apos;', $out);
8535
8536 // 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).
8537 // 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
8538 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8539 $out = preg_replace_callback(
8540 '/&#(x?[0-9][0-9a-f]+;?)/i',
8545 static function ($m) {
8546 return realCharForNumericEntities($m);
8547 },
8548 $out
8549 );
8550
8551 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8552 $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'.
8553
8554 // Keep only some html tags and remove also some 'javascript:' strings
8555 if ($check == 'restricthtmlallowlinkscript') {
8556 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
8557 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8558 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8559 } else {
8560 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8561 }
8562
8563 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8564 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8566 }
8567
8568 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8569 $out = preg_replace('/&apos;/i', "&#39;", $out);
8570
8571 // Now remove js
8572 // 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
8573 $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)>
8574 $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);
8575 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8576 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8577 $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);
8578 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8579 // More not into the previous list
8580 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8581 } while ($oldstringtoclean != $out);
8582
8583 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8584 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8585 // 'url(' to avoid inline style like background: url(http...
8586 // '<link' to avoid <link href="http...">
8587 $reg = array();
8588 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8589 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8590 $nblinks = count($reg[0]);
8591 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8592 $out = 'ErrorTooManyLinksIntoHTMLString';
8593 }
8594
8595 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8596 if ($nblinks > 0) {
8597 $out = 'ErrorHTMLLinksNotAllowed';
8598 }
8599 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8600 $nblinks = 0;
8601 // Loop on each url in src= and url(
8602 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8603
8604 $matches = array();
8605 if (preg_match_all($pattern, $out, $matches)) {
8606 // URLs are into $matches[1]
8607 $urls = $matches[1];
8608
8609 // Affiche les URLs
8610 foreach ($urls as $url) {
8611 $nblinks++;
8612 echo "Found url = ".$url . "\n";
8613 }
8614 if ($nblinks > 0) {
8615 $out = 'ErrorHTMLExternalLinksNotAllowed';
8616 }
8617 }
8618 }
8619
8620 return $out;
8621 }
8622}
8623
8644function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8645{
8646 if (is_null($stringtoencode)) {
8647 return '';
8648 }
8649
8650 $newstring = $stringtoencode;
8651 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8652 $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.
8653 if ($removelasteolbr) {
8654 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8655 }
8656 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8657 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8658 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8659 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8660 } else {
8661 if ($removelasteolbr) {
8662 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8663 }
8664 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8665 }
8666 // Other substitutions that htmlentities does not do
8667 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8668 return $newstring;
8669}
8670
8678function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8679{
8680 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8681 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8682 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8683 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8684 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8685 return $ret;
8686}
8687
8694function dol_htmlcleanlastbr($stringtodecode)
8695{
8696 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8697 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8698 return $ret;
8699}
8700
8710function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8711{
8712 $newstring = $a;
8713 if ($keepsomeentities) {
8714 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8715 }
8716 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8717 if ($keepsomeentities) {
8718 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8719 }
8720 return $newstring;
8721}
8722
8734function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8735{
8736 return htmlentities($string, $flags, $encoding, $double_encode);
8737}
8738
8750function dol_string_is_good_iso($s, $clean = 0)
8751{
8752 $len = dol_strlen($s);
8753 $out = '';
8754 $ok = 1;
8755 for ($scursor = 0; $scursor < $len; $scursor++) {
8756 $ordchar = ord($s[$scursor]);
8757 //print $scursor.'-'.$ordchar.'<br>';
8758 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8759 $ok = 0;
8760 break;
8761 } elseif ($ordchar > 126 && $ordchar < 160) {
8762 $ok = 0;
8763 break;
8764 } elseif ($clean) {
8765 $out .= $s[$scursor];
8766 }
8767 }
8768 if ($clean) {
8769 return $out;
8770 }
8771 return $ok;
8772}
8773
8782function dol_nboflines($s, $maxchar = 0)
8783{
8784 if ($s == '') {
8785 return 0;
8786 }
8787 $arraystring = explode("\n", $s);
8788 $nb = count($arraystring);
8789
8790 return $nb;
8791}
8792
8793
8803function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8804{
8805 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8806 if (dol_textishtml($text)) {
8807 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8808 }
8809
8810 $text = strtr($text, $repTable);
8811 if ($charset == 'UTF-8') {
8812 $pattern = '/(<br[^>]*>)/Uu';
8813 } else {
8814 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8815 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8816 }
8817 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8818
8819 $nblines = (int) floor((count($a) + 1) / 2);
8820 // count possible auto line breaks
8821 if ($maxlinesize) {
8822 foreach ($a as $line) {
8823 if (dol_strlen($line) > $maxlinesize) {
8824 //$line_dec = html_entity_decode(strip_tags($line));
8825 $line_dec = html_entity_decode($line);
8826 if (dol_strlen($line_dec) > $maxlinesize) {
8827 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8828 $nblines += substr_count($line_dec, '\n');
8829 }
8830 }
8831 }
8832 }
8833
8834 unset($a);
8835 return $nblines;
8836}
8837
8846function dol_textishtml($msg, $option = 0)
8847{
8848 if (is_null($msg)) {
8849 return false;
8850 }
8851
8852 if ($option == 1) {
8853 if (preg_match('/<(html|link|script)/i', $msg)) {
8854 return true;
8855 } elseif (preg_match('/<body/i', $msg)) {
8856 return true;
8857 } elseif (preg_match('/<\/textarea/i', $msg)) {
8858 return true;
8859 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8860 return true;
8861 } elseif (preg_match('/<br/i', $msg)) {
8862 return true;
8863 }
8864 return false;
8865 } else {
8866 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8867 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8868 if (preg_match('/<(html|link|script|body)/i', $msg)) {
8869 return true;
8870 } elseif (preg_match('/<\/textarea/i', $msg)) {
8871 return true;
8872 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8873 return true;
8874 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8875 return true;
8876 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8877 return true;
8878 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8879 return true;
8880 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8881 return true; // must accept <img src="http://example.com/aaa.png" />
8882 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8883 return true; // must accept <a href="http://example.com/aaa.png" />
8884 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8885 return true;
8886 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8887 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8888 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8889 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8890 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8891 }
8892
8893 return false;
8894 }
8895}
8896
8911function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8912{
8913 if (!empty($invert)) {
8914 $tmp = $text1;
8915 $text1 = $text2;
8916 $text2 = $tmp;
8917 }
8918
8919 $ret = '';
8920 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8921 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8922 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8923 return $ret;
8924}
8925
8926
8927
8941function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8942{
8943 global $db, $conf, $mysoc, $user, $extrafields;
8944
8945 $substitutionarray = array();
8946
8947 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
8948 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8949 // this will include signature content first and then replace var found into content of signature
8950 //var_dump($onlykey);
8951 $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()
8952 $usersignature = $user->signature;
8953 $substitutionarray = array_merge($substitutionarray, array(
8954 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8955 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8956 ));
8957
8958 if (is_object($user) && ($user instanceof User)) {
8959 $substitutionarray = array_merge($substitutionarray, array(
8960 '__USER_ID__' => (string) $user->id,
8961 '__USER_LOGIN__' => (string) $user->login,
8962 '__USER_EMAIL__' => (string) $user->email,
8963 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8964 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8965 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8966 '__USER_FAX__' => (string) $user->office_fax,
8967 '__USER_LASTNAME__' => (string) $user->lastname,
8968 '__USER_FIRSTNAME__' => (string) $user->firstname,
8969 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8970 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8971 '__USER_JOB__' => (string) $user->job,
8972 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8973 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8974 ));
8975 }
8976 }
8977 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8978 $substitutionarray = array_merge($substitutionarray, array(
8979 '__MYCOMPANY_NAME__' => $mysoc->name,
8980 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8981 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8982 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8983 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8984 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8985 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8986 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8987 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8988 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8989 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8990 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8991 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8992 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8993 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8994 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8995 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8996 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8997 '__MYCOMPANY_TOWN__' => $mysoc->town,
8998 '__MYCOMPANY_STATE__' => $mysoc->state,
8999 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
9000 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
9001 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
9002 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
9003 ));
9004 }
9005
9006 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
9007 if ($onlykey) {
9008 $substitutionarray['__ID__'] = '__ID__';
9009 $substitutionarray['__REF__'] = '__REF__';
9010 $substitutionarray['__NEWREF__'] = '__NEWREF__';
9011 $substitutionarray['__LABEL__'] = '__LABEL__';
9012 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
9013 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
9014 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
9015 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
9016 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
9017
9018 if (isModEnabled("societe")) { // Most objects are concerned
9019 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
9020 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
9021 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
9022 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
9023 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
9024 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
9025 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
9026 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
9027 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
9028 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
9029 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
9030 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
9031 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
9032 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
9033 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
9034 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
9035 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
9036 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
9037 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
9038 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
9039 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
9040 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
9041 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
9042 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
9043 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
9044 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
9045 }
9046 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9047 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9048 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9049 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9050 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9051 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9052 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9053 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9054 }
9055 // add substitution variables for ticket
9056 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9057 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9058 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9059 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9060 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9061 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9062 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9063 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9064 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9065 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9066 }
9067
9068 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9069 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9070 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9071 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9072 }
9073 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9074 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9075 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9076 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9077 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9078 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9079 }
9080 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9081 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9082 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9083 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9084 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9085 }
9086 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9087 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9088 }
9089 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9090 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9091 }
9092 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9093 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9094 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9095 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9096 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9097 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9098 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9099
9100 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9101 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9102 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9103 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9104 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9105
9106 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9107 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9108 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9109 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9110 }
9111 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9112 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9113 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9114 }
9115 } else {
9116 '@phan-var-force Adherent|Delivery $object';
9117 $substitutionarray['__ID__'] = $object->id;
9118 $substitutionarray['__REF__'] = $object->ref;
9119 $substitutionarray['__NEWREF__'] = $object->newref;
9120 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9121 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9122 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9123 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9124 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9125 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9126 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9127 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9128 $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', false, $outputlangs) : '');
9129 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%d") : '');
9130 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%A") : '');
9131 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%m") : '');
9132 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%b") : '');
9133 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%Y") : '');
9134 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%H") : '');
9135 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%M") : '');
9136 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%S") : '');
9137
9138 // For backward compatibility (deprecated)
9139 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9140 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9141 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->delivery_date) ? dol_print_date($object->delivery_date, 'day', false, $outputlangs) : '');
9142 $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 : '')) : '');
9143 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9144
9145 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9146 '@phan-var-force Adherent $object';
9147 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9148
9149 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9150 if (method_exists($object, 'getCivilityLabel')) {
9151 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9152 }
9153 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9154 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9155 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9156 if (method_exists($object, 'getFullName')) {
9157 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9158 }
9159 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9160 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9161 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9162 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9163 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9164 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9165 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9166 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9167 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9168 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9169 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9170 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9171 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9172 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9173 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9174 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9175
9176 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9177 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9178 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9179 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9180 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9181 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9182 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9183 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9184 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9185 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9186 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9187 }
9188
9189 if (is_object($object) && $object->element == 'societe') {
9190 '@phan-var-force Societe $object';
9191 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
9192 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
9193 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
9194 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
9195 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
9196 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
9197 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
9198 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
9199 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
9200 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
9201 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
9202 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
9203 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object) ? $object->state : '');
9204 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
9205 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
9206 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
9207 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
9208 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
9209 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
9210 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
9211 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
9212 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
9213 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
9214 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
9215 } elseif (is_object($object->thirdparty)) {
9216 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
9217 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9218 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9219 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9220 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9221 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9222 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9223 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9224 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9225 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9226 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9227 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9228 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object->thirdparty) ? $object->thirdparty->state : '');
9229 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9230 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9231 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9232 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9233 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9234 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9235 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9236 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9237 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9238 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9239 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9240 }
9241
9242 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9243 '@phan-var-force RecruitmentCandidature $object';
9244 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9245 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9246 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9247 }
9248 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9249 '@phan-var-force ConferenceOrBoothAttendee $object';
9250 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9251 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9252 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9253 }
9254
9255 if (is_object($object) && $object->element == 'project') {
9256 '@phan-var-force Project $object';
9257 $substitutionarray['__PROJECT_ID__'] = $object->id;
9258 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9259 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9260 } elseif (is_object($object)) {
9261 $project = null;
9262 if (!empty($object->project)) {
9263 $project = $object->project;
9264 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9265 $project = $object->projet;
9266 }
9267 if (!is_null($project) && is_object($project)) {
9268 $substitutionarray['__PROJECT_ID__'] = $project->id;
9269 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9270 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9271 } else {
9272 // can substitute variables for project : uses lazy load in "make_substitutions" method
9273 $project_id = 0;
9274 if (!empty($object->fk_project) && $object->fk_project > 0) {
9275 $project_id = $object->fk_project;
9276 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9277 $project_id = $object->fk_project;
9278 }
9279 if ($project_id > 0) {
9280 // path:class:method:id
9281 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9282 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9283 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9284 }
9285 }
9286 }
9287
9288 if (is_object($object) && $object->element == 'facture') {
9289 '@phan-var-force Facture $object';
9290 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9291 }
9292 if (is_object($object) && $object->element == 'shipping') {
9293 '@phan-var-force Expedition $object';
9294 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9295 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9296 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9297 }
9298 if (is_object($object) && $object->element == 'reception') {
9299 '@phan-var-force Reception $object';
9300 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9301 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9302 }
9303
9304 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9305 '@phan-var-force Contrat $object';
9306 $dateplannedstart = '';
9307 $datenextexpiration = '';
9308 foreach ($object->lines as $line) {
9309 if ($line->date_start > $dateplannedstart) {
9310 $dateplannedstart = $line->date_start;
9311 }
9312 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9313 $datenextexpiration = $line->date_end;
9314 }
9315 }
9316 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9317 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9318 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9319
9320 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9321 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9322 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9323 }
9324 // add substitution variables for ticket
9325 if (is_object($object) && $object->element == 'ticket') {
9326 '@phan-var-force Ticket $object';
9327 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9328 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9329 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9330 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9331 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9332 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9333 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9334 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9335 $userstat = new User($db);
9336 if ($object->fk_user_assign > 0) {
9337 $userstat->fetch($object->fk_user_assign);
9338 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9339 }
9340
9341 if ($object->fk_user_create > 0) {
9342 $userstat->fetch($object->fk_user_create);
9343 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9344 }
9345 }
9346
9347 // Create dynamic tags for __EXTRAFIELD_FIELD__
9348 if ($object->table_element && $object->id > 0) {
9349 if (!is_object($extrafields)) {
9350 $extrafields = new ExtraFields($db);
9351 }
9352 $extrafields->fetch_name_optionals_label($object->table_element, true);
9353
9354 if ($object->fetch_optionals() > 0) {
9355 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9356 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9357 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9358 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9359 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9360 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9361 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9362 $datetime = $object->array_options['options_'.$key];
9363 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9364 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9365 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9366 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9367 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9368 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9369 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9370 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9371 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9372 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9373 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9374 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9375 }
9376 }
9377 }
9378 }
9379 }
9380
9381 // Complete substitution array with the url to make online payment
9382 if (empty($substitutionarray['__REF__'])) {
9383 $paymenturl = '';
9384 } else {
9385 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9386 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9387 $outputlangs->loadLangs(array('paypal', 'other'));
9388
9389 $amounttouse = 0;
9390 $typeforonlinepayment = 'free';
9391 if (is_object($object) && $object->element == 'commande') {
9392 $typeforonlinepayment = 'order';
9393 }
9394 if (is_object($object) && $object->element == 'facture') {
9395 $typeforonlinepayment = 'invoice';
9396 }
9397 if (is_object($object) && $object->element == 'member') {
9398 $typeforonlinepayment = 'member';
9399 if (!empty($object->last_subscription_amount)) {
9400 $amounttouse = $object->last_subscription_amount;
9401 }
9402 }
9403 if (is_object($object) && $object->element == 'contrat') {
9404 $typeforonlinepayment = 'contract';
9405 }
9406 if (is_object($object) && $object->element == 'fichinter') {
9407 $typeforonlinepayment = 'ficheinter';
9408 }
9409
9410 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9411 $paymenturl = $url;
9412 }
9413
9414 if ($object->id > 0) {
9415 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9416 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9417
9418 // Show structured communication
9419 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9420 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9421 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9422 }
9423
9424 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9425 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9426 } else {
9427 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9428 }
9429 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9430 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9431 } else {
9432 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9433 }
9434 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9435 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9436 } else {
9437 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9438 }
9439 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9440 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9441 } else {
9442 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9443 }
9444 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9445 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9446 } else {
9447 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9448 }
9449 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9450 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9451 } else {
9452 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9453 }
9454
9455 if (is_object($object) && $object->element == 'propal') {
9456 '@phan-var-force Propal $object';
9457 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9458 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9459 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9460 }
9461 if (is_object($object) && $object->element == 'commande') {
9462 '@phan-var-force Commande $object';
9463 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9464 }
9465 if (is_object($object) && $object->element == 'facture') {
9466 '@phan-var-force Facture $object';
9467 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9468 }
9469 if (is_object($object) && $object->element == 'contrat') {
9470 '@phan-var-force Contrat $object';
9471 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9472 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9473 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9474 }
9475 if (is_object($object) && $object->element == 'fichinter') {
9476 '@phan-var-force Fichinter $object';
9477 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9478 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9479 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9480 }
9481 if (is_object($object) && $object->element == 'supplier_proposal') {
9482 '@phan-var-force SupplierProposal $object';
9483 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9484 }
9485 if (is_object($object) && $object->element == 'invoice_supplier') {
9486 '@phan-var-force FactureFournisseur $object';
9487 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9488 }
9489 if (is_object($object) && $object->element == 'shipping') {
9490 '@phan-var-force Expedition $object';
9491 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9492 }
9493 }
9494
9495 if (is_object($object) && $object->element == 'action') {
9496 '@phan-var-force ActionComm $object';
9497 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9498 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9499 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9500 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9501 }
9502 }
9503 }
9504 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9505 '@phan-var-force Facture|FactureRec $object';
9506 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9507
9508 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9509 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9510 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9511 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9512
9513 $already_payed_all = 0;
9514 if (is_object($object) && ($object instanceof Facture)) {
9515 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9516 }
9517
9518 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9519 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9520 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9521
9522 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9523 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9524 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9525
9526 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9527
9528 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9529 $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)) : '';
9530 $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)) : '';
9531
9532 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9533 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9534 }
9535 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9536 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9537 }
9538
9539 // Amount keys formatted in a currency
9540 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9541 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9542 $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) : '';
9543 $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)) : '';
9544 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9545 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9546 }
9547 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9548 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9549 }
9550 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9551 if ($onlykey != 2) {
9552 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9553 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9554 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9555 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9556 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9557 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9558 }
9559 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9560 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9561 }
9562 }
9563
9564 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9565 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9566 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9567 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9568 // TODO Add other keys for foreign multicurrency
9569
9570 // For backward compatibility
9571 if ($onlykey != 2) {
9572 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9573 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9574 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9575 }
9576 }
9577
9578
9579 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9580 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9581
9582 $now = dol_now();
9583
9584 $tmp = dol_getdate($now, true);
9585 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9586 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9587 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9588 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9589
9590 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9591
9592 $substitutionarray = array_merge($substitutionarray, array(
9593 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9594 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9595 '__DAY__' => (string) $tmp['mday'],
9596 '__DAY_TEXT__' => $daytext, // Monday
9597 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9598 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9599 '__MONTH__' => (string) $tmp['mon'],
9600 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9601 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9602 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9603 '__YEAR__' => (string) $tmp['year'],
9604 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9605 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9606 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9607 '__NEXT_DAY__' => (string) $tmp4['day'],
9608 '__NEXT_MONTH__' => (string) $tmp5['month'],
9609 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9610 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9611 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9612 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9613 ));
9614 }
9615
9616 if (isModEnabled('multicompany')) {
9617 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9618 }
9619 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9620 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9621 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9622 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9623 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9624 }
9625
9626 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9627
9628 return $substitutionarray;
9629}
9630
9647function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9648{
9649 global $conf, $db, $langs;
9650
9651 if (!is_array($substitutionarray)) {
9652 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9653 }
9654
9655 if (empty($outputlangs)) {
9656 $outputlangs = $langs;
9657 }
9658
9659 // Is initial text HTML or simple text ?
9660 $msgishtml = 0;
9661 if (dol_textishtml($text, 1)) {
9662 $msgishtml = 1;
9663 }
9664
9665 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9666 if (is_object($outputlangs)) {
9667 $reg = array();
9668 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9669 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9670 $tmp = explode('|', $reg[1]);
9671 if (!empty($tmp[1])) {
9672 $outputlangs->load($tmp[1]);
9673 }
9674
9675 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9676
9677 if (empty($converttextinhtmlifnecessary)) {
9678 // convert $newval into HTML is necessary
9679 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9680 } else {
9681 if (! $msgishtml) {
9682 $valueishtml = dol_textishtml($value, 1);
9683 //var_dump("valueishtml=".$valueishtml);
9684
9685 if ($valueishtml) {
9686 $text = dol_htmlentitiesbr($text);
9687 $msgishtml = 1;
9688 }
9689 } else {
9690 $value = dol_nl2br("$value");
9691 }
9692
9693 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9694 }
9695 }
9696 }
9697
9698 // Make substitution for constant keys.
9699 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9700 $reg = array();
9701 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9702 $keyfound = $reg[1];
9703 if (isASecretKey($keyfound)) {
9704 $value = '*****forbidden*****';
9705 } else {
9706 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9707 }
9708
9709 if (empty($converttextinhtmlifnecessary)) {
9710 // convert $newval into HTML is necessary
9711 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9712 } else {
9713 if (! $msgishtml) {
9714 $valueishtml = dol_textishtml($value, 1);
9715
9716 if ($valueishtml) {
9717 $text = dol_htmlentitiesbr($text);
9718 $msgishtml = 1;
9719 }
9720 } else {
9721 $value = dol_nl2br("$value");
9722 }
9723
9724 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9725 }
9726 }
9727
9728 // Make substitution for array $substitutionarray
9729 foreach ($substitutionarray as $key => $value) {
9730 if (!isset($value)) {
9731 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9732 }
9733
9734 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9735 $value = ''; // Protection
9736 }
9737
9738 if (empty($converttextinhtmlifnecessary)) {
9739 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9740 } else {
9741 if (! $msgishtml) {
9742 $valueishtml = dol_textishtml($value, 1);
9743
9744 if ($valueishtml) {
9745 $text = dol_htmlentitiesbr($text);
9746 $msgishtml = 1;
9747 }
9748 } else {
9749 $value = dol_nl2br("$value");
9750 }
9751 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9752 }
9753 }
9754
9755 // TODO Implement the lazyload substitution
9756 /*
9757 add a loop to scan $substitutionarray:
9758 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.
9759 If no, we don't need to make replacement, so we do nothing.
9760 If yes, we can make the substitution:
9761
9762 include_once $path;
9763 $tmpobj = new $class($db);
9764 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9765 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9766 */
9767 $memory_object_list = array();
9768 foreach ($substitutionarray as $key => $value) {
9769 $lazy_load_arr = array();
9770 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9771 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9772 $key_to_substitute = $lazy_load_arr[1];
9773 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9774 $param_arr = explode(':', $value);
9775 // path:class:method:id
9776 if (count($param_arr) == 4) {
9777 $path = $param_arr[0];
9778 $class = $param_arr[1];
9779 $method = $param_arr[2];
9780 $id = (int) $param_arr[3];
9781
9782 // load class file and init object list in memory
9783 if (!isset($memory_object_list[$class])) {
9784 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9785 require_once DOL_DOCUMENT_ROOT . $path;
9786 if (class_exists($class)) {
9787 $memory_object_list[$class] = array(
9788 'list' => array(),
9789 );
9790 }
9791 }
9792 }
9793
9794 // fetch object and set substitution
9795 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9796 if (method_exists($class, $method)) {
9797 if (!isset($memory_object_list[$class]['list'][$id])) {
9798 $tmpobj = new $class($db);
9799 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9800 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9801 $memory_object_list[$class]['list'][$id] = $tmpobj;
9802 } else {
9803 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9804 $tmpobj = $memory_object_list[$class]['list'][$id];
9805 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9806 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9807 }
9808
9809 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9810 }
9811 }
9812 }
9813 }
9814 }
9815 }
9816 }
9817
9818 return $text;
9819}
9820
9833function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9834{
9835 global $conf, $user;
9836
9837 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9838
9839 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9840
9841 // Check if there is external substitution to do, requested by plugins
9842 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9843
9844 foreach ($dirsubstitutions as $reldir) {
9845 $dir = dol_buildpath($reldir, 0);
9846
9847 // Check if directory exists
9848 if (!dol_is_dir($dir)) {
9849 continue;
9850 }
9851
9852 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9853 foreach ($substitfiles as $substitfile) {
9854 $reg = array();
9855 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9856 $module = $reg[1];
9857
9858 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9859 // Include the user's functions file
9860 require_once $dir.$substitfile['name'];
9861 // Call the user's function, and only if it is defined
9862 $function_name = $module."_".$callfunc;
9863 if (function_exists($function_name)) {
9864 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9865 }
9866 }
9867 }
9868 }
9869 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9870 // to list all tags in odt template
9871 $tags = '';
9872 foreach ($substitutionarray as $key => $value) {
9873 $tags .= '{'.$key.'} => '.$value."\n";
9874 }
9875 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9876 }
9877}
9878
9888function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9889{
9890 print get_date_range($date_start, $date_end, $format, $outputlangs);
9891}
9892
9903function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9904{
9905 global $langs;
9906
9907 $out = '';
9908
9909 if (!is_object($outputlangs)) {
9910 $outputlangs = $langs;
9911 }
9912
9913 if ($date_start && $date_end) {
9914 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9915 }
9916 if ($date_start && !$date_end) {
9917 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9918 }
9919 if (!$date_start && $date_end) {
9920 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9921 }
9922
9923 return $out;
9924}
9925
9934function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9935{
9936 global $conf;
9937
9938 $ret = '';
9939 // If order not defined, we use the setup
9940 if ($nameorder < 0) {
9941 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9942 }
9943 if ($nameorder == 1) {
9944 $ret .= $firstname;
9945 if ($firstname && $lastname) {
9946 $ret .= ' ';
9947 }
9948 $ret .= $lastname;
9949 } elseif ($nameorder == 2 || $nameorder == 3) {
9950 $ret .= $firstname;
9951 if (empty($ret) && $nameorder == 3) {
9952 $ret .= $lastname;
9953 }
9954 } else { // 0, 4 or 5
9955 $ret .= $lastname;
9956 if (empty($ret) && $nameorder == 5) {
9957 $ret .= $firstname;
9958 }
9959 if ($nameorder == 0) {
9960 if ($firstname && $lastname) {
9961 $ret .= ' ';
9962 }
9963 $ret .= $firstname;
9964 }
9965 }
9966 return $ret;
9967}
9968
9969
9982function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
9983{
9984 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9985 if (!is_array($mesgs)) {
9986 $mesgs = trim((string) $mesgs);
9987 // If mesgs is a not an empty string
9988 if ($mesgs) {
9989 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9990 return;
9991 }
9992 if ($attop) {
9993 array_unshift($_SESSION['dol_events'][$style], $mesgs);
9994 } else {
9995 $_SESSION['dol_events'][$style][] = $mesgs;
9996 }
9997 }
9998 } else {
9999 // If mesgs is an array
10000 foreach ($mesgs as $mesg) {
10001 $mesg = trim((string) $mesg);
10002 if ($mesg) {
10003 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
10004 return;
10005 }
10006 if ($attop) {
10007 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10008 } else {
10009 $_SESSION['dol_events'][$style][] = $mesg;
10010 }
10011 }
10012 }
10013 }
10014}
10015
10029function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
10030{
10031 if (empty($mesg) && empty($mesgs)) {
10032 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
10033 } else {
10034 if ($messagekey) {
10035 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
10036 // TODO
10037 $mesg .= '';
10038 }
10039 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
10040 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
10041 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
10042 }
10043 if (empty($mesgs)) {
10044 setEventMessage($mesg, $style, $noduplicate, $attop);
10045 } else {
10046 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10047 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10048 }
10049 setEventMessage($mesgs, $style, $noduplicate, $attop);
10050 }
10051 }
10052 }
10053}
10054
10064function dol_htmloutput_events($disabledoutputofmessages = 0)
10065{
10066 // Show mesgs
10067 if (isset($_SESSION['dol_events']['mesgs'])) {
10068 if (empty($disabledoutputofmessages)) {
10069 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10070 }
10071 unset($_SESSION['dol_events']['mesgs']);
10072 }
10073 // Show errors
10074 if (isset($_SESSION['dol_events']['errors'])) {
10075 if (empty($disabledoutputofmessages)) {
10076 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10077 }
10078 unset($_SESSION['dol_events']['errors']);
10079 }
10080
10081 // Show warnings
10082 if (isset($_SESSION['dol_events']['warnings'])) {
10083 if (empty($disabledoutputofmessages)) {
10084 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10085 }
10086 unset($_SESSION['dol_events']['warnings']);
10087 }
10088}
10089
10104function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10105{
10106 global $conf, $langs;
10107
10108 $ret = 0;
10109 $return = '';
10110 $out = '';
10111 $divstart = $divend = '';
10112
10113 // If inline message with no format, we add it.
10114 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10115 $divstart = '<div class="'.$style.' clearboth">';
10116 $divend = '</div>';
10117 }
10118
10119 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10120 $langs->load("errors");
10121 $out .= $divstart;
10122 if (is_array($mesgarray) && count($mesgarray)) {
10123 foreach ($mesgarray as $message) {
10124 $ret++;
10125 $out .= $langs->trans($message);
10126 if ($ret < count($mesgarray)) {
10127 $out .= "<br>\n";
10128 }
10129 }
10130 }
10131 if ($mesgstring) {
10132 $ret++;
10133 $out .= $langs->trans($mesgstring);
10134 }
10135 $out .= $divend;
10136 }
10137
10138 if ($out) {
10139 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10140 $return = '<script nonce="'.getNonce().'">
10141 $(document).ready(function() {
10142 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
10143 if (block) {
10144 $.dolEventValid("","'.dol_escape_js($out).'");
10145 } else {
10146 /* jnotify(message, preset of message type, keepmessage) */
10147 $.jnotify("'.dol_escape_js($out).'",
10148 "'.($style == "ok" ? 3000 : $style).'",
10149 '.($style == "ok" ? "false" : "true").',
10150 { remove: function (){} } );
10151 }
10152 });
10153 </script>';
10154 } else {
10155 $return = $out;
10156 }
10157 }
10158
10159 return $return;
10160}
10161
10173function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10174{
10175 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10176}
10177
10191function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10192{
10193 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10194 return;
10195 }
10196
10197 $iserror = 0;
10198 $iswarning = 0;
10199 if (is_array($mesgarray)) {
10200 foreach ($mesgarray as $val) {
10201 if ($val && preg_match('/class="error"/i', $val)) {
10202 $iserror++;
10203 break;
10204 }
10205 if ($val && preg_match('/class="warning"/i', $val)) {
10206 $iswarning++;
10207 break;
10208 }
10209 }
10210 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10211 $iserror++;
10212 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10213 $iswarning++;
10214 }
10215 if ($style == 'error') {
10216 $iserror++;
10217 }
10218 if ($style == 'warning') {
10219 $iswarning++;
10220 }
10221
10222 if ($iserror || $iswarning) {
10223 // Remove div from texts
10224 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10225 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10226 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10227 // Remove div from texts array
10228 if (is_array($mesgarray)) {
10229 $newmesgarray = array();
10230 foreach ($mesgarray as $val) {
10231 if (is_string($val)) {
10232 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10233 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10234 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10235 $newmesgarray[] = $tmpmesgstring;
10236 } else {
10237 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10238 }
10239 }
10240 $mesgarray = $newmesgarray;
10241 }
10242 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10243 } else {
10244 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10245 }
10246}
10247
10259function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10260{
10261 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10262}
10263
10284function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10285{
10286 // Clean parameters
10287 $order = strtolower($order);
10288
10289 if (is_array($array)) {
10290 $sizearray = count($array);
10291 if ($sizearray > 0) {
10292 $temp = array();
10293 foreach (array_keys($array) as $key) {
10294 if (is_object($array[$key])) {
10295 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10296 } else {
10297 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10298 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10299 }
10300 if ($natsort == -1) {
10301 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10302 }
10303 }
10304
10305 if (empty($natsort) || $natsort == -1) {
10306 if ($order == 'asc') {
10307 asort($temp);
10308 } else {
10309 arsort($temp);
10310 }
10311 } else {
10312 if ($case_sensitive) {
10313 natsort($temp);
10314 } else {
10315 natcasesort($temp); // natecasesort is not sensible to case
10316 }
10317 if ($order != 'asc') {
10318 $temp = array_reverse($temp, true);
10319 }
10320 }
10321
10322 $sorted = array();
10323
10324 foreach (array_keys($temp) as $key) {
10325 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10326 }
10327
10328 return $sorted;
10329 }
10330 }
10331 return $array;
10332}
10333
10334
10342function utf8_check($str)
10343{
10344 $str = (string) $str; // Sometimes string is an int.
10345
10346 // We must use here a binary strlen function (so not dol_strlen)
10347 $strLength = strlen($str);
10348 for ($i = 0; $i < $strLength; $i++) {
10349 if (ord($str[$i]) < 0x80) {
10350 continue; // 0bbbbbbb
10351 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10352 $n = 1; // 110bbbbb
10353 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10354 $n = 2; // 1110bbbb
10355 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10356 $n = 3; // 11110bbb
10357 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10358 $n = 4; // 111110bb
10359 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10360 $n = 5; // 1111110b
10361 } else {
10362 return false; // Does not match any model
10363 }
10364 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10365 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10366 return false;
10367 }
10368 }
10369 }
10370 return true;
10371}
10372
10380function utf8_valid($str)
10381{
10382 /* 2 other methods to test if string is utf8
10383 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10384 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10385 */
10386 return preg_match('//u', $str) ? true : false;
10387}
10388
10389
10396function ascii_check($str)
10397{
10398 if (function_exists('mb_check_encoding')) {
10399 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10400 if (!mb_check_encoding($str, 'ASCII')) {
10401 return false;
10402 }
10403 } else {
10404 if (preg_match('/[^\x00-\x7f]/', $str)) {
10405 return false; // Contains a byte > 7f
10406 }
10407 }
10408
10409 return true;
10410}
10411
10412
10420function dol_osencode($str)
10421{
10422 $tmp = ini_get("unicode.filesystem_encoding");
10423 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10424 $tmp = 'iso-8859-1'; // By default for windows
10425 }
10426 if (empty($tmp)) {
10427 $tmp = 'utf-8'; // By default for other
10428 }
10429 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10430 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10431 }
10432
10433 if ($tmp == 'iso-8859-1') {
10434 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10435 }
10436 return $str;
10437}
10438
10439
10454function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
10455{
10456 global $conf;
10457
10458 // If key empty
10459 if ($key == '') {
10460 return 0;
10461 }
10462
10463 // Check in cache
10464 if (isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10465 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10466 }
10467
10468 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10469
10470 $sql = "SELECT ".$fieldid." as valuetoget";
10471 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10472 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10473 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10474 } else {
10475 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10476 }
10477 if (!empty($entityfilter)) {
10478 $sql .= " AND entity IN (".getEntity($tablename).")";
10479 }
10480 if ($filters) {
10481 $sql .= $filters;
10482 }
10483
10484 $resql = $db->query($sql);
10485 if ($resql) {
10486 $obj = $db->fetch_object($resql);
10487 if ($obj) {
10488 $conf->cache['codeid'][$tablename][$key][$fieldid] = $obj->valuetoget;
10489 } else {
10490 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10491 }
10492 $db->free($resql);
10493
10494 return $conf->cache['codeid'][$tablename][$key][$fieldid];
10495 } else {
10496 return -1;
10497 }
10498}
10499
10509function isStringVarMatching($var, $regextext, $matchrule = 1)
10510{
10511 if ($matchrule == 1) {
10512 if ($var == 'mainmenu') {
10513 global $mainmenu;
10514 return (preg_match('/^'.$regextext.'/', $mainmenu));
10515 } elseif ($var == 'leftmenu') {
10516 global $leftmenu;
10517 return (preg_match('/^'.$regextext.'/', $leftmenu));
10518 } else {
10519 return 'This variable is not accessible with dol_eval';
10520 }
10521 } else {
10522 return 'This value for matchrule is not implemented';
10523 }
10524}
10525
10526
10536function verifCond($strToEvaluate, $onlysimplestring = '1')
10537{
10538 //print $strToEvaluate."<br>\n";
10539 $rights = true;
10540 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10541 //var_dump($strToEvaluate);
10542 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10543 $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
10544 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10545 //var_dump($rights);
10546 }
10547 return $rights;
10548}
10549
10564function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10565{
10566 // Only this global variables can be read by eval function and returned to caller
10567 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10568 global $db, $langs, $user, $website, $websitepage;
10569 global $action, $mainmenu, $leftmenu;
10570 global $mysoc;
10571 global $objectoffield; // To allow the use of $objectoffield in computed fields
10572
10573 // Old variables used
10574 global $object;
10575 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10576
10577 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10578 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10579 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10580 }
10581
10582 try {
10583 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10584 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10585 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10586 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10587 // We must accept with 2: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) <= 99) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found"
10588
10589 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
10590 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10591 if ($onlysimplestring == '2') {
10592 $specialcharsallowed .= '<[]';
10593 }
10594 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10595 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10596 }
10597 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10598 if ($returnvalue) {
10599 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s;
10600 } else {
10601 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING);
10602 return '';
10603 }
10604 }
10605
10606 // Check if there is a < or <= without spaces before/after
10607 if (preg_match('/<=?[^\s]/', $s)) {
10608 if ($returnvalue) {
10609 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s;
10610 } else {
10611 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING);
10612 return '';
10613 }
10614 }
10615
10616 // Check if there is dynamic call (first we use black list patterns)
10617 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10618 if ($returnvalue) {
10619 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;
10620 } else {
10621 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);
10622 return '';
10623 }
10624 }
10625
10626 // Now we check if we try dynamic call
10627 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
10628 $savescheck = '';
10629 $scheck = $s;
10630 while ($scheck && $savescheck != $scheck) {
10631 $savescheck = $scheck;
10632 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10633 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
10634 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
10635 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '&& (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
10636 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '|| (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
10637 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10638 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10639 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
10640 $scheck = preg_replace('/\s!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
10641 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10642 }
10643 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10644
10645 // Now test if it remains 1 one parenthesis.
10646 if (strpos($scheck, '(') !== false) {
10647 if ($returnvalue) {
10648 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10649 } else {
10650 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);
10651 return '';
10652 }
10653 }
10654
10655 // TODO
10656 // We can exclude $ char that are not:
10657 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10658 }
10659 if (is_array($s) || $s === 'Array') {
10660 if ($returnvalue) {
10661 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10662 } else {
10663 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10664 return '';
10665 }
10666 }
10667 if (strpos($s, '::') !== false) {
10668 if ($returnvalue) {
10669 return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
10670 } else {
10671 dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s, LOG_WARNING);
10672 return '';
10673 }
10674 }
10675 if (strpos($s, '`') !== false) {
10676 if ($returnvalue) {
10677 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10678 } else {
10679 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10680 return '';
10681 }
10682 }
10683 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10684 if ($returnvalue) {
10685 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10686 } else {
10687 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10688 return '';
10689 }
10690 }
10691
10692 // We block use of php exec or php file functions
10693 $forbiddenphpstrings = array('$$', '$_', '}[');
10694 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10695
10696 // We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all)
10697 $forbiddenphpfunctions = array();
10698 // @phpcs:ignore
10699 $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
10700 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10701 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10702 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
10703 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10704 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10705 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10706 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
10707 $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
10708 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10709 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
10710
10711 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10712
10713 $forbiddenphpregex = 'global\s*\$';
10714 $forbiddenphpregex .= '|';
10715 $forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
10716
10717 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10718
10719 do {
10720 $oldstringtoclean = $s;
10721 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10722 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10723 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10724 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10725 } while ($oldstringtoclean != $s);
10726
10727
10728 if (strpos($s, '__forbiddenstring__') !== false) {
10729 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10730 if ($returnvalue) {
10731 return 'Bad string syntax to evaluate: '.$s;
10732 } else {
10733 dol_syslog('Bad string syntax to evaluate: '.$s);
10734 return '';
10735 }
10736 }
10737
10738 //print $s."<br>\n";
10739 if ($returnvalue) {
10740 ob_start(); // An evaluation has no reason to output data
10741 $isObBufferActive = true;
10742 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
10743 $tmpo = ob_get_clean();
10744 $isObBufferActive = false;
10745 if ($tmpo) {
10746 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
10747 }
10748 return $tmps;
10749 } else {
10750 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10751 if ($hideerrors) {
10752 @eval($s);
10753 } else {
10754 eval($s);
10755 }
10756 return '';
10757 }
10758 } catch (Error $e) {
10759 if ($isObBufferActive) {
10760 // Clean up buffer which was left behind due to exception.
10761 $tmpo = ob_get_clean();
10762 $isObBufferActive = false;
10763 }
10764 $error = 'dol_eval try/catch error : ';
10765 $error .= $e->getMessage();
10766 dol_syslog($error, LOG_WARNING);
10767 if ($returnvalue) {
10768 return 'Exception during evaluation: '.$s;
10769 } else {
10770 return '';
10771 }
10772 }
10773}
10774
10782function dol_validElement($element)
10783{
10784 return (trim($element) != '');
10785}
10786
10795function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10796{
10797 if (empty($codelang)) {
10798 return '';
10799 }
10800
10801 if ($codelang == 'auto') {
10802 return '<span class="fa fa-language"></span>';
10803 }
10804
10805 $langtocountryflag = array(
10806 'ar_AR' => '',
10807 'ca_ES' => 'catalonia',
10808 'da_DA' => 'dk',
10809 'fr_CA' => 'mq',
10810 'sv_SV' => 'se',
10811 'sw_SW' => 'unknown',
10812 'AQ' => 'unknown',
10813 'CW' => 'unknown',
10814 'IM' => 'unknown',
10815 'JE' => 'unknown',
10816 'MF' => 'unknown',
10817 'BL' => 'unknown',
10818 'SX' => 'unknown'
10819 );
10820
10821 if (isset($langtocountryflag[$codelang])) {
10822 $flagImage = $langtocountryflag[$codelang];
10823 } else {
10824 $tmparray = explode('_', $codelang);
10825 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10826 }
10827
10828 $morecss = '';
10829 $reg = array();
10830 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10831 $morecss = $reg[1];
10832 $moreatt = "";
10833 }
10834
10835 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10836 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10837}
10838
10846function getLanguageCodeFromCountryCode($countrycode)
10847{
10848 global $mysoc;
10849
10850 if (empty($countrycode)) {
10851 return null;
10852 }
10853
10854 if (strtoupper($countrycode) == 'MQ') {
10855 return 'fr_CA';
10856 }
10857 if (strtoupper($countrycode) == 'SE') {
10858 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10859 }
10860 if (strtoupper($countrycode) == 'CH') {
10861 if ($mysoc->country_code == 'FR') {
10862 return 'fr_CH';
10863 }
10864 if ($mysoc->country_code == 'DE') {
10865 return 'de_CH';
10866 }
10867 if ($mysoc->country_code == 'IT') {
10868 return 'it_CH';
10869 }
10870 }
10871
10872 // Locale list taken from:
10873 // http://stackoverflow.com/questions/3191664/
10874 // list-of-all-locales-and-their-short-codes
10875 $locales = array(
10876 'af-ZA',
10877 'am-ET',
10878 'ar-AE',
10879 'ar-BH',
10880 'ar-DZ',
10881 'ar-EG',
10882 'ar-IQ',
10883 'ar-JO',
10884 'ar-KW',
10885 'ar-LB',
10886 'ar-LY',
10887 'ar-MA',
10888 'ar-OM',
10889 'ar-QA',
10890 'ar-SA',
10891 'ar-SY',
10892 'ar-TN',
10893 'ar-YE',
10894 //'as-IN', // Moved after en-IN
10895 'ba-RU',
10896 'be-BY',
10897 'bg-BG',
10898 'bn-BD',
10899 //'bn-IN', // Moved after en-IN
10900 'bo-CN',
10901 'br-FR',
10902 'ca-ES',
10903 'co-FR',
10904 'cs-CZ',
10905 'cy-GB',
10906 'da-DK',
10907 'de-AT',
10908 'de-CH',
10909 'de-DE',
10910 'de-LI',
10911 'de-LU',
10912 'dv-MV',
10913 'el-GR',
10914 'en-AU',
10915 'en-BZ',
10916 'en-CA',
10917 'en-GB',
10918 'en-IE',
10919 'en-IN',
10920 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10921 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10922 'en-JM',
10923 'en-MY',
10924 'en-NZ',
10925 'en-PH',
10926 'en-SG',
10927 'en-TT',
10928 'en-US',
10929 'en-ZA',
10930 'en-ZW',
10931 'es-AR',
10932 'es-BO',
10933 'es-CL',
10934 'es-CO',
10935 'es-CR',
10936 'es-DO',
10937 'es-EC',
10938 'es-ES',
10939 'es-GT',
10940 'es-HN',
10941 'es-MX',
10942 'es-NI',
10943 'es-PA',
10944 'es-PE',
10945 'es-PR',
10946 'es-PY',
10947 'es-SV',
10948 'es-US',
10949 'es-UY',
10950 'es-VE',
10951 'et-EE',
10952 'eu-ES',
10953 'fa-IR',
10954 'fi-FI',
10955 'fo-FO',
10956 'fr-BE',
10957 'fr-CA',
10958 'fr-CH',
10959 'fr-FR',
10960 'fr-LU',
10961 'fr-MC',
10962 'fy-NL',
10963 'ga-IE',
10964 'gd-GB',
10965 'gl-ES',
10966 'gu-IN',
10967 'he-IL',
10968 'hi-IN',
10969 'hr-BA',
10970 'hr-HR',
10971 'hu-HU',
10972 'hy-AM',
10973 'id-ID',
10974 'ig-NG',
10975 'ii-CN',
10976 'is-IS',
10977 'it-CH',
10978 'it-IT',
10979 'ja-JP',
10980 'ka-GE',
10981 'kk-KZ',
10982 'kl-GL',
10983 'km-KH',
10984 'kn-IN',
10985 'ko-KR',
10986 'ky-KG',
10987 'lb-LU',
10988 'lo-LA',
10989 'lt-LT',
10990 'lv-LV',
10991 'mi-NZ',
10992 'mk-MK',
10993 'ml-IN',
10994 'mn-MN',
10995 'mr-IN',
10996 'ms-BN',
10997 'ms-MY',
10998 'mt-MT',
10999 'nb-NO',
11000 'ne-NP',
11001 'nl-BE',
11002 'nl-NL',
11003 'nn-NO',
11004 'oc-FR',
11005 'or-IN',
11006 'pa-IN',
11007 'pl-PL',
11008 'ps-AF',
11009 'pt-BR',
11010 'pt-PT',
11011 'rm-CH',
11012 'ro-MD',
11013 'ro-RO',
11014 'ru-RU',
11015 'rw-RW',
11016 'sa-IN',
11017 'se-FI',
11018 'se-NO',
11019 'se-SE',
11020 'si-LK',
11021 'sk-SK',
11022 'sl-SI',
11023 'sq-AL',
11024 'sv-FI',
11025 'sv-SE',
11026 'sw-KE',
11027 'ta-IN',
11028 'te-IN',
11029 'th-TH',
11030 'tk-TM',
11031 'tn-ZA',
11032 'tr-TR',
11033 'tt-RU',
11034 'ug-CN',
11035 'uk-UA',
11036 'ur-PK',
11037 'vi-VN',
11038 'wo-SN',
11039 'xh-ZA',
11040 'yo-NG',
11041 'zh-CN',
11042 'zh-HK',
11043 'zh-MO',
11044 'zh-SG',
11045 'zh-TW',
11046 'zu-ZA',
11047 );
11048
11049 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
11050 if (in_array($buildprimarykeytotest, $locales)) {
11051 return strtolower($countrycode).'_'.strtoupper($countrycode);
11052 }
11053
11054 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
11055 foreach ($locales as $locale) {
11056 $locale_language = locale_get_primary_language($locale);
11057 $locale_region = locale_get_region($locale);
11058 if (strtoupper($countrycode) == $locale_region) {
11059 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
11060 return strtolower($locale_language).'_'.strtoupper($locale_region);
11061 }
11062 }
11063 } else {
11064 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
11065 }
11066
11067 return null;
11068}
11069
11100function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11101{
11102 global $hookmanager, $db;
11103
11104 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11105 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11106 $values = explode(':', $value);
11107
11108 $reg = array();
11109 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11110 $newtab = array();
11111 $postab = $h;
11112 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11113 $str = $values[1];
11114 $posstart = strpos($str, '(');
11115 if ($posstart > 0) {
11116 $posend = strpos($str, ')');
11117 if ($posstart > 0) {
11118 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11119 if (is_numeric($res1)) {
11120 $postab = (int) $res1;
11121 $values[1] = '+' . substr($str, $posend + 1);
11122 }
11123 }
11124 }
11125 if (count($values) == 6) {
11126 // new declaration with permissions:
11127 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11128 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11129 if ($values[0] != $type) {
11130 continue;
11131 }
11132
11133 if (verifCond($values[4], '2')) {
11134 if ($values[3]) {
11135 if ($filterorigmodule) { // If a filter of module origin has been requested
11136 if (strpos($values[3], '@')) { // This is an external module
11137 if ($filterorigmodule != 'external') {
11138 continue;
11139 }
11140 } else { // This looks a core module
11141 if ($filterorigmodule != 'core') {
11142 continue;
11143 }
11144 }
11145 }
11146 $langs->load($values[3]);
11147 }
11148 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11149 // If label is "SUBSTITUION_..."
11150 $substitutionarray = array();
11151 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11152 $label = make_substitutions($reg[1], $substitutionarray);
11153 } else {
11154 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11155 $labeltemp = explode(',', $values[2]);
11156 $label = $langs->trans($labeltemp[0]);
11157
11158 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11159 dol_include_once($labeltemp[2]);
11160 $classtoload = $labeltemp[1];
11161 if (class_exists($classtoload)) {
11162 $obj = new $classtoload($db);
11163 $function = $labeltemp[3];
11164 if ($obj && $function && method_exists($obj, $function)) {
11165 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11166 $nbrec = $obj->$function($object->id, $obj);
11167 if (!empty($nbrec)) {
11168 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11169 }
11170 }
11171 }
11172 }
11173 }
11174
11175 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11176 $newtab[1] = $label;
11177 $newtab[2] = str_replace('+', '', $values[1]);
11178 $h++;
11179 } else {
11180 continue;
11181 }
11182 } elseif (count($values) == 5) { // case deprecated
11183 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11184
11185 if ($values[0] != $type) {
11186 continue;
11187 }
11188 if ($values[3]) {
11189 if ($filterorigmodule) { // If a filter of module origin has been requested
11190 if (strpos($values[3], '@')) { // This is an external module
11191 if ($filterorigmodule != 'external') {
11192 continue;
11193 }
11194 } else { // This looks a core module
11195 if ($filterorigmodule != 'core') {
11196 continue;
11197 }
11198 }
11199 }
11200 $langs->load($values[3]);
11201 }
11202 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11203 $substitutionarray = array();
11204 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11205 $label = make_substitutions($reg[1], $substitutionarray);
11206 } else {
11207 $label = $langs->trans($values[2]);
11208 }
11209
11210 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11211 $newtab[1] = $label;
11212 $newtab[2] = str_replace('+', '', $values[1]);
11213 $h++;
11214 }
11215 // set tab at its position
11216 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11217 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11218 if ($values[0] != $type) {
11219 continue;
11220 }
11221 $tabname = str_replace('-', '', $values[1]);
11222 foreach ($head as $key => $val) {
11223 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11224 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11225 if ($head[$key][2] == $tabname && $condition) {
11226 unset($head[$key]);
11227 break;
11228 }
11229 }
11230 }
11231 }
11232 }
11233
11234 // No need to make a return $head. Var is modified as a reference
11235 if (!empty($hookmanager)) {
11236 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11237 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11238 if ($reshook > 0) { // Hook ask to replace completely the array
11239 $head = $hookmanager->resArray;
11240 } else { // Hook
11241 $head = array_merge($head, $hookmanager->resArray);
11242 }
11243 $h = count($head);
11244 }
11245}
11246
11258function printCommonFooter($zone = 'private')
11259{
11260 global $conf, $hookmanager, $user, $langs;
11261 global $debugbar;
11262 global $action;
11263 global $micro_start_time;
11264
11265 if ($zone == 'private') {
11266 print "\n".'<!-- Common footer for private page -->'."\n";
11267 } else {
11268 print "\n".'<!-- Common footer for public page -->'."\n";
11269 }
11270
11271 // A div to store page_y POST parameter so we can read it using javascript
11272 print "\n<!-- A div to store page_y POST parameter -->\n";
11273 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11274
11275 $parameters = array();
11276 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11277 if (empty($reshook)) {
11278 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11279 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11280 }
11281
11282 print "\n";
11283 if (!empty($conf->use_javascript_ajax)) {
11284 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11285 print '<script>'."\n";
11286 print 'jQuery(document).ready(function() {'."\n";
11287
11288 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11289 print "\n";
11290 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11291 print 'jQuery("li.menuhider").click(function(event) {';
11292 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11293 print ' console.log("We click on .menuhider");'."\n";
11294 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11295 print '});'."\n";
11296 }
11297
11298 // Management of focus and mandatory for fields
11299 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"])))) {
11300 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11301 $relativepathstring = $_SERVER["PHP_SELF"];
11302 // Clean $relativepathstring
11303 if (constant('DOL_URL_ROOT')) {
11304 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11305 }
11306 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11307 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11308 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11309
11310 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11311 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11312 $qualified = 0;
11313 if ($defkey != '_noquery_') {
11314 $tmpqueryarraytohave = explode('&', $defkey);
11315 $foundintru = 0;
11316 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11317 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11318 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11319 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11320 $foundintru = 1;
11321 }
11322 }
11323 if (!$foundintru) {
11324 $qualified = 1;
11325 }
11326 //var_dump($defkey.'-'.$qualified);
11327 } else {
11328 $qualified = 1;
11329 }
11330
11331 if ($qualified) {
11332 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11333 foreach ($defval as $paramkey => $paramval) {
11334 // Set focus on field
11335 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11336 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11337 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11338 }
11339 }
11340 }
11341 }
11342 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11343 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11344 $qualified = 0;
11345 if ($defkey != '_noquery_') {
11346 $tmpqueryarraytohave = explode('&', $defkey);
11347 $foundintru = 0;
11348 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11349 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11350 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11351 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11352 $foundintru = 1;
11353 }
11354 }
11355 if (!$foundintru) {
11356 $qualified = 1;
11357 }
11358 //var_dump($defkey.'-'.$qualified);
11359 } else {
11360 $qualified = 1;
11361 }
11362
11363 if ($qualified) {
11364 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11365
11366 foreach ($defval as $paramkey => $paramval) {
11367 // Solution 1: Add handler on submit to check if mandatory fields are empty
11368 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11369 print "form.on('submit', function(event) {
11370 var submitter = event.originalEvent.submitter;
11371 if (submitter) {
11372 var buttonName = $(submitter).attr('name');
11373 if (buttonName == 'cancel') {
11374 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11375 return true;
11376 }
11377 }
11378
11379 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11380
11381 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11382 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11383
11384 if (tmptypefield == 'textarea') {
11385 // We must instead check the content of ckeditor
11386 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11387 if (tmpeditor) {
11388 tmpvalue = tmpeditor.getData();
11389 console.log('For textarea tmpvalue is '+tmpvalue);
11390 }
11391 }
11392
11393 let tmpvalueisempty = false;
11394 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
11395 tmpvalueisempty = true;
11396 }
11397 if (tmpvalue === '0' && tmptypefield == 'select') {
11398 tmpvalueisempty = true;
11399 }
11400 if (tmpvalueisempty) {
11401 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11402 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11403 event.stopPropagation(); // Stop other handlers.
11404 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11405 return false;
11406 }
11407 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11408 return true;
11409 });
11410 \n";
11411
11412 // Solution 2: Add property 'required' on input
11413 // so browser will check value and try to focus on it when submitting the form.
11414 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11415 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11416 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11417 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11418 //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";
11419 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11420 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11421 // Add 'field required' class on closest td for all input elements : input, textarea and select
11422 //print '}, 500);'; // 500 milliseconds delay
11423
11424 // Now set the class "fieldrequired"
11425 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11426 }
11427
11428
11429 // If we submit using the cancel button, we remove the required attributes
11430 print 'jQuery("input[name=\'cancel\']").click(function() {
11431 console.log("We click on cancel button so removed all required attribute");
11432 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11433 });'."\n";
11434 }
11435 }
11436 }
11437 }
11438
11439 print '});'."\n";
11440
11441 // End of tuning
11442 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11443 print "\n";
11444 print "/* JS CODE TO ENABLE to add memory info */\n";
11445 print 'window.console && console.log("';
11446 if (getDolGlobalString('MEMCACHED_SERVER')) {
11447 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11448 }
11449 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11450 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11451 $micro_end_time = microtime(true);
11452 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11453 }
11454
11455 if (function_exists("memory_get_usage")) {
11456 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11457 }
11458 if (function_exists("memory_get_peak_usage")) {
11459 print ' - Real mem peak: '.memory_get_peak_usage(true);
11460 }
11461 if (function_exists("zend_loader_file_encoded")) {
11462 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11463 }
11464 print '");'."\n";
11465 }
11466
11467 print "\n".'</script>'."\n";
11468
11469 // Google Analytics
11470 // TODO Add a hook here
11471 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11472 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11473 foreach ($tmptagarray as $tmptag) {
11474 print "\n";
11475 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11476 print '
11477 <!-- Global site tag (gtag.js) - Google Analytics -->
11478 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11479 <script>
11480 window.dataLayer = window.dataLayer || [];
11481 function gtag(){dataLayer.push(arguments);}
11482 gtag(\'js\', new Date());
11483
11484 gtag(\'config\', \''.trim($tmptag).'\');
11485 </script>';
11486 print "\n";
11487 }
11488 }
11489 }
11490
11491 // Add Xdebug coverage of code
11492 if (defined('XDEBUGCOVERAGE')) {
11493 print_r(xdebug_get_code_coverage());
11494 }
11495
11496 // Add DebugBar data
11497 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11498 if (isset($debugbar['time'])) {
11499 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11500 $debugbar['time']->stopMeasure('pageaftermaster');
11501 }
11502 print '<!-- Output debugbar data -->'."\n";
11503 $renderer = $debugbar->getJavascriptRenderer();
11504 print $renderer->render();
11505 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11506 print "\n";
11507 print "<!-- Start of log output\n";
11508 //print '<div class="hidden">'."\n";
11509 foreach ($conf->logbuffer as $logline) {
11510 print $logline."<br>\n";
11511 }
11512 //print '</div>'."\n";
11513 print "End of log output -->\n";
11514 }
11515 }
11516}
11517
11527function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11528{
11529 if (is_null($string)) {
11530 return array();
11531 }
11532
11533 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11534 // This is a regex string
11535 $newdelimiter = $delimiter;
11536 } else {
11537 // This is a simple string
11538 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11539 $newdelimiter = preg_quote($delimiter, '/');
11540 }
11541
11542 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11543 $ka = array();
11544 foreach ($a as $s) { // each part
11545 if ($s) {
11546 if ($pos = strpos($s, $kv)) { // key/value delimiter
11547 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11548 } else { // key delimiter not found
11549 $ka[] = trim($s);
11550 }
11551 }
11552 }
11553 return $ka;
11554 }
11555
11556 return array();
11557}
11558
11559
11566function dol_set_focus($selector)
11567{
11568 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11569 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11570}
11571
11572
11580function dol_getmypid()
11581{
11582 if (!function_exists('getmypid')) {
11583 return mt_rand(99900000, 99965535);
11584 } else {
11585 return getmypid(); // May be a number on 64 bits (depending on OS)
11586 }
11587}
11588
11589
11611function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11612{
11613 global $db, $langs;
11614
11615 $value = trim($value);
11616
11617 if ($mode == 0) {
11618 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11619 }
11620 if ($mode == 1) {
11621 $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
11622 }
11623
11624 $value = preg_replace('/\s*\|\s*/', '|', $value);
11625
11626 $crits = explode(' ', $value);
11627 $res = '';
11628 if (!is_array($fields)) {
11629 $fields = array($fields);
11630 }
11631
11632 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11633 foreach ($crits as $crit) { // Loop on each AND criteria
11634 $crit = trim($crit);
11635 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11636 $newres = '';
11637 foreach ($fields as $field) {
11638 if ($mode == 1) {
11639 $tmpcrits = explode('|', $crit);
11640 $i3 = 0; // count the nb of valid criteria added for this current field
11641 foreach ($tmpcrits as $tmpcrit) {
11642 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11643 continue;
11644 }
11645 $tmpcrit = trim($tmpcrit);
11646
11647 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11648
11649 $operator = '=';
11650 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11651
11652 $reg = array();
11653 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11654 if (!empty($reg[1])) {
11655 $operator = $reg[1];
11656 }
11657 if ($newcrit != '') {
11658 $numnewcrit = price2num($newcrit);
11659 if (is_numeric($numnewcrit)) {
11660 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11661 } else {
11662 $newres .= '1 = 2'; // force false, we received a corrupted data
11663 }
11664 $i3++; // a criteria was added to string
11665 }
11666 }
11667 $i2++; // a criteria for 1 more field was added to string
11668 } elseif ($mode == 2 || $mode == -2) {
11669 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11670 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11671 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11672 if ($mode == -2) {
11673 $newres .= ' OR '.$field.' IS NULL';
11674 }
11675 $i2++; // a criteria for 1 more field was added to string
11676 } elseif ($mode == 3 || $mode == -3) {
11677 $tmparray = explode(',', $crit);
11678 if (count($tmparray)) {
11679 $listofcodes = '';
11680 foreach ($tmparray as $val) {
11681 $val = trim($val);
11682 if ($val) {
11683 $listofcodes .= ($listofcodes ? ',' : '');
11684 $listofcodes .= "'".$db->escape($val)."'";
11685 }
11686 }
11687 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
11688 $i2++; // a criteria for 1 more field was added to string
11689 }
11690 if ($mode == -3) {
11691 $newres .= ' OR '.$field.' IS NULL';
11692 }
11693 } elseif ($mode == 4) {
11694 $tmparray = explode(',', $crit);
11695 if (count($tmparray)) {
11696 $listofcodes = '';
11697 foreach ($tmparray as $val) {
11698 $val = trim($val);
11699 if ($val) {
11700 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11701 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11702 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11703 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11704 $newres .= ')';
11705 $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)
11706 }
11707 }
11708 }
11709 } else { // $mode=0
11710 $tmpcrits = explode('|', $crit);
11711 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11712 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11713 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11714 continue;
11715 }
11716 $tmpcrit = trim($tmpcrit);
11717
11718 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11719 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11720 } else {
11721 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11722 }
11723
11724 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11725 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11726 } else {
11727 $tmpcrit2 = $tmpcrit;
11728 $tmpbefore = '%';
11729 $tmpafter = '%';
11730 $tmps = '';
11731
11732 if (preg_match('/^!/', $tmpcrit)) {
11733 $tmps .= $field." NOT LIKE "; // ! as exclude character
11734 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11735 } else {
11736 $tmps .= $field." LIKE ";
11737 }
11738 $tmps .= "'";
11739
11740 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11741 $tmpbefore = '';
11742 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11743 }
11744 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11745 $tmpafter = '';
11746 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11747 }
11748
11749 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11750 $tmps = "(".$tmps;
11751 }
11752 $newres .= $tmps;
11753 $newres .= $tmpbefore;
11754 $newres .= $db->escape($tmpcrit2);
11755 $newres .= $tmpafter;
11756 $newres .= "'";
11757 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11758 $newres .= " OR ".$field." IS NULL)";
11759 }
11760 }
11761
11762 $i3++;
11763 }
11764
11765 $i2++; // a criteria for 1 more field was added to string
11766 }
11767 }
11768
11769 if ($newres) {
11770 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11771 }
11772 $i1++;
11773 }
11774 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11775
11776 return $res;
11777}
11778
11785function showDirectDownloadLink($object)
11786{
11787 global $langs;
11788
11789 $out = '';
11790 $url = $object->getLastMainDocLink($object->element);
11791
11792 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11793 if ($url) {
11794 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11795 $out .= ajax_autoselect("directdownloadlink", '');
11796 } else {
11797 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11798 }
11799
11800 return $out;
11801}
11802
11811function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11812{
11813 $dirName = dirname($file);
11814 if ($dirName == '.') {
11815 $dirName = '';
11816 }
11817
11818 if (!in_array($extName, array('', '_small', '_mini'))) {
11819 return 'Bad parameter extName';
11820 }
11821
11822 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
11823 $fileName = basename($fileName);
11824
11825 if (empty($extImgTarget)) {
11826 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11827 }
11828 if (empty($extImgTarget)) {
11829 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11830 }
11831 if (empty($extImgTarget)) {
11832 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11833 }
11834 if (empty($extImgTarget)) {
11835 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11836 }
11837 if (empty($extImgTarget)) {
11838 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11839 }
11840 if (empty($extImgTarget)) {
11841 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11842 }
11843
11844 if (!$extImgTarget) {
11845 return $file;
11846 }
11847
11848 $subdir = '';
11849 if ($extName) {
11850 $subdir = 'thumbs/';
11851 }
11852
11853 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11854}
11855
11856
11866function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11867{
11868 global $conf, $langs;
11869
11870 if (empty($conf->use_javascript_ajax)) {
11871 return '';
11872 }
11873
11874 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11875
11876 if ($alldata == 1) {
11877 if ($isAllowedForPreview) {
11878 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));
11879 } else {
11880 return array();
11881 }
11882 }
11883
11884 // old behavior, return a string
11885 if ($isAllowedForPreview) {
11886 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11887 $title = $langs->transnoentities("Preview");
11888 //$title = '%27-alert(document.domain)-%27'; // An example of js injection into a corrupted title string, that should be blocked by the dol_escape_uri().
11889 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
11890
11891 // We need to do a dol_escape_uri() on the full string after the javascript: because such parts are the URI and when we click on such links, a RFC3986 decode is done,
11892 // by the browser, converting the %27 (like when having param file=abc%27def), or when having a corrupted title), into a ', BEFORE interpreting the content that can be a js code.
11893 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
11894 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
11895 } else {
11896 return '';
11897 }
11898}
11899
11906function getLabelSpecialCode($idcode)
11907{
11908 global $langs;
11909
11910 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
11911 if ($idcode > 10) {
11912 return 'Module ID '.$idcode;
11913 }
11914 if (!empty($arrayspecialines[$idcode])) {
11915 return $langs->trans($arrayspecialines[$idcode]);
11916 }
11917 return '';
11918}
11919
11928function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11929{
11930 global $langs;
11931 $out = '<script nonce="'.getNonce().'">
11932 jQuery(document).ready(function () {
11933 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11934 });
11935 </script>';
11936 if ($addlink) {
11937 if ($textonlink === 'image') {
11938 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11939 } else {
11940 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11941 }
11942 }
11943 return $out;
11944}
11945
11953function dolIsAllowedForPreview($file)
11954{
11955 // Check .noexe extension in filename
11956 if (preg_match('/\.noexe$/i', $file)) {
11957 return 0;
11958 }
11959
11960 // Check mime types
11961 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11962 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11963 $mime_preview[] = 'svg+xml';
11964 }
11965 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11966 //$mime_preview[]='archive';
11967 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11968 if ($num_mime !== false) {
11969 return 1;
11970 }
11971
11972 // By default, not allowed for preview
11973 return 0;
11974}
11975
11976
11986function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11987{
11988 $mime = $default;
11989 $imgmime = 'other.png';
11990 $famime = 'file-o';
11991 $srclang = '';
11992
11993 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11994
11995 // Plain text files
11996 if (preg_match('/\.txt$/i', $tmpfile)) {
11997 $mime = 'text/plain';
11998 $imgmime = 'text.png';
11999 $famime = 'file-alt';
12000 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
12001 $mime = 'text/richtext';
12002 $imgmime = 'text.png';
12003 $famime = 'file-alt';
12004 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
12005 $mime = 'text/csv';
12006 $imgmime = 'text.png';
12007 $famime = 'file-csv';
12008 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
12009 $mime = 'text/tab-separated-values';
12010 $imgmime = 'text.png';
12011 $famime = 'file-alt';
12012 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
12013 $mime = 'text/plain';
12014 $imgmime = 'text.png';
12015 $famime = 'file-alt';
12016 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
12017 $mime = 'text/plain';
12018 $imgmime = 'text.png';
12019 $srclang = 'ini';
12020 $famime = 'file-alt';
12021 } elseif (preg_match('/\.md$/i', $tmpfile)) {
12022 $mime = 'text/plain';
12023 $imgmime = 'text.png';
12024 $srclang = 'md';
12025 $famime = 'file-alt';
12026 } elseif (preg_match('/\.css$/i', $tmpfile)) {
12027 $mime = 'text/css';
12028 $imgmime = 'css.png';
12029 $srclang = 'css';
12030 $famime = 'file-alt';
12031 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
12032 $mime = 'text/plain';
12033 $imgmime = 'text.png';
12034 $srclang = 'lang';
12035 $famime = 'file-alt';
12036 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
12037 $mime = 'text/plain';
12038 $imgmime = 'text.png';
12039 $famime = 'file-alt';
12040 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
12041 $mime = 'text/html';
12042 $imgmime = 'html.png';
12043 $srclang = 'html';
12044 $famime = 'file-alt';
12045 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
12046 $mime = 'text/xml';
12047 $imgmime = 'other.png';
12048 $srclang = 'xml';
12049 $famime = 'file-alt';
12050 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
12051 $mime = 'text/xml';
12052 $imgmime = 'other.png';
12053 $srclang = 'xaml';
12054 $famime = 'file-alt';
12055 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
12056 $mime = 'text/plain';
12057 $imgmime = 'text.png';
12058 $srclang = 'bas';
12059 $famime = 'file-code';
12060 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
12061 $mime = 'text/plain';
12062 $imgmime = 'text.png';
12063 $srclang = 'c';
12064 $famime = 'file-code';
12065 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
12066 $mime = 'text/plain';
12067 $imgmime = 'text.png';
12068 $srclang = 'cpp';
12069 $famime = 'file-code';
12070 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
12071 $mime = 'text/plain';
12072 $imgmime = 'text.png';
12073 $srclang = 'cs';
12074 $famime = 'file-code';
12075 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
12076 $mime = 'text/plain';
12077 $imgmime = 'text.png';
12078 $srclang = 'h';
12079 $famime = 'file-code';
12080 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
12081 $mime = 'text/plain';
12082 $imgmime = 'text.png';
12083 $srclang = 'java';
12084 $famime = 'file-code';
12085 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
12086 $mime = 'text/plain';
12087 $imgmime = 'php.png';
12088 $srclang = 'php';
12089 $famime = 'file-code';
12090 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12091 $mime = 'text/plain';
12092 $imgmime = 'php.png';
12093 $srclang = 'php';
12094 $famime = 'file-code';
12095 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12096 $mime = 'text/plain';
12097 $imgmime = 'pl.png';
12098 $srclang = 'perl';
12099 $famime = 'file-code';
12100 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12101 $mime = 'text/plain';
12102 $imgmime = 'text.png';
12103 $srclang = 'sql';
12104 $famime = 'file-code';
12105 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12106 $mime = 'text/x-javascript';
12107 $imgmime = 'jscript.png';
12108 $srclang = 'js';
12109 $famime = 'file-code';
12110 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12111 $mime = 'application/vnd.oasis.opendocument.presentation';
12112 $imgmime = 'ooffice.png';
12113 $famime = 'file-powerpoint';
12114 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12115 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12116 $imgmime = 'ooffice.png';
12117 $famime = 'file-excel';
12118 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12119 $mime = 'application/vnd.oasis.opendocument.text';
12120 $imgmime = 'ooffice.png';
12121 $famime = 'file-word';
12122 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12123 $mime = 'application/msaccess';
12124 $imgmime = 'mdb.png';
12125 $famime = 'file';
12126 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12127 $mime = 'application/msword';
12128 $imgmime = 'doc.png';
12129 $famime = 'file-word';
12130 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12131 $mime = 'application/msword';
12132 $imgmime = 'doc.png';
12133 $famime = 'file-word';
12134 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12135 $mime = 'application/vnd.ms-excel';
12136 $imgmime = 'xls.png';
12137 $famime = 'file-excel';
12138 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12139 $mime = 'application/vnd.ms-excel';
12140 $imgmime = 'xls.png';
12141 $famime = 'file-excel';
12142 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12143 $mime = 'application/vnd.ms-excel';
12144 $imgmime = 'xls.png';
12145 $famime = 'file-excel';
12146 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12147 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12148 $imgmime = 'xls.png';
12149 $famime = 'file-excel';
12150 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12151 $mime = 'application/vnd.ms-powerpoint';
12152 $imgmime = 'ppt.png';
12153 $famime = 'file-powerpoint';
12154 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12155 $mime = 'application/x-mspowerpoint';
12156 $imgmime = 'ppt.png';
12157 $famime = 'file-powerpoint';
12158 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12159 $mime = 'application/pdf';
12160 $imgmime = 'pdf.png';
12161 $famime = 'file-pdf';
12162 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12163 $mime = 'text/x-bat';
12164 $imgmime = 'script.png';
12165 $srclang = 'dos';
12166 $famime = 'file-code';
12167 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12168 $mime = 'text/x-sh';
12169 $imgmime = 'script.png';
12170 $srclang = 'bash';
12171 $famime = 'file-code';
12172 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12173 $mime = 'text/x-ksh';
12174 $imgmime = 'script.png';
12175 $srclang = 'bash';
12176 $famime = 'file-code';
12177 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12178 $mime = 'text/x-bash';
12179 $imgmime = 'script.png';
12180 $srclang = 'bash';
12181 $famime = 'file-code';
12182 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12183 $mime = 'image/x-icon';
12184 $imgmime = 'image.png';
12185 $famime = 'file-image';
12186 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12187 $mime = 'image/jpeg';
12188 $imgmime = 'image.png';
12189 $famime = 'file-image';
12190 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12191 $mime = 'image/png';
12192 $imgmime = 'image.png';
12193 $famime = 'file-image';
12194 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12195 $mime = 'image/gif';
12196 $imgmime = 'image.png';
12197 $famime = 'file-image';
12198 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12199 $mime = 'image/bmp';
12200 $imgmime = 'image.png';
12201 $famime = 'file-image';
12202 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12203 $mime = 'image/tiff';
12204 $imgmime = 'image.png';
12205 $famime = 'file-image';
12206 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12207 $mime = 'image/svg+xml';
12208 $imgmime = 'image.png';
12209 $famime = 'file-image';
12210 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12211 $mime = 'image/webp';
12212 $imgmime = 'image.png';
12213 $famime = 'file-image';
12214 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12215 $mime = 'text/calendar';
12216 $imgmime = 'other.png';
12217 $famime = 'file-alt';
12218 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12219 $mime = 'text/calendar';
12220 $imgmime = 'other.png';
12221 $famime = 'file-alt';
12222 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12223 $mime = 'application/x-bittorrent';
12224 $imgmime = 'other.png';
12225 $famime = 'file-o';
12226 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12227 $mime = 'audio';
12228 $imgmime = 'audio.png';
12229 $famime = 'file-audio';
12230 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12231 $mime = 'video/mp4';
12232 $imgmime = 'video.png';
12233 $famime = 'file-video';
12234 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12235 $mime = 'video/ogg';
12236 $imgmime = 'video.png';
12237 $famime = 'file-video';
12238 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12239 $mime = 'video/webm';
12240 $imgmime = 'video.png';
12241 $famime = 'file-video';
12242 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12243 $mime = 'video/x-msvideo';
12244 $imgmime = 'video.png';
12245 $famime = 'file-video';
12246 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12247 $mime = 'video/divx';
12248 $imgmime = 'video.png';
12249 $famime = 'file-video';
12250 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12251 $mime = 'video/xvid';
12252 $imgmime = 'video.png';
12253 $famime = 'file-video';
12254 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12255 $mime = 'video';
12256 $imgmime = 'video.png';
12257 $famime = 'file-video';
12258 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12259 // application/xxx where zzz is zip, ...
12260 $mime = 'archive';
12261 $imgmime = 'archive.png';
12262 $famime = 'file-archive';
12263 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12264 $mime = 'application/octet-stream';
12265 $imgmime = 'other.png';
12266 $famime = 'file-o';
12267 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12268 $mime = 'library';
12269 $imgmime = 'library.png';
12270 $famime = 'file-o';
12271 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
12272 $mime = 'error';
12273 $imgmime = 'error.png';
12274 $famime = 'file-alt';
12275 }
12276
12277 // Return mimetype string
12278 switch ((int) $mode) {
12279 case 1:
12280 $tmp = explode('/', $mime);
12281 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12282 case 2:
12283 return $imgmime;
12284 case 3:
12285 return $srclang;
12286 case 4:
12287 return $famime;
12288 }
12289 return $mime;
12290}
12291
12303function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12304{
12305 global $conf, $db;
12306
12307 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12308
12309 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12310
12311 if (is_null($dictvalues)) {
12312 $dictvalues = array();
12313
12314 $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
12315 if ($checkentity) {
12316 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12317 }
12318
12319 $resql = $db->query($sql);
12320 if ($resql) {
12321 while ($obj = $db->fetch_object($resql)) {
12322 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12323 }
12324 } else {
12325 dol_print_error($db);
12326 }
12327
12328 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12329 }
12330
12331 if (!empty($dictvalues[$id])) {
12332 // Found
12333 $tmp = $dictvalues[$id];
12334 return (property_exists($tmp, $field) ? $tmp->$field : '');
12335 } else {
12336 // Not found
12337 return '';
12338 }
12339}
12340
12347function colorIsLight($stringcolor)
12348{
12349 $stringcolor = str_replace('#', '', $stringcolor);
12350 $res = -1;
12351 if (!empty($stringcolor)) {
12352 $res = 0;
12353 $tmp = explode(',', $stringcolor);
12354 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12355 $r = $tmp[0];
12356 $g = $tmp[1];
12357 $b = $tmp[2];
12358 } else {
12359 $hexr = $stringcolor[0].$stringcolor[1];
12360 $hexg = $stringcolor[2].$stringcolor[3];
12361 $hexb = $stringcolor[4].$stringcolor[5];
12362 $r = hexdec($hexr);
12363 $g = hexdec($hexg);
12364 $b = hexdec($hexb);
12365 }
12366 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12367 if ($bright > 0.6) {
12368 $res = 1;
12369 }
12370 }
12371 return $res;
12372}
12373
12382function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12383{
12384 global $conf;
12385
12386 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12387 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12388 if (empty($menuentry['enabled'])) {
12389 return 0; // Entry disabled by condition
12390 }
12391 if ($type_user && $menuentry['module']) {
12392 $tmploops = explode('|', $menuentry['module']);
12393 $found = 0;
12394 foreach ($tmploops as $tmploop) {
12395 if (in_array($tmploop, $listofmodulesforexternal)) {
12396 $found++;
12397 break;
12398 }
12399 }
12400 if (!$found) {
12401 return 0; // Entry is for menus all excluded to external users
12402 }
12403 }
12404 if (!$menuentry['perms'] && $type_user) {
12405 return 0; // No permissions and user is external
12406 }
12407 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12408 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12409 }
12410 if (!$menuentry['perms']) {
12411 return 2; // No permissions and user is external
12412 }
12413 return 1;
12414}
12415
12423function roundUpToNextMultiple($n, $x = 5)
12424{
12425 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12426 return (int) $result;
12427}
12428
12440function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12441{
12442 $csstouse = 'badge';
12443 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12444 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12445 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12446
12447 $attr = array(
12448 'class' => $csstouse
12449 );
12450
12451 if (empty($html)) {
12452 $html = $label;
12453 }
12454
12455 if (!empty($url)) {
12456 $attr['href'] = $url;
12457 }
12458
12459 if ($mode === 'dot') {
12460 $attr['class'] .= ' classfortooltip';
12461 $attr['title'] = $html;
12462 $attr['aria-label'] = $label;
12463 $html = '';
12464 }
12465
12466 // Override attr
12467 if (!empty($params['attr']) && is_array($params['attr'])) {
12468 foreach ($params['attr'] as $key => $value) {
12469 if ($key == 'class') {
12470 $attr['class'] .= ' '.$value;
12471 } elseif ($key == 'classOverride') {
12472 $attr['class'] = $value;
12473 } else {
12474 $attr[$key] = $value;
12475 }
12476 }
12477 }
12478
12479 // TODO: add hook
12480
12481 // escape all attribute
12482 $attr = array_map('dol_escape_htmltag', $attr);
12483
12484 $TCompiledAttr = array();
12485 foreach ($attr as $key => $value) {
12486 $TCompiledAttr[] = $key.'="'.$value.'"';
12487 }
12488
12489 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12490
12491 $tag = !empty($url) ? 'a' : 'span';
12492
12493 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12494}
12495
12496
12509function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12510{
12511 global $conf;
12512
12513 $return = '';
12514 $dolGetBadgeParams = array();
12515
12516 if (!empty($params['badgeParams'])) {
12517 $dolGetBadgeParams = $params['badgeParams'];
12518 }
12519
12520 // TODO : add a hook
12521 if ($displayMode == 0) {
12522 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12523 } elseif ($displayMode == 1) {
12524 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12525 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12526 // Use status with images (for backward compatibility)
12527 $return = '';
12528 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12529 $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>' : '');
12530
12531 // For small screen, we always use the short label instead of long label.
12532 if (!empty($conf->dol_optimize_smallscreen)) {
12533 if ($displayMode == 0) {
12534 $displayMode = 1;
12535 } elseif ($displayMode == 4) {
12536 $displayMode = 2;
12537 } elseif ($displayMode == 6) {
12538 $displayMode = 5;
12539 }
12540 }
12541
12542 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12543 $statusImg = array(
12544 'status0' => 'statut0',
12545 'status1' => 'statut1',
12546 'status2' => 'statut2',
12547 'status3' => 'statut3',
12548 'status4' => 'statut4',
12549 'status5' => 'statut5',
12550 'status6' => 'statut6',
12551 'status7' => 'statut7',
12552 'status8' => 'statut8',
12553 'status9' => 'statut9'
12554 );
12555
12556 if (!empty($statusImg[$statusType])) {
12557 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12558 } else {
12559 $htmlImg = img_picto($statusLabel, $statusType);
12560 }
12561
12562 if ($displayMode === 2) {
12563 $return = $htmlImg.' '.$htmlLabelShort;
12564 } elseif ($displayMode === 3) {
12565 $return = $htmlImg;
12566 } elseif ($displayMode === 4) {
12567 $return = $htmlImg.' '.$htmlLabel;
12568 } elseif ($displayMode === 5) {
12569 $return = $htmlLabelShort.' '.$htmlImg;
12570 } else { // $displayMode >= 6
12571 $return = $htmlLabel.' '.$htmlImg;
12572 }
12573 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12574 // Use new badge
12575 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12576
12577 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12578 if (empty($dolGetBadgeParams['attr']['title'])) {
12579 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12580 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12581 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12582 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
12583 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12584 }
12585
12586 if ($displayMode == 3) {
12587 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12588 } elseif ($displayMode === 5) {
12589 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12590 } else {
12591 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12592 }
12593 }
12594
12595 return $return;
12596}
12597
12598
12637function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12638{
12639 global $hookmanager, $action, $object, $langs;
12640
12641 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12642 if (is_array($url)) {
12643 // Loop on $url array to remove entries of disabled modules
12644 foreach ($url as $key => $subbutton) {
12645 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12646 unset($url[$key]);
12647 }
12648 }
12649
12650 $out = '';
12651
12652 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
12653 foreach ($url as $button) {
12654 if (!empty($button['lang'])) {
12655 $langs->load($button['lang']);
12656 }
12657 $label = $langs->trans($button['label']);
12658 $text = $button['text'] ?? '';
12659 $actionType = $button['actionType'] ?? '';
12660 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12661 $id = $button['id'] ?? '';
12662 $userRight = $button['perm'] ?? 1;
12663 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
12664
12665 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12666 }
12667 return $out;
12668 }
12669
12670 if (count($url) > 1) {
12671 $out .= '<div class="dropdown inline-block dropdown-holder">';
12672 $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>';
12673 $out .= '<div class="dropdown-content">';
12674 foreach ($url as $subbutton) {
12675 if (!empty($subbutton['lang'])) {
12676 $langs->load($subbutton['lang']);
12677 }
12678
12679 if (!empty($subbutton['urlraw'])) {
12680 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12681 } else {
12682 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12683 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12684 }
12685
12686 $subbuttonparam = array();
12687 if (!empty($subbutton['attr'])) {
12688 $subbuttonparam['attr'] = $subbutton['attr'];
12689 }
12690 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
12691
12692 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12693 }
12694 $out .= "</div>";
12695 $out .= "</div>";
12696 } else {
12697 foreach ($url as $subbutton) { // Should loop on 1 record only
12698 if (!empty($subbutton['lang'])) {
12699 $langs->load($subbutton['lang']);
12700 }
12701
12702 if (!empty($subbutton['urlraw'])) {
12703 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12704 } else {
12705 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12706 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12707 }
12708
12709 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12710 }
12711 }
12712
12713 return $out;
12714 }
12715
12716 // Here, $url is a simple link
12717
12718 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12719 $class = "dropdown-item";
12720 } else {
12721 $class = 'butAction';
12722 if ($actionType == 'danger' || $actionType == 'delete') {
12723 $class = 'butActionDelete';
12724 if (!empty($url) && strpos($url, 'token=') === false) {
12725 $url .= '&token='.newToken();
12726 }
12727 }
12728 }
12729 $attr = array(
12730 'class' => $class,
12731 'href' => empty($url) ? '' : $url,
12732 'title' => $label
12733 );
12734
12735 if (empty($text)) {
12736 $text = $label;
12737 $attr['title'] = ''; // if html not set, leave label on title is redundant
12738 } else {
12739 $attr['title'] = $label;
12740 $attr['aria-label'] = $label;
12741 }
12742
12743 if (empty($userRight)) {
12744 $attr['class'] = 'butActionRefused';
12745 $attr['href'] = '';
12746 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12747 $attr['title'] = ($attr['title'] ? $attr['title'].'<br>' : '').$langs->trans('NotEnoughPermissions');
12748 }
12749
12750 if (!empty($id)) {
12751 $attr['id'] = $id;
12752 }
12753
12754 // Override attr
12755 if (!empty($params['attr']) && is_array($params['attr'])) {
12756 foreach ($params['attr'] as $key => $value) {
12757 if ($key == 'class') {
12758 $attr['class'] .= ' '.$value;
12759 } elseif ($key == 'classOverride') {
12760 $attr['class'] = $value;
12761 } else {
12762 $attr[$key] = $value;
12763 }
12764 }
12765 }
12766
12767 // automatic add tooltip when title is detected
12768 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12769 $attr['class'] .= ' classfortooltip';
12770 }
12771
12772 // Js Confirm button
12773 if ($userRight && !empty($params['confirm'])) {
12774 if (!is_array($params['confirm'])) {
12775 $params['confirm'] = array();
12776 }
12777
12778 if (empty($params['confirm']['url'])) {
12779 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12780 }
12781
12782 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12783 $attr['data-confirm-url'] = $params['confirm']['url'];
12784 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12785 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12786 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12787 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12788 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12789 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12790
12791 $attr['class'] .= ' butActionConfirm';
12792 }
12793
12794 if (isset($attr['href']) && empty($attr['href'])) {
12795 unset($attr['href']);
12796 }
12797
12798 // escape all attribute
12799 $attr = array_map('dol_escape_htmltag', $attr);
12800
12801 $TCompiledAttr = array();
12802 foreach ($attr as $key => $value) {
12803 $TCompiledAttr[] = $key.'= "'.$value.'"';
12804 }
12805
12806 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12807
12808 $tag = !empty($attr['href']) ? 'a' : 'span';
12809
12810
12811 $parameters = array(
12812 'TCompiledAttr' => $TCompiledAttr, // array
12813 'compiledAttributes' => $compiledAttributes, // string
12814 'attr' => $attr,
12815 'tag' => $tag,
12816 'label' => $label,
12817 'html' => $text,
12818 'actionType' => $actionType,
12819 'url' => $url,
12820 'id' => $id,
12821 'userRight' => $userRight,
12822 'params' => $params
12823 );
12824
12825 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12826 if ($reshook < 0) {
12827 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12828 }
12829
12830 if (empty($reshook)) {
12831 if (dol_textishtml($text)) { // If content already HTML encoded
12832 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
12833 } else {
12834 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
12835 }
12836 } else {
12837 return $hookmanager->resPrint;
12838 }
12839}
12840
12841
12850function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
12851{
12852 if (empty($url)) {
12853 return '';
12854 }
12855
12856 $parsedUrl = parse_url($url);
12857 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
12858 return $url;
12859 }
12860
12861 if (!empty($parsedUrl['query'])) {
12862 // Use parse_str() function to parse the string passed via URL
12863 parse_str($parsedUrl['query'], $urlQuery);
12864 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
12865 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
12866 }
12867 }
12868
12869 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
12870 $url = DOL_URL_ROOT.$url;
12871 }
12872
12873 return $url;
12874}
12875
12876
12883function dolGetButtonTitleSeparator($moreClass = "")
12884{
12885 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12886}
12887
12894function getFieldErrorIcon($fieldValidationErrorMsg)
12895{
12896 $out = '';
12897 if (!empty($fieldValidationErrorMsg)) {
12898 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12899 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12900 $out .= '</span>';
12901 }
12902
12903 return $out;
12904}
12905
12918function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12919{
12920 global $langs, $conf, $user;
12921
12922 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12923 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12924 return '';
12925 }
12926
12927 $class = 'btnTitle';
12928 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12929 $class .= ' btnTitlePlus';
12930 }
12931 $useclassfortooltip = 1;
12932
12933 if (!empty($params['morecss'])) {
12934 $class .= ' '.$params['morecss'];
12935 }
12936
12937 $attr = array(
12938 'class' => $class,
12939 'href' => empty($url) ? '' : $url
12940 );
12941
12942 if (!empty($helpText)) {
12943 $attr['title'] = dol_escape_htmltag($helpText);
12944 } elseif ($label) { // empty($attr['title']) &&
12945 $attr['title'] = $label;
12946 $useclassfortooltip = 0;
12947 }
12948
12949 if ($status == 2) {
12950 $attr['class'] .= ' btnTitleSelected';
12951 } elseif ($status <= 0) {
12952 $attr['class'] .= ' refused';
12953
12954 $attr['href'] = '';
12955
12956 if ($status == -1) { // disable
12957 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12958 } elseif ($status == 0) { // Not enough permissions
12959 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12960 }
12961 }
12962
12963 if (!empty($attr['title']) && $useclassfortooltip) {
12964 $attr['class'] .= ' classfortooltip';
12965 }
12966
12967 if (!empty($id)) {
12968 $attr['id'] = $id;
12969 }
12970
12971 // Override attr
12972 if (!empty($params['attr']) && is_array($params['attr'])) {
12973 foreach ($params['attr'] as $key => $value) {
12974 if ($key == 'class') {
12975 $attr['class'] .= ' '.$value;
12976 } elseif ($key == 'classOverride') {
12977 $attr['class'] = $value;
12978 } else {
12979 $attr[$key] = $value;
12980 }
12981 }
12982 }
12983
12984 if (isset($attr['href']) && empty($attr['href'])) {
12985 unset($attr['href']);
12986 }
12987
12988 // TODO : add a hook
12989
12990 $TCompiledAttr = array();
12991 foreach ($attr as $key => $value) {
12992 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
12993 }
12994
12995 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12996
12997 $tag = (empty($attr['href']) ? 'span' : 'a');
12998
12999 $button = '<'.$tag.' '.$compiledAttributes.'>';
13000 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
13001 if (!empty($params['forcenohideoftext'])) {
13002 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
13003 }
13004 $button .= '</'.$tag.'>';
13005
13006 return $button;
13007}
13008
13018function getElementProperties($elementType)
13019{
13020 global $conf, $db, $hookmanager;
13021
13022 $regs = array();
13023
13024 //$element_type='facture';
13025
13026 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
13027
13028 // Parse element/subelement
13029 $module = $elementType;
13030 $element = $elementType;
13031 $subelement = $elementType;
13032 $table_element = $elementType;
13033
13034 // If we ask a resource form external module (instead of default path)
13035 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
13036 $element = $subelement = $regs[1];
13037 $module = $regs[2];
13038 }
13039
13040 // If we ask a resource for a string with an element and a subelement
13041 // Example 'project_task'
13042 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
13043 $module = $element = $regs[1];
13044 $subelement = $regs[2];
13045 }
13046
13047 // Object lines will use parent classpath and module ref
13048 if (substr($elementType, -3) == 'det') {
13049 $module = preg_replace('/det$/', '', $element);
13050 $subelement = preg_replace('/det$/', '', $subelement);
13051 $classpath = $module.'/class';
13052 $classfile = $module;
13053 $classname = preg_replace('/det$/', 'Line', $element);
13054 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
13055 $classname = preg_replace('/det$/', 'Ligne', $element);
13056 }
13057 }
13058 // For compatibility and to work with non standard path
13059 if ($elementType == "action" || $elementType == "actioncomm") {
13060 $classpath = 'comm/action/class';
13061 $subelement = 'Actioncomm';
13062 $module = 'agenda';
13063 $table_element = 'actioncomm';
13064 } elseif ($elementType == 'cronjob') {
13065 $classpath = 'cron/class';
13066 $module = 'cron';
13067 $table_element = 'cron';
13068 } elseif ($elementType == 'adherent_type') {
13069 $classpath = 'adherents/class';
13070 $classfile = 'adherent_type';
13071 $module = 'adherent';
13072 $subelement = 'adherent_type';
13073 $classname = 'AdherentType';
13074 $table_element = 'adherent_type';
13075 } elseif ($elementType == 'bank_account') {
13076 $classpath = 'compta/bank/class';
13077 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13078 $classfile = 'account';
13079 $classname = 'Account';
13080 } elseif ($elementType == 'category') {
13081 $classpath = 'categories/class';
13082 $module = 'categorie';
13083 $subelement = 'categorie';
13084 $table_element = 'categorie';
13085 } elseif ($elementType == 'contact') {
13086 $classpath = 'contact/class';
13087 $classfile = 'contact';
13088 $module = 'societe';
13089 $subelement = 'contact';
13090 $table_element = 'socpeople';
13091 } elseif ($elementType == 'inventory') {
13092 $module = 'product';
13093 $classpath = 'product/inventory/class';
13094 } elseif ($elementType == 'inventoryline') {
13095 $module = 'product';
13096 $classpath = 'product/inventory/class';
13097 $table_element = 'inventorydet';
13098 $parent_element = 'inventory';
13099 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
13100 $module = 'stock';
13101 $classpath = 'product/stock/class';
13102 $classfile = 'entrepot';
13103 $classname = 'Entrepot';
13104 $table_element = 'entrepot';
13105 } elseif ($elementType == 'project') {
13106 $classpath = 'projet/class';
13107 $module = 'projet';
13108 $table_element = 'projet';
13109 } elseif ($elementType == 'project_task') {
13110 $classpath = 'projet/class';
13111 $module = 'projet';
13112 $subelement = 'task';
13113 $table_element = 'projet_task';
13114 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13115 $classpath = 'compta/facture/class';
13116 $module = 'facture';
13117 $subelement = 'facture';
13118 $table_element = 'facture';
13119 } elseif ($elementType == 'facturedet') {
13120 $classpath = 'compta/facture/class';
13121 $classfile = 'facture';
13122 $classname = 'FactureLigne';
13123 $module = 'facture';
13124 $table_element = 'facturedet';
13125 $parent_element = 'facture';
13126 } elseif ($elementType == 'facturerec') {
13127 $classpath = 'compta/facture/class';
13128 $module = 'facture';
13129 $classname = 'FactureRec';
13130 } elseif ($elementType == 'commande' || $elementType == 'order') {
13131 $classpath = 'commande/class';
13132 $module = 'commande';
13133 $subelement = 'commande';
13134 $table_element = 'commande';
13135 } elseif ($elementType == 'commandedet') {
13136 $classpath = 'commande/class';
13137 $classfile = 'commande';
13138 $classname = 'OrderLine';
13139 $module = 'commande';
13140 $table_element = 'commandedet';
13141 $parent_element = 'commande';
13142 } elseif ($elementType == 'propal') {
13143 $classpath = 'comm/propal/class';
13144 $table_element = 'propal';
13145 } elseif ($elementType == 'propaldet') {
13146 $classpath = 'comm/propal/class';
13147 $classfile = 'propal';
13148 $subelement = 'propaleligne';
13149 $module = 'propal';
13150 $table_element = 'propaldet';
13151 $parent_element = 'propal';
13152 } elseif ($elementType == 'shipping') {
13153 $classpath = 'expedition/class';
13154 $classfile = 'expedition';
13155 $classname = 'Expedition';
13156 $module = 'expedition';
13157 $table_element = 'expedition';
13158 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13159 $classpath = 'expedition/class';
13160 $classfile = 'expedition';
13161 $classname = 'ExpeditionLigne';
13162 $module = 'expedition';
13163 $table_element = 'expeditiondet';
13164 $parent_element = 'expedition';
13165 } elseif ($elementType == 'delivery_note') {
13166 $classpath = 'delivery/class';
13167 $subelement = 'delivery';
13168 $module = 'expedition';
13169 } elseif ($elementType == 'delivery') {
13170 $classpath = 'delivery/class';
13171 $subelement = 'delivery';
13172 $module = 'expedition';
13173 } elseif ($elementType == 'deliverydet') {
13174 // @todo
13175 } elseif ($elementType == 'supplier_proposal') {
13176 $classpath = 'supplier_proposal/class';
13177 $module = 'supplier_proposal';
13178 $element = 'supplierproposal';
13179 $classfile = 'supplier_proposal';
13180 $subelement = 'supplierproposal';
13181 } elseif ($elementType == 'supplier_proposaldet') {
13182 $classpath = 'supplier_proposal/class';
13183 $module = 'supplier_proposal';
13184 $classfile = 'supplier_proposal';
13185 $table_element = 'supplier_proposaldet';
13186 $parent_element = 'supplier_proposal';
13187 } elseif ($elementType == 'contract') {
13188 $classpath = 'contrat/class';
13189 $module = 'contrat';
13190 $subelement = 'contrat';
13191 $table_element = 'contract';
13192 } elseif ($elementType == 'contratdet') {
13193 $classpath = 'contrat/class';
13194 $module = 'contrat';
13195 $table_element = 'contratdet';
13196 $parent_element = 'contrat';
13197 } elseif ($elementType == 'mailing') {
13198 $classpath = 'comm/mailing/class';
13199 $module = 'mailing';
13200 $classfile = 'mailing';
13201 $classname = 'Mailing';
13202 $subelement = '';
13203 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13204 $classpath = 'adherents/class';
13205 $module = 'adherent';
13206 $subelement = 'adherent';
13207 $table_element = 'adherent';
13208 } elseif ($elementType == 'usergroup') {
13209 $classpath = 'user/class';
13210 $module = 'user';
13211 } elseif ($elementType == 'mo') {
13212 $classpath = 'mrp/class';
13213 $classfile = 'mo';
13214 $classname = 'Mo';
13215 $module = 'mrp';
13216 $subelement = '';
13217 $table_element = 'mrp_mo';
13218 } elseif ($elementType == 'mrp_production') {
13219 $classpath = 'mrp/class';
13220 $classfile = 'mo';
13221 $classname = 'MoLine';
13222 $module = 'mrp';
13223 $subelement = '';
13224 $table_element = 'mrp_production';
13225 $parent_element = 'mo';
13226 } elseif ($elementType == 'cabinetmed_cons') {
13227 $classpath = 'cabinetmed/class';
13228 $module = 'cabinetmed';
13229 $subelement = 'cabinetmedcons';
13230 $table_element = 'cabinetmedcons';
13231 } elseif ($elementType == 'fichinter') {
13232 $classpath = 'fichinter/class';
13233 $module = 'ficheinter';
13234 $subelement = 'fichinter';
13235 $table_element = 'fichinter';
13236 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13237 $classpath = 'resource/class';
13238 $module = 'resource';
13239 $subelement = 'dolresource';
13240 $table_element = 'resource';
13241 } elseif ($elementType == 'opensurvey_sondage') {
13242 $classpath = 'opensurvey/class';
13243 $module = 'opensurvey';
13244 $subelement = 'opensurveysondage';
13245 } elseif ($elementType == 'order_supplier' || $elementType == 'commande_fournisseur') {
13246 $classpath = 'fourn/class';
13247 $module = 'fournisseur';
13248 $classfile = 'fournisseur.commande';
13249 $element = 'order_supplier';
13250 $subelement = '';
13251 $classname = 'CommandeFournisseur';
13252 $table_element = 'commande_fournisseur';
13253 } elseif ($elementType == 'commande_fournisseurdet') {
13254 $classpath = 'fourn/class';
13255 $module = 'fournisseur';
13256 $classfile = 'fournisseur.commande';
13257 $element = 'commande_fournisseurdet';
13258 $subelement = '';
13259 $classname = 'CommandeFournisseurLigne';
13260 $table_element = 'commande_fournisseurdet';
13261 $parent_element = 'commande_fournisseur';
13262 } elseif ($elementType == 'invoice_supplier') {
13263 $classpath = 'fourn/class';
13264 $module = 'fournisseur';
13265 $classfile = 'fournisseur.facture';
13266 $element = 'invoice_supplier';
13267 $subelement = '';
13268 $classname = 'FactureFournisseur';
13269 $table_element = 'facture_fourn';
13270 } elseif ($elementType == 'facture_fourn_det') {
13271 $classpath = 'fourn/class';
13272 $module = 'fournisseur';
13273 $classfile = 'fournisseur.facture';
13274 $element = 'facture_fourn_det';
13275 $subelement = '';
13276 $classname = 'SupplierInvoiceLine';
13277 $table_element = 'facture_fourn_det';
13278 $parent_element = 'invoice_supplier';
13279 } elseif ($elementType == "service") {
13280 $classpath = 'product/class';
13281 $subelement = 'product';
13282 $table_element = 'product';
13283 } elseif ($elementType == 'salary') {
13284 $classpath = 'salaries/class';
13285 $module = 'salaries';
13286 } elseif ($elementType == 'payment_salary') {
13287 $classpath = 'salaries/class';
13288 $classfile = 'paymentsalary';
13289 $classname = 'PaymentSalary';
13290 $module = 'salaries';
13291 } elseif ($elementType == 'productlot') {
13292 $module = 'productbatch';
13293 $classpath = 'product/stock/class';
13294 $classfile = 'productlot';
13295 $classname = 'Productlot';
13296 $element = 'productlot';
13297 $subelement = '';
13298 $table_element = 'product_lot';
13299 } elseif ($elementType == 'societeaccount') {
13300 $classpath = 'societe/class';
13301 $classfile = 'societeaccount';
13302 $classname = 'SocieteAccount';
13303 $module = 'societe';
13304 } elseif ($elementType == 'websitepage') {
13305 $classpath = 'website/class';
13306 $classfile = 'websitepage';
13307 $classname = 'Websitepage';
13308 $module = 'website';
13309 $subelement = 'websitepage';
13310 $table_element = 'website_page';
13311 } elseif ($elementType == 'fiscalyear') {
13312 $classpath = 'core/class';
13313 $module = 'accounting';
13314 $subelement = 'fiscalyear';
13315 } elseif ($elementType == 'chargesociales') {
13316 $classpath = 'compta/sociales/class';
13317 $module = 'tax';
13318 $table_element = 'chargesociales';
13319 } elseif ($elementType == 'tva') {
13320 $classpath = 'compta/tva/class';
13321 $module = 'tax';
13322 $subdir = '/vat';
13323 $table_element = 'tva';
13324 } elseif ($elementType == 'emailsenderprofile') {
13325 $module = '';
13326 $classpath = 'core/class';
13327 $classfile = 'emailsenderprofile';
13328 $classname = 'EmailSenderProfile';
13329 $table_element = 'c_email_senderprofile';
13330 $subelement = '';
13331 } elseif ($elementType == 'conferenceorboothattendee') {
13332 $classpath = 'eventorganization/class';
13333 $classfile = 'conferenceorboothattendee';
13334 $classname = 'ConferenceOrBoothAttendee';
13335 $module = 'eventorganization';
13336 } elseif ($elementType == 'conferenceorbooth') {
13337 $classpath = 'eventorganization/class';
13338 $classfile = 'conferenceorbooth';
13339 $classname = 'ConferenceOrBooth';
13340 $module = 'eventorganization';
13341 } elseif ($elementType == 'ccountry') {
13342 $module = '';
13343 $classpath = 'core/class';
13344 $classfile = 'ccountry';
13345 $classname = 'Ccountry';
13346 $table_element = 'c_country';
13347 $subelement = '';
13348 } elseif ($elementType == 'ecmfiles') {
13349 $module = 'ecm';
13350 $classpath = 'ecm/class';
13351 $classfile = 'ecmfiles';
13352 $classname = 'Ecmfiles';
13353 $table_element = 'ecmfiles';
13354 $subelement = '';
13355 } elseif ($elementType == 'knowledgerecord') {
13356 $module = '';
13357 $classpath = 'knowledgemanagement/class';
13358 $classfile = 'knowledgerecord';
13359 $classname = 'KnowledgeRecord';
13360 $table_element = 'knowledgemanagement_knowledgerecord';
13361 $subelement = '';
13362 }
13363
13364 if (empty($classfile)) {
13365 $classfile = strtolower($subelement);
13366 }
13367 if (empty($classname)) {
13368 $classname = ucfirst($subelement);
13369 }
13370 if (empty($classpath)) {
13371 $classpath = $module.'/class';
13372 }
13373
13374 //print 'getElementProperties subdir='.$subdir;
13375
13376 // Set dir_output
13377 if ($module && isset($conf->$module)) { // The generic case
13378 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13379 $dir_output = $conf->$module->multidir_output[$conf->entity];
13380 } elseif (!empty($conf->$module->output[$conf->entity])) {
13381 $dir_output = $conf->$module->output[$conf->entity];
13382 } elseif (!empty($conf->$module->dir_output)) {
13383 $dir_output = $conf->$module->dir_output;
13384 }
13385 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
13386 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
13387 } elseif (!empty($conf->$module->temp[$conf->entity])) {
13388 $dir_temp = $conf->$module->temp[$conf->entity];
13389 } elseif (!empty($conf->$module->dir_temp)) {
13390 $dir_temp = $conf->$module->dir_temp;
13391 }
13392 }
13393
13394 // Overwrite value for special cases
13395 if ($element == 'order_supplier') {
13396 $dir_output = $conf->fournisseur->commande->dir_output;
13397 $dir_temp = $conf->fournisseur->commande->dir_temp;
13398 } elseif ($element == 'invoice_supplier') {
13399 $dir_output = $conf->fournisseur->facture->dir_output;
13400 $dir_temp = $conf->fournisseur->facture->dir_temp;
13401 }
13402 $dir_output .= $subdir;
13403 $dir_temp .= $subdir;
13404
13405 $elementProperties = array(
13406 'module' => $module,
13407 'element' => $element,
13408 'table_element' => $table_element,
13409 'subelement' => $subelement,
13410 'classpath' => $classpath,
13411 'classfile' => $classfile,
13412 'classname' => $classname,
13413 'dir_output' => $dir_output,
13414 'dir_temp' => $dir_temp,
13415 'parent_element' => $parent_element,
13416 );
13417
13418
13419 // Add hook
13420 if (!is_object($hookmanager)) {
13421 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13422 $hookmanager = new HookManager($db);
13423 }
13424 $hookmanager->initHooks(array('elementproperties'));
13425
13426
13427 // Hook params
13428 $parameters = array(
13429 'elementType' => $elementType,
13430 'elementProperties' => $elementProperties
13431 );
13432
13433 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13434
13435 if ($reshook) {
13436 $elementProperties = $hookmanager->resArray;
13437 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13438 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13439 }
13440
13441 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13442 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13443 unset($hookmanager->contextarray[$key]);
13444 }
13445
13446 return $elementProperties;
13447}
13448
13461function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13462{
13463 global $db, $conf;
13464
13465 $ret = 0;
13466
13467 $element_prop = getElementProperties($element_type);
13468
13469 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13470 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13471 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13472 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13473 // of service and we will return properties of a product.
13474 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13475 } elseif ($element_prop['module'] == 'societeaccount') {
13476 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13477 } else {
13478 $ismodenabled = isModEnabled($element_prop['module']);
13479 }
13480 //var_dump('element_type='.$element_type);
13481 //var_dump($element_prop);
13482 //var_dump($element_prop['module'].' '.$ismodenabled);
13483 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13484 if ($useCache === 1
13485 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13486 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13487 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13488 ) {
13489 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13490 }
13491
13492 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13493
13494 if (class_exists($element_prop['classname'])) {
13495 $className = $element_prop['classname'];
13496 $objecttmp = new $className($db);
13497 '@phan-var-force CommonObject $objecttmp';
13498
13499 if ($element_id > 0 || !empty($element_ref)) {
13500 $ret = $objecttmp->fetch($element_id, $element_ref);
13501 if ($ret >= 0) {
13502 if (empty($objecttmp->module)) {
13503 $objecttmp->module = $element_prop['module'];
13504 }
13505
13506 if ($useCache > 0) {
13507 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13508 $conf->cache['fetchObjectByElement'][$element_type] = [];
13509 }
13510
13511 // Manage cache limit
13512 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13513 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13514 }
13515
13516 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13517 }
13518
13519 return $objecttmp;
13520 }
13521 } else {
13522 return $objecttmp; // returned an object without fetch
13523 }
13524 } else {
13525 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13526 return -1;
13527 }
13528 }
13529
13530 return $ret;
13531}
13532
13539function isAFileWithExecutableContent($filename)
13540{
13541 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)) {
13542 return true;
13543 }
13544
13545 return false;
13546}
13547
13555function newToken()
13556{
13557 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13558}
13559
13567function currentToken()
13568{
13569 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13570}
13571
13577function getNonce()
13578{
13579 global $conf;
13580
13581 if (empty($conf->cache['nonce'])) {
13582 $conf->cache['nonce'] = dolGetRandomBytes(8);
13583 }
13584
13585 return $conf->cache['nonce'];
13586}
13587
13588
13602function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13603{
13604 global $langs;
13605
13606 print '<div class="div-table-responsive-no-min">';
13607 print '<table class="noborder centpercent">';
13608 print '<tr class="liste_titre">';
13609
13610 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13611
13612 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13613
13614 if (!empty($link)) {
13615 if (!empty($arguments)) {
13616 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13617 } else {
13618 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13619 }
13620 }
13621
13622 if ($number > -1) {
13623 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13624 } elseif (!empty($link)) {
13625 print '<span class="badge marginleftonlyshort">...</span>';
13626 }
13627
13628 if (!empty($link)) {
13629 print '</a>';
13630 }
13631
13632 print '</th>';
13633
13634 if ($number < 0 && !empty($link)) {
13635 print '<th class="right">';
13636 print '</th>';
13637 }
13638
13639 print '</tr>';
13640}
13641
13650function finishSimpleTable($addLineBreak = false)
13651{
13652 print '</table>';
13653 print '</div>';
13654
13655 if ($addLineBreak) {
13656 print '<br>';
13657 }
13658}
13659
13671function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13672{
13673 global $langs;
13674
13675 if ($num === 0) {
13676 print '<tr class="oddeven">';
13677 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13678 print '</tr>';
13679 return;
13680 }
13681
13682 if ($nbofloop === 0) {
13683 // don't show a summary line
13684 return;
13685 }
13686
13687 /* Case already handled above, commented to satisfy phpstan.
13688 if ($num === 0) {
13689 $colspan = $tableColumnCount;
13690 } else
13691 */
13692 if ($num > $nbofloop) {
13693 $colspan = $tableColumnCount;
13694 } else {
13695 $colspan = $tableColumnCount - 1;
13696 }
13697
13698 if ($extraRightColumn) {
13699 $colspan--;
13700 }
13701
13702 print '<tr class="liste_total">';
13703
13704 if ($nbofloop > 0 && $num > $nbofloop) {
13705 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13706 } else {
13707 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13708 print '<td class="right centpercent">'.price($total).'</td>';
13709 }
13710
13711 if ($extraRightColumn) {
13712 print '<td></td>';
13713 }
13714
13715 print '</tr>';
13716}
13717
13726function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13727{
13728 if ($method == -1) {
13729 $method = 0;
13730 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13731 $method = 1;
13732 }
13733 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13734 $method = 2;
13735 }
13736 }
13737
13738 // Be sure we don't have output buffering enabled to have readfile working correctly
13739 while (ob_get_level()) {
13740 ob_end_flush();
13741 }
13742
13743 // Solution 0
13744 if ($method == 0) {
13745 readfile($fullpath_original_file_osencoded);
13746 } elseif ($method == 1) {
13747 // Solution 1
13748 $handle = fopen($fullpath_original_file_osencoded, "rb");
13749 while (!feof($handle)) {
13750 print fread($handle, 8192);
13751 }
13752 fclose($handle);
13753 } elseif ($method == 2) {
13754 // Solution 2
13755 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13756 $handle2 = fopen("php://output", "wb");
13757 stream_copy_to_stream($handle1, $handle2);
13758 fclose($handle1);
13759 fclose($handle2);
13760 }
13761}
13762
13772function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13773{
13774 global $langs;
13775
13776 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13777
13778 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
13779 if ($texttoshow === 'none') {
13780 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13781 $result .= '<span class="clipboardCPValueToPrint"></span>';
13782 } elseif ($texttoshow) {
13783 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13784 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
13785 } else {
13786 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13787 }
13788 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
13789 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
13790 $result .= '<span class="clipboardCPText"></span>';
13791 $result .= '</span>';
13792
13793 return $result;
13794}
13795
13796
13803function jsonOrUnserialize($stringtodecode)
13804{
13805 $result = json_decode($stringtodecode);
13806 if ($result === null) {
13807 $result = unserialize($stringtodecode);
13808 }
13809
13810 return $result;
13811}
13812
13813
13830function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13831{
13832 global $db, $user;
13833
13834 if (is_null($filter) || !is_string($filter) || $filter === '') {
13835 return '';
13836 }
13837 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13838 $filter = '(' . $filter . ')';
13839 }
13840
13841 $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'
13842 $firstandlastparenthesis = 0;
13843
13844 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13845 if ($noerror) {
13846 return '1 = 2';
13847 } else {
13848 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13849 }
13850 }
13851
13852 // Test the filter syntax
13853 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13854 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
13855 // If the string result contains something else than '()', the syntax was wrong
13856
13857 if (preg_match('/[^\‍(\‍)]/', $t)) {
13858 $tmperrorstr = 'Bad syntax of the search string';
13859 $errorstr = 'Bad syntax of the search string: '.$filter;
13860 if ($noerror) {
13861 return '1 = 2';
13862 } else {
13863 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
13864 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13865 }
13866 }
13867
13868 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
13869
13870 if (is_object($db)) {
13871 $ret = str_replace('__NOW__', $db->idate(dol_now()), $ret);
13872 }
13873 if (is_object($user)) {
13874 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13875 }
13876
13877 return $ret;
13878}
13879
13887function dolForgeExplodeAnd($sqlfilters)
13888{
13889 $arrayofandtags = array();
13890 $nbofchars = dol_strlen($sqlfilters);
13891
13892 $error = '';
13893 $parenthesislevel = 0;
13894 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13895 if (!$result) {
13896 return array();
13897 }
13898 if ($parenthesislevel >= 1) {
13899 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13900 }
13901
13902 $i = 0;
13903 $s = '';
13904 $countparenthesis = 0;
13905 while ($i < $nbofchars) {
13906 $char = dol_substr($sqlfilters, $i, 1);
13907
13908 if ($char == '(') {
13909 $countparenthesis++;
13910 } elseif ($char == ')') {
13911 $countparenthesis--;
13912 }
13913
13914 if ($countparenthesis == 0) {
13915 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13916 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13917 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13918 // We found a AND
13919 $s = trim($s);
13920 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13921 $s = '('.$s.')';
13922 }
13923 $arrayofandtags[] = $s;
13924 $s = '';
13925 $i += 2;
13926 } else {
13927 $s .= $char;
13928 }
13929 } else {
13930 $s .= $char;
13931 }
13932 $i++;
13933 }
13934 if ($s) {
13935 $s = trim($s);
13936 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13937 $s = '('.$s.')';
13938 }
13939 $arrayofandtags[] = $s;
13940 }
13941
13942 return $arrayofandtags;
13943}
13944
13954function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13955{
13956 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13957 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13958 $tmp = $sqlfilters;
13959
13960 $nb = dol_strlen($tmp);
13961 $counter = 0;
13962 $parenthesislevel = 0;
13963
13964 $error = '';
13965
13966 $i = 0;
13967 while ($i < $nb) {
13968 $char = dol_substr($tmp, $i, 1);
13969
13970 if ($char == '(') {
13971 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13972 // We open a parenthesis and it is the first char
13973 $parenthesislevel++;
13974 }
13975 $counter++;
13976 } elseif ($char == ')') {
13977 $nbcharremaining = ($nb - $i - 1);
13978 if ($nbcharremaining >= $counter) {
13979 $parenthesislevel = min($parenthesislevel, $counter - 1);
13980 }
13981 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13982 $parenthesislevel = $counter;
13983 }
13984 $counter--;
13985 }
13986
13987 if ($counter < 0) {
13988 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13989 $parenthesislevel = 0;
13990 dol_syslog($error, LOG_WARNING);
13991 return false;
13992 }
13993
13994 $i++;
13995 }
13996
13997 if ($counter > 0) {
13998 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13999 $parenthesislevel = 0;
14000 dol_syslog($error, LOG_WARNING);
14001 return false;
14002 }
14003
14004 return true;
14005}
14006
14014function dolForgeDummyCriteriaCallback($matches)
14015{
14016 //dol_syslog("Convert matches ".$matches[1]);
14017 if (empty($matches[1])) {
14018 return '';
14019 }
14020 $tmp = explode(':', $matches[1]);
14021 if (count($tmp) < 3) {
14022 return '';
14023 }
14024
14025 return '()'; // An empty criteria
14026}
14027
14036function dolForgeSQLCriteriaCallback($matches)
14037{
14038 global $db;
14039
14040 //dol_syslog("Convert matches ".$matches[1]);
14041 if (empty($matches[1])) {
14042 return '';
14043 }
14044 $tmp = explode(':', $matches[1], 3);
14045 if (count($tmp) < 3) {
14046 return '';
14047 }
14048
14049 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
14050
14051 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
14052
14053 $realOperator = [
14054 'NOTLIKE' => 'NOT LIKE',
14055 'ISNOT' => 'IS NOT',
14056 'NOTIN' => 'NOT IN',
14057 '!=' => '<>',
14058 ];
14059
14060 if (array_key_exists($operator, $realOperator)) {
14061 $operator = $realOperator[$operator];
14062 }
14063
14064 $tmpescaped = $tmp[2];
14065
14066 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
14067
14068 $regbis = array();
14069
14070 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
14071 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
14072 $tmpescaped2 = '(';
14073 // Explode and sanitize each element in list
14074 $tmpelemarray = explode(',', $tmpescaped);
14075 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
14076 $reg = array();
14077 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
14078 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
14079 } else {
14080 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
14081 }
14082 }
14083 $tmpescaped2 .= implode(',', $tmpelemarray);
14084 $tmpescaped2 .= ')';
14085
14086 $tmpescaped = $tmpescaped2;
14087 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14088 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14089 $tmpescaped = $regbis[1];
14090 }
14091 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14092 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14093 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14094 // TODO Retrieve type of field for $operand field name.
14095 // So we can complete format. For example we could complete a year with month and day.
14096 $tmpescaped = "'".$db->escape($regbis[1])."'";
14097 } else {
14098 if (strtoupper($tmpescaped) == 'NULL') {
14099 $tmpescaped = 'NULL';
14100 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
14101 $tmpescaped = (int) $tmpescaped;
14102 } else {
14103 $tmpescaped = (float) $tmpescaped;
14104 }
14105 }
14106
14107 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14108}
14109
14110
14120function getTimelineIcon($actionstatic, &$histo, $key)
14121{
14122 global $langs;
14123
14124 $out = '<!-- timeline icon -->'."\n";
14125 $iconClass = 'fa fa-comments';
14126 $img_picto = '';
14127 $colorClass = '';
14128 $pictoTitle = '';
14129
14130 if ($histo[$key]['percent'] == -1) {
14131 $colorClass = 'timeline-icon-not-applicble';
14132 $pictoTitle = $langs->trans('StatusNotApplicable');
14133 } elseif ($histo[$key]['percent'] == 0) {
14134 $colorClass = 'timeline-icon-todo';
14135 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14136 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14137 $colorClass = 'timeline-icon-in-progress';
14138 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14139 } elseif ($histo[$key]['percent'] >= 100) {
14140 $colorClass = 'timeline-icon-done';
14141 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14142 }
14143
14144 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14145 $iconClass = 'fa fa-ticket';
14146 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14147 $iconClass = 'fa fa-pencilxxx';
14148 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14149 $iconClass = 'fa fa-comments';
14150 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14151 $iconClass = 'fa fa-mask';
14152 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14153 if ($actionstatic->type_picto) {
14154 $img_picto = img_picto('', $actionstatic->type_picto);
14155 } else {
14156 if ($actionstatic->type_code == 'AC_RDV') {
14157 $iconClass = 'fa fa-handshake';
14158 } elseif ($actionstatic->type_code == 'AC_TEL') {
14159 $iconClass = 'fa fa-phone';
14160 } elseif ($actionstatic->type_code == 'AC_FAX') {
14161 $iconClass = 'fa fa-fax';
14162 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14163 $iconClass = 'fa fa-envelope';
14164 } elseif ($actionstatic->type_code == 'AC_INT') {
14165 $iconClass = 'fa fa-shipping-fast';
14166 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14167 $iconClass = 'fa fa-robot';
14168 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14169 $iconClass = 'fa fa-robot';
14170 }
14171 }
14172 }
14173
14174 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14175 return $out;
14176}
14177
14185{
14186 global $conf, $db;
14187
14188 $documents = array();
14189
14190 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
14191 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14192 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14193 //$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
14194 $sql .= ' ORDER BY ecm.position ASC';
14195
14196 $resql = $db->query($sql);
14197 if ($resql) {
14198 if ($db->num_rows($resql)) {
14199 while ($obj = $db->fetch_object($resql)) {
14200 $documents[$obj->id] = $obj;
14201 }
14202 }
14203 }
14204
14205 return $documents;
14206}
14207
14208
14226function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
14227{
14228 global $user, $conf;
14229 global $form;
14230
14231 global $param, $massactionbutton;
14232
14233 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
14234
14235 // Check parameters
14236 if (!is_object($filterobj) && !is_object($objcon)) {
14237 dol_print_error(null, 'BadParameter');
14238 }
14239
14240 $histo = array();
14241 '@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';
14242
14243 $numaction = 0;
14244 $now = dol_now();
14245
14246 $sortfield_list = explode(',', $sortfield);
14247 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
14248 $sortfield_new_list = array();
14249 foreach ($sortfield_list as $sortfield_value) {
14250 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
14251 }
14252 $sortfield_new = implode(',', $sortfield_new_list);
14253
14254 $sql = null;
14255 $sql2 = null;
14256
14257 if (isModEnabled('agenda')) {
14258 // Search histo on actioncomm
14259 if (is_object($objcon) && $objcon->id > 0) {
14260 $sql = "SELECT DISTINCT a.id, a.label as label,";
14261 } else {
14262 $sql = "SELECT a.id, a.label as label,";
14263 }
14264 $sql .= " a.datep as dp,";
14265 $sql .= " a.note as message,";
14266 $sql .= " a.datep2 as dp2,";
14267 $sql .= " a.percent as percent, 'action' as type,";
14268 $sql .= " a.fk_element, a.elementtype,";
14269 $sql .= " a.fk_contact,";
14270 $sql .= " a.email_from as msg_from,";
14271 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
14272 $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";
14273 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14274 $sql .= ", sp.lastname, sp.firstname";
14275 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14276 $sql .= ", m.lastname, m.firstname";
14277 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
14278 $sql .= ", o.ref";
14279 }
14280 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
14281 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
14282 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
14283
14284 $force_filter_contact = $filterobj instanceof User;
14285
14286 if (is_object($objcon) && $objcon->id > 0) {
14287 $force_filter_contact = true;
14288 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
14289 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
14290 }
14291
14292 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
14293 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
14294 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
14295 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
14296 $sql .= " ON er.resource_type = 'dolresource'";
14297 $sql .= " AND er.element_id = a.id";
14298 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
14299 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14300 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
14301 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14302 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
14303 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14304 $sql .= ", ".MAIN_DB_PREFIX."product as o";
14305 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14306 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
14307 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14308 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
14309 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14310 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
14311 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14312 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
14313 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14314 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
14315 }
14316
14317 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
14318 if (!$force_filter_contact) {
14319 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
14320 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
14321 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
14322 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
14323 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14324 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
14325 if ($filterobj->id) {
14326 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14327 }
14328 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
14329 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
14330 if ($filterobj->id) {
14331 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14332 }
14333 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14334 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
14335 if ($filterobj->id) {
14336 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14337 }
14338 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14339 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
14340 if ($filterobj->id) {
14341 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14342 }
14343 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14344 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
14345 if ($filterobj->id) {
14346 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14347 }
14348 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14349 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
14350 if ($filterobj->id) {
14351 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14352 }
14353 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14354 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
14355 if ($filterobj->id) {
14356 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14357 }
14358 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
14359 $sql .= " AND a.fk_contact = sp.rowid";
14360 if ($filterobj->id) {
14361 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
14362 }
14363 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14364 $sql .= " AND a.fk_element = o.rowid";
14365 if ($filterobj->id) {
14366 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
14367 }
14368 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14369 $sql .= " AND a.fk_element = o.rowid";
14370 if ($filterobj->id) {
14371 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
14372 }
14373 }
14374 } else {
14375 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
14376 }
14377
14378 // Condition on actioncode
14379 if (!empty($actioncode) && $actioncode != '-1') {
14380 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14381 if ($actioncode == 'AC_NON_AUTO') {
14382 $sql .= " AND c.type != 'systemauto'";
14383 } elseif ($actioncode == 'AC_ALL_AUTO') {
14384 $sql .= " AND c.type = 'systemauto'";
14385 } else {
14386 if ($actioncode == 'AC_OTH') {
14387 $sql .= " AND c.type != 'systemauto'";
14388 } elseif ($actioncode == 'AC_OTH_AUTO') {
14389 $sql .= " AND c.type = 'systemauto'";
14390 }
14391 }
14392 } else {
14393 if ($actioncode == 'AC_NON_AUTO') {
14394 $sql .= " AND c.type != 'systemauto'";
14395 } elseif ($actioncode == 'AC_ALL_AUTO') {
14396 $sql .= " AND c.type = 'systemauto'";
14397 } else {
14398 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14399 }
14400 }
14401 }
14402 if ($donetodo == 'todo') {
14403 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14404 } elseif ($donetodo == 'done') {
14405 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14406 }
14407 if (is_array($filters) && $filters['search_agenda_label']) {
14408 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14409 }
14410 }
14411
14412 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14413 if (isModEnabled('mailing') && !empty($objcon->email)
14414 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14415 $langs->load("mails");
14416
14417 $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";
14418 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14419 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14420 $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
14421 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14422 $sql2 .= ", '' as lastname, '' as firstname";
14423 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14424 $sql2 .= ", '' as lastname, '' as firstname";
14425 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14426 $sql2 .= ", '' as ref";
14427 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14428 $sql2 .= ", '' as ref";
14429 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14430 $sql2 .= ", '' as ref";
14431 }
14432 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14433 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14434 $sql2 .= " AND mc.statut = 1";
14435 $sql2 .= " AND u.rowid = m.fk_user_valid";
14436 $sql2 .= " AND mc.fk_mailing=m.rowid";
14437 }
14438
14439 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14440 if (!empty($sql) && !empty($sql2)) {
14441 $sql = $sql." UNION ".$sql2;
14442 } elseif (empty($sql) && !empty($sql2)) {
14443 $sql = $sql2;
14444 }
14445
14446 //TODO Add navigation with this limits...
14447 $offset = 0;
14448 $limit = 1000;
14449
14450 // Complete request and execute it with limit
14451 $sql .= $db->order($sortfield_new, $sortorder);
14452 if ($limit) {
14453 $sql .= $db->plimit($limit + 1, $offset);
14454 }
14455
14456 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14457 $resql = $db->query($sql);
14458 if ($resql) {
14459 $i = 0;
14460 $num = $db->num_rows($resql);
14461
14462 $imaxinloop = ($limit ? min($num, $limit) : $num);
14463 while ($i < $imaxinloop) {
14464 $obj = $db->fetch_object($resql);
14465
14466 if ($obj->type == 'action') {
14467 $contactaction = new ActionComm($db);
14468 $contactaction->id = $obj->id;
14469 $result = $contactaction->fetchResources();
14470 if ($result < 0) {
14471 dol_print_error($db);
14472 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14473 }
14474
14475 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14476 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14477 $tododone = '';
14478 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14479 $tododone = 'todo';
14480 }
14481
14482 $histo[$numaction] = array(
14483 'type' => $obj->type,
14484 'tododone' => $tododone,
14485 'id' => $obj->id,
14486 'datestart' => $db->jdate($obj->dp),
14487 'dateend' => $db->jdate($obj->dp2),
14488 'note' => $obj->label,
14489 'message' => dol_htmlentitiesbr($obj->message),
14490 'percent' => $obj->percent,
14491
14492 'userid' => $obj->user_id,
14493 'login' => $obj->user_login,
14494 'userfirstname' => $obj->user_firstname,
14495 'userlastname' => $obj->user_lastname,
14496 'userphoto' => $obj->user_photo,
14497 'msg_from' => $obj->msg_from,
14498
14499 'contact_id' => $obj->fk_contact,
14500 'socpeopleassigned' => $contactaction->socpeopleassigned,
14501 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14502 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14503 'fk_element' => $obj->fk_element,
14504 'elementtype' => $obj->elementtype,
14505 // Type of event
14506 'acode' => $obj->acode,
14507 'alabel' => $obj->alabel,
14508 'libelle' => $obj->alabel, // deprecated
14509 'apicto' => $obj->apicto
14510 );
14511 } else {
14512 $histo[$numaction] = array(
14513 'type' => $obj->type,
14514 'tododone' => 'done',
14515 'id' => $obj->id,
14516 'datestart' => $db->jdate($obj->dp),
14517 'dateend' => $db->jdate($obj->dp2),
14518 'note' => $obj->label,
14519 'message' => dol_htmlentitiesbr($obj->message),
14520 'percent' => $obj->percent,
14521 'acode' => $obj->acode,
14522
14523 'userid' => $obj->user_id,
14524 'login' => $obj->user_login,
14525 'userfirstname' => $obj->user_firstname,
14526 'userlastname' => $obj->user_lastname,
14527 'userphoto' => $obj->user_photo
14528 );
14529 }
14530
14531 $numaction++;
14532 $i++;
14533 }
14534 } else {
14535 dol_print_error($db);
14536 }
14537 }
14538
14539 // Set $out to show events
14540 $out = '';
14541
14542 if (!isModEnabled('agenda')) {
14543 $langs->loadLangs(array("admin", "errors"));
14544 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14545 }
14546
14547 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14548 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
14549
14550 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14551 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14552 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14553 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14554
14555 $formactions = new FormActions($db);
14556
14557 $actionstatic = new ActionComm($db);
14558 $userstatic = new User($db);
14559 $contactstatic = new Contact($db);
14560 $userGetNomUrlCache = array();
14561 $contactGetNomUrlCache = array();
14562
14563 $out .= '<div class="filters-container" >';
14564 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14565 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14566
14567 if ($objcon && get_class($objcon) == 'Contact' &&
14568 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14569 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14570 } else {
14571 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14572 }
14573 if (($filterobj && get_class($filterobj) == 'Societe')) {
14574 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14575 } else {
14576 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14577 }
14578
14579 $out .= "\n";
14580
14581 $out .= '<div class="div-table-responsive-no-min">';
14582 $out .= '<table class="noborder borderbottom centpercent">';
14583
14584 $out .= '<tr class="liste_titre">';
14585
14586 // Action column
14587 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14588 $out .= '<th class="liste_titre width50 middle">';
14589 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14590 $out .= $searchpicto;
14591 $out .= '</th>';
14592 }
14593
14594 // Date
14595 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
14596
14597 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14598 if ($donetodo) {
14599 $out .= '<th class="liste_titre"></th>';
14600 }
14601 // Type of event
14602 $out .= '<th class="liste_titre">';
14603 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14604 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
14605 $out .= '</th>';
14606 // Label
14607 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14608 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14609 $out .= '</th>';
14610
14611 // Action column
14612 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14613 $out .= '<th class="liste_titre width50 middle">';
14614 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14615 $out .= $searchpicto;
14616 $out .= '</th>';
14617 }
14618
14619 $out .= '</tr>';
14620
14621 $out .= '</table>';
14622
14623 $out .= '</form>';
14624 $out .= '</div>';
14625
14626 $out .= "\n";
14627
14628 $out .= '<ul class="timeline">';
14629
14630 if ($donetodo) {
14631 $tmp = '';
14632 if ($filterobj instanceof Societe) {
14633 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14634 }
14635 if ($filterobj instanceof User) {
14636 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14637 }
14638 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14639 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14640 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14641 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14642 if ($filterobj instanceof Societe) {
14643 $tmp .= '</a>';
14644 }
14645 if ($filterobj instanceof User) {
14646 $tmp .= '</a>';
14647 }
14648 $out .= getTitleFieldOfList($tmp);
14649 }
14650
14651 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14652 $caction = new CActionComm($db);
14653 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14654
14655 $actualCycleDate = false;
14656
14657 // Loop on each event to show it
14658 foreach ($histo as $key => $value) {
14659 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14660
14661 $actionstatic->type_picto = $histo[$key]['apicto'];
14662 $actionstatic->type_code = $histo[$key]['acode'];
14663
14664 $labeltype = $actionstatic->type_code;
14665 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14666 $labeltype = 'AC_OTH';
14667 }
14668 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14669 $labeltype = $langs->trans("Message");
14670 } else {
14671 if (!empty($arraylist[$labeltype])) {
14672 $labeltype = $arraylist[$labeltype];
14673 }
14674 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14675 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14676 }
14677 }
14678
14679 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14680
14681 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14682
14683 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14684 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14685 $out .= '<!-- timeline time label -->';
14686 $out .= '<li class="time-label">';
14687 $out .= '<span class="timeline-badge-date">';
14688 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14689 $out .= '</span>';
14690 $out .= '</li>';
14691 $out .= '<!-- /.timeline-label -->';
14692 }
14693
14694
14695 $out .= '<!-- timeline item -->'."\n";
14696 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
14697
14698 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14699 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14700 //$out .= $timelineicon;
14701 //var_dump($timelineicon);
14702 $out .= $typeicon;
14703
14704 $out .= '<div class="timeline-item">'."\n";
14705
14706 $out .= '<span class="time timeline-header-action2">';
14707
14708 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14709 $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").' ';
14710 $out .= $histo[$key]['id'];
14711 $out .= '</a> ';
14712 } else {
14713 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14714 }
14715
14716 if ($user->hasRight('agenda', 'allactions', 'create') ||
14717 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14718 $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).'">';
14719 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14720 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14721 $out .= '</a>';
14722 }
14723
14724 $out .= '</span>';
14725
14726 // Date
14727 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14728 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14729 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14730 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14731 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14732 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14733 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14734 } else {
14735 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14736 }
14737 }
14738 $late = 0;
14739 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14740 $late = 1;
14741 }
14742 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14743 $late = 1;
14744 }
14745 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14746 $late = 1;
14747 }
14748 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14749 $late = 1;
14750 }
14751 if ($late) {
14752 $out .= img_warning($langs->trans("Late")).' ';
14753 }
14754 $out .= "</span></span>\n";
14755
14756 // Ref
14757 $out .= '<h3 class="timeline-header">';
14758
14759 // Author of event
14760 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14761 if ($histo[$key]['userid'] > 0) {
14762 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14763 $userstatic->fetch($histo[$key]['userid']);
14764 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14765 }
14766 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14767 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14768 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14769 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14770 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14771 } else {
14772 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14773 }
14774 }
14775 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14776 }
14777 $out .= '</div>';
14778
14779 // Title
14780 $out .= ' <div class="messaging-title inline-block">';
14781 //$out .= $actionstatic->getTypePicto();
14782 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14783 $out .= $labeltype.' - ';
14784 }
14785
14786 $libelle = '';
14787 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14788 $out .= $langs->trans('TicketNewMessage');
14789 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14790 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14791 } elseif (isset($histo[$key]['type'])) {
14792 if ($histo[$key]['type'] == 'action') {
14793 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14794 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14795 $libelle = $histo[$key]['note'];
14796 $actionstatic->id = $histo[$key]['id'];
14797 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14798 } elseif ($histo[$key]['type'] == 'mailing') {
14799 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14800 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14801 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14802 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14803 } else {
14804 $libelle .= $histo[$key]['note'];
14805 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14806 }
14807 }
14808
14809 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14810 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14811 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14812 } else {
14813 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14814 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14815 }
14816 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14817 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14818 }
14819 if ($link) {
14820 $out .= ' - '.$link;
14821 }
14822 }
14823
14824 $out .= '</div>';
14825
14826 $out .= '</h3>';
14827
14828 // Message
14829 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14830 && $actionstatic->code != 'AC_TICKET_CREATE'
14831 && $actionstatic->code != 'AC_TICKET_MODIFY'
14832 ) {
14833 $out .= '<div class="timeline-body wordbreak small">';
14834 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14835 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14836 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14837 $out .= '<div class="readmore-block --closed" >';
14838 $out .= ' <div class="readmore-block__excerpt">';
14839 $out .= dolPrintHTML($truncatedText);
14840 $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>';
14841 $out .= ' </div>';
14842 $out .= ' <div class="readmore-block__full-text" >';
14843 $out .= dolPrintHTML($histo[$key]['message']);
14844 $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>';
14845 $out .= ' </div>';
14846 $out .= '</div>';
14847 } else {
14848 $out .= dolPrintHTML($histo[$key]['message']);
14849 }
14850
14851 $out .= '</div>';
14852 }
14853
14854 // Timeline footer
14855 $footer = '';
14856
14857 // Contact for this action
14858 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14859 $contactList = '';
14860 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14861 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14862 $contact = new Contact($db);
14863 $contact->fetch($cid);
14864 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14865 } else {
14866 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14867 }
14868
14869 if ($contact) {
14870 $contactList .= !empty($contactList) ? ', ' : '';
14871 $contactList .= $contact->getNomUrl(1);
14872 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14873 if (!empty($contact->phone_pro)) {
14874 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14875 }
14876 }
14877 }
14878 }
14879
14880 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14881 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14882 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14883 $contact = new Contact($db);
14884 $result = $contact->fetch($histo[$key]['contact_id']);
14885 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14886 } else {
14887 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14888 $result = ($contact instanceof Contact) ? $contact->id : 0;
14889 }
14890
14891 if ($result > 0) {
14892 $footer .= $contact->getNomUrl(1);
14893 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14894 if (!empty($contact->phone_pro)) {
14895 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14896 }
14897 }
14898 }
14899 }
14900
14901 $documents = getActionCommEcmList($actionstatic);
14902 if (!empty($documents)) {
14903 $footer .= '<div class="timeline-documents-container">';
14904 foreach ($documents as $doc) {
14905 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14906 $footer .= ' data-id="'.$doc->id.'" ';
14907 $footer .= ' data-path="'.$doc->filepath.'"';
14908 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14909 $footer .= '>';
14910
14911 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14912 $mime = dol_mimetype($filePath);
14913 $file = $actionstatic->id.'/'.$doc->filename;
14914 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14915 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14916 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14917
14918 $mimeAttr = ' mime="'.$mime.'" ';
14919 $class = '';
14920 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14921 $class .= ' documentpreview';
14922 }
14923
14924 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14925 $footer .= img_mime($filePath).' '.$doc->filename;
14926 $footer .= '</a>';
14927
14928 $footer .= '</span>';
14929 }
14930 $footer .= '</div>';
14931 }
14932
14933 if (!empty($footer)) {
14934 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14935 }
14936
14937 $out .= '</div>'."\n"; // end timeline-item
14938
14939 $out .= '</li>';
14940 $out .= '<!-- END timeline item -->';
14941 }
14942
14943 $out .= "</ul>\n";
14944
14945 $out .= '<script>
14946 jQuery(document).ready(function () {
14947 $(document).on("click", "[data-read-more-action]", function(e){
14948 let readMoreBloc = $(this).closest(".readmore-block");
14949 if(readMoreBloc.length > 0){
14950 e.preventDefault();
14951 if($(this).attr("data-read-more-action") == "close"){
14952 readMoreBloc.addClass("--closed").removeClass("--open");
14953 $("html, body").animate({
14954 scrollTop: readMoreBloc.offset().top - 200
14955 }, 100);
14956 }else{
14957 readMoreBloc.addClass("--open").removeClass("--closed");
14958 }
14959 }
14960 });
14961 });
14962 </script>';
14963
14964
14965 if (empty($histo)) {
14966 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14967 }
14968 }
14969
14970 if ($noprint) {
14971 return $out;
14972 } else {
14973 print $out;
14974 return null;
14975 }
14976}
14977
14989function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14990{
14991 if ($timestamp === null) {
14992 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14993 }
14994 $TParam = array(
14995 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14996 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14997 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14998 );
14999 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
15000 $TParam = array_merge($TParam, array(
15001 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
15002 $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
15003 $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
15004 ));
15005 }
15006
15007 return '&' . http_build_query($TParam);
15008}
15009
15028function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
15029{
15030 global $conf, $db, $langs, $hookmanager;
15031 global $action, $object;
15032
15033 if (!is_object($langs)) {
15034 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
15035 $langs = new Translate('', $conf);
15036 $langs->setDefaultLang();
15037 }
15038
15039 $langs->load("errors");
15040
15041 if ($printheader) {
15042 if (function_exists("llxHeader")) {
15043 llxHeader('');
15044 } elseif (function_exists("llxHeaderVierge")) {
15045 llxHeaderVierge('');
15046 }
15047 }
15048
15049 print '<div class="error">';
15050 if (empty($message)) {
15051 print $langs->trans("ErrorRecordNotFound");
15052 } else {
15053 print $langs->trans($message);
15054 }
15055 print '</div>';
15056 print '<br>';
15057
15058 if (empty($showonlymessage)) {
15059 if (empty($hookmanager)) {
15060 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
15061 $hookmanager = new HookManager($db);
15062 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
15063 $hookmanager->initHooks(array('main'));
15064 }
15065
15066 $parameters = array('message' => $message, 'params' => $params);
15067 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
15068 print $hookmanager->resPrint;
15069 }
15070
15071 if ($printfooter && function_exists("llxFooter")) {
15072 llxFooter();
15073 }
15074 exit(0);
15075}
$id
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
if(!defined( 'NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined( 'NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) if(!defined( 'NOLOGIN')) if(!defined('NOCSRFCHECK')) if(!defined( 'NOIPCHECK')) llxHeaderVierge($title, $head="", $disablejs=0, $disablehead=0, $arrayofjs=[], $arrayofcss=[])
Header function.
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:459
ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array(), $morecss='', $htmlname='', $forcenojs=0, $moreparam='')
On/off button to change a property status of an object This uses the ajax service objectonoff....
Definition ajax.lib.php:750
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:87
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:71
$object ref
Definition info.php:89
Class to manage agenda events (actions)
Class to manage different types of events.
Class to manage contact/addresses.
Class to manage GeoIP conversion Usage: $geoip=new GeoIP('country',$datfile); $geoip->getCountryCodeF...
Class to manage standard extra fields.
Class to manage invoices.
Class to manage building of HTML components.
Class to manage generation of HTML components Only common components must be here.
Class to manage hooks.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
dol_get_prev_month($month, $year)
Return previous month.
Definition date.lib.php:519
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:504
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:86
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition date.lib.php:488
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:538
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_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dolPrintHTML($s, $allowiframe=0)
Return a string (that can be on several lines) ready to be output on a HTML page.
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
dolPrintHTMLForTextArea($s, $allowiframe=0)
Return a string ready to be output on input textarea.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Get formatted error messages to output (Used to show messages on html output).
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
roundUpToNextMultiple($n, $x=5)
Round to next multiple.
dol_user_country()
Return country code for current user.
dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes=null)
Clean a string from some undesirable HTML tags.
getMultidirTemp($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its temporary ...
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
picto_required()
Return picto saying a field is required.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_action($titlealt, $numaction, $picto='', $moreatt='')
Show logo action.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
GETPOSTDATE($prefix, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
checkVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dolGetBadge($label, $html='', $type='primary', $mode='', $url='', $params=array())
Function dolGetBadge.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dolPrintText($s)
Return a string label (possible on several lines and that should not contains any HTML) ready to be o...
utf8_valid($str)
Check if a string is in UTF8.
getPictoForType($key, $morecss='')
Return the picto for a data type.
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
Create a dialog with two buttons for export and overwrite of a website.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
get_localtax_by_third($local)
Get values of localtaxes (1 or 2) for company country for the common vat with the highest value.
dol_escape_php($stringtoescape, $stringforquotes=2)
Returns text escaped for inclusion into a php string, build with double quotes " or '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getElementProperties($elementType)
Get an array with properties of an element.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
dolPrintHTMLForAttribute($s)
Return a string ready to be output on an HTML attribute (alt, title, data-html, .....
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot=true)
An function to complete dropdown url in dolGetButtonAction.
dol_get_object_properties($obj, $properties=[])
Get properties for an object - including magic properties when requested.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
img_search($titlealt='default', $other='')
Show search logo.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
isValidPhone($phone)
Return true if phone number syntax is ok TODO Decide what to do with this.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
img_previous($titlealt='default', $moreatt='')
Show previous logo.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
getBrowserInfo($user_agent)
Return information about user browser.
dolGetFirstLetters($s, $nbofchar=1)
Return first letters of a strings.
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
getMultidirVersion($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its versioned ...
dol_sanitizeUrl($stringtoclean, $type=1)
Clean a string to use it as an URL (into a href or src attribute)
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
img_printer($titlealt="default", $other='')
Show printer logo.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
ascii_check($str)
Check if a string is in ASCII.
get_date_range($date_start, $date_end, $format='', $outputlangs=null, $withparenthesis=1)
Format output for start and end date.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
print_date_range($date_start, $date_end, $format='', $outputlangs=null)
Format output for start and end date.
getArrayOfSocialNetworks()
Get array of social network dictionary.
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
num2Alpha($n)
Return a numeric value into an Excel like column number.
dol_size($size, $type='')
Optimize a size for some browsers (phone, smarphone...)
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
img_pdf($titlealt='default', $size=3)
Show pdf logo.
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolPrintPassword($s)
Return a string ready to be output on an HTML attribute (alt, title, ...)
colorIsLight($stringcolor)
Return true if the color is light.
dol_escape_all($stringtoescape)
Returns text escaped for all protocols (so only alpha chars and numbers)
GETPOSTFLOAT($paramname, $rounding='')
Return the value of a $_GET or $_POST supervariable, converted into float.
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
dolForgeSQLCriteriaCallback($matches)
Function to forge a SQL criteria from a USF (Universal Filter Syntax) string.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
dol_shutdown()
Function called at end of web php process.
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl='')
Format address string.
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
dol_escape_uri($stringtoescape)
Returns text escaped by RFC 3986 for inclusion into a clicable link.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
fetchObjectByElement($element_id, $element_type, $element_ref='', $useCache=0, $maxCacheByType=10)
Fetch an object from its id and element_type Inclusion of classes is automatic.
print_titre($title)
Show a title.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
img_error($titlealt='default')
Show error logo.
getTimelineIcon($actionstatic, &$histo, $key)
Get timeline icon.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formatted messages to output (Used to show messages on html output).
dol_clone($object, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
buildParamDate($prefix, $timestamp=null, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_next($titlealt='default', $moreatt='')
Show next logo.
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_string_is_good_iso($s, $clean=0)
Check if a string is a correct iso string If not, it will not be considered as HTML encoded even if i...
getNonce()
Return a random string to be used as a nonce value for js.
isStringVarMatching($var, $regextext, $matchrule=1)
Check if a variable with name $var startx with $text.
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode='add', $filterorigmodule='')
Complete or removed entries into a head array (used to build tabs).
get_htmloutput_mesg($mesgstring='', $mesgarray=[], $style='ok', $keepembedded=0)
Get formatted messages to output (Used to show messages on html output).
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
Return localtax vat rate of a product in a particular country or default country vat if product is un...
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $selectlimitsuffix='', $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
dol_nboflines($s, $maxchar=0)
Return nb of lines of a clear text.
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js, dangerous content and external link.
jsonOrUnserialize($stringtodecode)
Decode an encode string.
addSummaryTableLine($tableColumnCount, $num, $nbofloop=0, $total=0, $noneWord="None", $extraRightColumn=false)
Add a summary line to the current open table ("None", "XMoreLines" or "Total xxx")
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
dol_escape_xml($stringtoescape)
Returns text escaped for inclusion into a XML string.
getActionCommEcmList($object)
getActionCommEcmList
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
img_edit_add($titlealt='default', $other='')
Show logo +.
print_fiche_titre($title, $mesg='', $picto='generic', $pictoisfullpath=0, $id='')
Show a title with picto.
get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice=0)
Return vat rate of a product in a particular country, or default country vat if product is unknown.
dolForgeDummyCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax string.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0, $allowphp=0)
Clean a string to keep only desirable HTML tags.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
getUserRemoteIP()
Return the IP of remote user.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_validElement($element)
Return if var element is ok.
dol_sanitizeKeyCode($str)
Clean a string to use it as a key or code.
img_credit_card($brand, $morecss=null)
Return image of a credit card according to its brand name.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
img_searchclear($titlealt='default', $other='')
Show search logo.
dolPrintLabel($s)
Return a string label (so on 1 line only and that should not contains any HTML) ready to be output on...
utf8_check($str)
Check if a string is in UTF8.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Print formatted error messages to output (Used to show messages on html output).
startSimpleTable($header, $link="", $arguments="", $emptyColumns=0, $number=-1, $pictofulllist='')
Start a table with headers and a optional clickable number (don't forget to use "finishSimpleTable()"...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
getFieldErrorIcon($fieldValidationErrorMsg)
get field error icon
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
img_edit_remove($titlealt='default', $other='')
Show logo -.
img_info($titlealt='default')
Show info logo.
getDoliDBInstance($type, $host, $user, $pass, $name, $port)
Return a DoliDB instance (database handler).
dol_sanitizeEmail($stringtoclean)
Clean a string to use it as an Email.
dol_nboflines_bis($text, $maxlinesize=0, $charset='UTF-8')
Return nb of lines of a formatted text with and (WARNING: string must not have mixed and br sep...
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
const MODULE_MAPPING
This mapping defines the conversion to the current internal names from the alternative allowed names ...
dolBECalculateStructuredCommunication($invoice_number, $invoice_type)
Calculate Structured Communication / BE Bank payment reference number.
dol_convertToWord($num, $langs, $currency='', $centimes=false)
Function to return a number into a text.
ui state ui widget content ui state ui widget header ui state a ui button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
div refaddress div address
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition inc.php:420
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition main.inc.php:86
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
dol_setcache($memoryid, $data, $expire=0, $filecache=0)
Save data into a memory area shared by all users, all sessions on server.
dol_getcache($memoryid, $filecache=0)
Read a memory area shared by all users, all sessions on server.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:149
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:152
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length for cryptographic purposes.