dolibarr 20.0.4
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2022 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
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 *
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 3 of the License, or
30 * (at your option) any later version.
31 *
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
36 *
37 * You should have received a copy of the GNU General Public License
38 * along with this program. If not, see <https://www.gnu.org/licenses/>.
39 * or see https://www.gnu.org/
40 */
41
48include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
49
50// Function for better PHP x compatibility
51if (!function_exists('utf8_encode')) {
59 function utf8_encode($elements)
60 {
61 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
62 }
63}
64
65if (!function_exists('utf8_decode')) {
73 function utf8_decode($elements)
74 {
75 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
76 }
77}
78if (!function_exists('str_starts_with')) {
87 function str_starts_with($haystack, $needle)
88 {
89 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
90 }
91}
92if (!function_exists('str_ends_with')) {
101 function str_ends_with($haystack, $needle)
102 {
103 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
104 }
105}
106if (!function_exists('str_contains')) {
115 function str_contains($haystack, $needle)
116 {
117 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
118 }
119}
120
121
133function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
134{
135 global $conf;
136
137 if (!is_object($object) && empty($module)) {
138 return null;
139 }
140 if (empty($module) && !empty($object->element)) {
141 $module = $object->element;
142 }
143
144 // Special case for backward compatibility
145 if ($module == 'fichinter') {
146 $module = 'ficheinter';
147 } elseif ($module == 'invoice_supplier') {
148 $module = 'supplier_invoice';
149 } elseif ($module == 'order_supplier') {
150 $module = 'supplier_order';
151 }
152
153 // Get the relative path of directory
154 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
155 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
156 $s = '';
157 if ($mode != 'outputrel') {
158 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
159 }
160 if ($forobject && $object->id > 0) {
161 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
162 }
163 return $s;
164 } else {
165 return 'error-diroutput-not-defined-for-this-object='.$module;
166 }
167 } elseif ($mode == 'temp') {
168 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
169 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
170 } else {
171 return 'error-dirtemp-not-defined-for-this-object='.$module;
172 }
173 } else {
174 return 'error-bad-value-for-mode';
175 }
176}
177
187function getMultidirTemp($object, $module = '', $forobject = 0)
188{
189 return getMultidirOutput($object, $module, $forobject, 'temp');
190}
191
201function getMultidirVersion($object, $module = '', $forobject = 0)
202{
203 return getMultidirOutput($object, $module, $forobject, 'version');
204}
205
206
215function getDolGlobalString($key, $default = '')
216{
217 global $conf;
218 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
219}
220
230function getDolGlobalInt($key, $default = 0)
231{
232 global $conf;
233 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
234}
235
245function getDolUserString($key, $default = '', $tmpuser = null)
246{
247 if (empty($tmpuser)) {
248 global $user;
249 $tmpuser = $user;
250 }
251
252 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
253}
254
263function getDolUserInt($key, $default = 0, $tmpuser = null)
264{
265 if (empty($tmpuser)) {
266 global $user;
267 $tmpuser = $user;
268 }
269
270 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key: $default);
271}
272
273
283define(
284 'MODULE_MAPPING',
285 array(
286 // Map deprecated names to new names
287 'adherent' => 'member', // Has new directory
288 'member_type' => 'adherent_type', // No directory, but file called adherent_type
289 'banque' => 'bank', // Has new directory
290 'contrat' => 'contract', // Has new directory
291 'entrepot' => 'stock', // Has new directory
292 'projet' => 'project', // Has new directory
293 'categorie' => 'category', // Has old directory
294 'commande' => 'order', // Has old directory
295 'expedition' => 'shipping', // Has old directory
296 'facture' => 'invoice', // Has old directory
297 'fichinter' => 'intervention', // Has old directory
298 'ficheinter' => 'intervention', // Backup for 'fichinter'
299 'propale' => 'propal', // Has old directory
300 'socpeople' => 'contact', // Has old directory
301 'fournisseur' => 'supplier', // Has old directory
302
303 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
304 'product_price' => 'productprice', // NO directory
305 'product_fournisseur_price' => 'productsupplierprice', // NO directory
306 )
307);
308
315function isModEnabled($module)
316{
317 global $conf;
318
319 // Fix old names (map to new names)
320 $arrayconv = MODULE_MAPPING;
321 $arrayconvbis = array_flip(MODULE_MAPPING);
322
323 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
324 // Special cases: both use the same module.
325 $arrayconv['supplier_order'] = 'fournisseur';
326 $arrayconv['supplier_invoice'] = 'fournisseur';
327 }
328 // Special case.
329 // @TODO Replace isModEnabled('delivery_note') with
330 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
331 if ($module == 'delivery_note') {
332 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
333 return false;
334 } else {
335 $module = 'shipping';
336 }
337 }
338
339 $module_alt = $module;
340 if (!empty($arrayconv[$module])) {
341 $module_alt = $arrayconv[$module];
342 }
343 $module_bis = $module;
344 if (!empty($arrayconvbis[$module])) {
345 $module_bis = $arrayconvbis[$module];
346 }
347
348 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
349 //return !empty($conf->$module->enabled);
350}
351
358function isDolTms($timestamp)
359{
360 if ($timestamp === '') {
361 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"], LOG_NOTICE);
362 return false;
363 }
364 if (is_null($timestamp) || !is_numeric($timestamp)) {
365 return false;
366 }
367
368 return true;
369}
370
382function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
383{
384 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
385
386 $class = 'DoliDB'.ucfirst($type);
387 $db = new $class($type, $host, $user, $pass, $name, $port);
388 return $db;
389}
390
408function getEntity($element, $shared = 1, $currentobject = null)
409{
410 global $conf, $mc, $hookmanager, $object, $action, $db;
411
412 if (!is_object($hookmanager)) {
413 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
414 $hookmanager = new HookManager($db);
415 }
416
417 // fix different element names (France to English)
418 switch ($element) {
419 case 'projet':
420 $element = 'project';
421 break;
422 case 'contrat':
423 $element = 'contract';
424 break; // "/contrat/class/contrat.class.php"
425 case 'order_supplier':
426 $element = 'supplier_order';
427 break; // "/fourn/class/fournisseur.commande.class.php"
428 case 'invoice_supplier':
429 $element = 'supplier_invoice';
430 break; // "/fourn/class/fournisseur.facture.class.php"
431 }
432
433 if (is_object($mc)) {
434 $out = $mc->getEntity($element, $shared, $currentobject);
435 } else {
436 $out = '';
437 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
438 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
439 $addzero[] = 'c_holiday_types';
440 }
441 if (in_array($element, $addzero)) {
442 $out .= '0,';
443 }
444 $out .= ((int) $conf->entity);
445 }
446
447 // Manipulate entities to query on the fly
448 $parameters = array(
449 'element' => $element,
450 'shared' => $shared,
451 'object' => $object,
452 'currentobject' => $currentobject,
453 'out' => $out
454 );
455 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
456
457 if (is_numeric($reshook)) {
458 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
459 $out .= ','.$hookmanager->resPrint; // add
460 } elseif ($reshook == 1) {
461 $out = $hookmanager->resPrint; // replace
462 }
463 }
464
465 return $out;
466}
467
474function setEntity($currentobject)
475{
476 global $conf, $mc;
477
478 if (is_object($mc) && method_exists($mc, 'setEntity')) {
479 return $mc->setEntity($currentobject);
480 } else {
481 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
482 }
483}
484
491function isASecretKey($keyname)
492{
493 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
494}
495
496
503function num2Alpha($n)
504{
505 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
506 $r = chr($n % 26 + 0x41) . $r;
507 }
508 return $r;
509}
510
511
528function getBrowserInfo($user_agent)
529{
530 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
531
532 $name = 'unknown';
533 $version = '';
534 $os = 'unknown';
535 $phone = '';
536
537 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
538
539 $detectmobile = new Mobile_Detect(null, $user_agent);
540 $tablet = $detectmobile->isTablet();
541
542 if ($detectmobile->isMobile()) {
543 $phone = 'unknown';
544
545 // If phone/smartphone, we set phone os name.
546 if ($detectmobile->is('AndroidOS')) {
547 $os = $phone = 'android';
548 } elseif ($detectmobile->is('BlackBerryOS')) {
549 $os = $phone = 'blackberry';
550 } elseif ($detectmobile->is('iOS')) {
551 $os = 'ios';
552 $phone = 'iphone';
553 } elseif ($detectmobile->is('PalmOS')) {
554 $os = $phone = 'palm';
555 } elseif ($detectmobile->is('SymbianOS')) {
556 $os = 'symbian';
557 } elseif ($detectmobile->is('webOS')) {
558 $os = 'webos';
559 } elseif ($detectmobile->is('MaemoOS')) {
560 $os = 'maemo';
561 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
562 $os = 'windows';
563 }
564 }
565
566 // OS
567 if (preg_match('/linux/i', $user_agent)) {
568 $os = 'linux';
569 } elseif (preg_match('/macintosh/i', $user_agent)) {
570 $os = 'macintosh';
571 } elseif (preg_match('/windows/i', $user_agent)) {
572 $os = 'windows';
573 }
574
575 // Name
576 $reg = array();
577 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
578 $name = 'firefox';
579 $version = empty($reg[2]) ? '' : $reg[2];
580 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
581 $name = 'edge';
582 $version = empty($reg[2]) ? '' : $reg[2];
583 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
584 $name = 'chrome';
585 $version = empty($reg[2]) ? '' : $reg[2];
586 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
587 // we can have 'chrome (Mozilla...) chrome x.y' in one string
588 $name = 'chrome';
589 } elseif (preg_match('/iceweasel/i', $user_agent)) {
590 $name = 'iceweasel';
591 } elseif (preg_match('/epiphany/i', $user_agent)) {
592 $name = 'epiphany';
593 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
594 $name = 'safari';
595 $version = empty($reg[2]) ? '' : $reg[2];
596 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
597 // Safari is often present in string for mobile but its not.
598 $name = 'opera';
599 $version = empty($reg[2]) ? '' : $reg[2];
600 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
601 $name = 'ie';
602 $version = end($reg);
603 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
604 // MS products at end
605 $name = 'ie';
606 $version = end($reg);
607 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
608 // MS products at end
609 $name = 'textbrowser';
610 $version = empty($reg[3]) ? '' : $reg[3];
611 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
612 // MS products at end
613 $name = 'textbrowser';
614 $version = empty($reg[1]) ? '' : $reg[1];
615 }
616
617 if ($tablet) {
618 $layout = 'tablet';
619 } elseif ($phone) {
620 $layout = 'phone';
621 } else {
622 $layout = 'classic';
623 }
624
625 return array(
626 'browsername' => $name,
627 'browserversion' => $version,
628 'browseros' => $os,
629 'browserua' => $user_agent,
630 'layout' => $layout, // tablet, phone, classic
631 'phone' => $phone, // deprecated
632 'tablet' => $tablet // deprecated
633 );
634}
635
641function dol_shutdown()
642{
643 global $db;
644 $disconnectdone = false;
645 $depth = 0;
646 if (is_object($db) && !empty($db->connected)) {
647 $depth = $db->transaction_opened;
648 $disconnectdone = $db->close();
649 }
650 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));
651}
652
662function GETPOSTISSET($paramname)
663{
664 $isset = false;
665
666 $relativepathstring = $_SERVER["PHP_SELF"];
667 // Clean $relativepathstring
668 if (constant('DOL_URL_ROOT')) {
669 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
670 }
671 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
672 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
673 //var_dump($relativepathstring);
674 //var_dump($user->default_values);
675
676 // Code for search criteria persistence.
677 // Retrieve values if restore_lastsearch_values
678 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
679 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
680 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
681 if (is_array($tmp)) {
682 foreach ($tmp as $key => $val) {
683 if ($key == $paramname) { // We are on the requested parameter
684 $isset = true;
685 break;
686 }
687 }
688 }
689 }
690 // If there is saved contextpage, limit, page or mode
691 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
692 $isset = true;
693 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
694 $isset = true;
695 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
696 $isset = true;
697 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
698 $isset = true;
699 }
700 } else {
701 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
702 }
703
704 return $isset;
705}
706
715function GETPOSTISARRAY($paramname, $method = 0)
716{
717 // for $method test need return the same $val as GETPOST
718 if (empty($method)) {
719 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
720 } elseif ($method == 1) {
721 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
722 } elseif ($method == 2) {
723 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
724 } elseif ($method == 3) {
725 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
726 } else {
727 $val = 'BadFirstParameterForGETPOST';
728 }
729
730 return is_array($val);
731}
732
762function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
763{
764 global $mysoc, $user, $conf;
765
766 if (empty($paramname)) { // Explicit test for null for phan.
767 return 'BadFirstParameterForGETPOST';
768 }
769 if (empty($check)) {
770 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);
771 // Enable this line to know who call the GETPOST with '' $check parameter.
772 //var_dump(debug_backtrace()[0]);
773 }
774
775 if (empty($method)) {
776 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
777 } elseif ($method == 1) {
778 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
779 } elseif ($method == 2) {
780 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
781 } elseif ($method == 3) {
782 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
783 } else {
784 return 'BadThirdParameterForGETPOST';
785 }
786
787 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
788
789 if (empty($method) || $method == 3 || $method == 4) {
790 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
791 // Clean $relativepathstring
792 if (constant('DOL_URL_ROOT')) {
793 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
794 }
795 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
796 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
797 //var_dump($relativepathstring);
798 //var_dump($user->default_values);
799
800 // Code for search criteria persistence.
801 // Retrieve saved values if restore_lastsearch_values is set
802 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
803 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
804 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
805 if (is_array($tmp)) {
806 foreach ($tmp as $key => $val) {
807 if ($key == $paramname) { // We are on the requested parameter
808 $out = $val;
809 break;
810 }
811 }
812 }
813 }
814 // If there is saved contextpage, page or limit
815 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
816 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
817 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
818 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
819 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
820 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
821 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
822 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
823 }
824 } elseif (!isset($_GET['sortfield'])) {
825 // Else, retrieve default values if we are not doing a sort
826 // 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
827 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
828 // Search default value from $object->field
829 global $object;
830 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
831 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
832 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
833 $out = $object->fields[$paramname]['default'];
834 }
835 }
836 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
837 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
838 // Now search in setup to overwrite default values
839 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
840 if (isset($user->default_values[$relativepathstring]['createform'])) {
841 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
842 $qualified = 0;
843 if ($defkey != '_noquery_') {
844 $tmpqueryarraytohave = explode('&', $defkey);
845 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
846 $foundintru = 0;
847 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
848 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
849 $foundintru = 1;
850 }
851 }
852 if (!$foundintru) {
853 $qualified = 1;
854 }
855 //var_dump($defkey.'-'.$qualified);
856 } else {
857 $qualified = 1;
858 }
859
860 if ($qualified) {
861 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
862 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
863 break;
864 }
865 }
866 }
867 }
868 }
869 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
870 // Management of default search_filters and sort order
871 if (!empty($user->default_values)) {
872 // $user->default_values defined from menu 'Setup - Default values'
873 //var_dump($user->default_values[$relativepathstring]);
874 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
875 // Sorted on which fields ? ASC or DESC ?
876 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
877 // Even if paramname is sortfield, data are stored into ['sortorder...']
878 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
879 $qualified = 0;
880 if ($defkey != '_noquery_') {
881 $tmpqueryarraytohave = explode('&', $defkey);
882 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
883 $foundintru = 0;
884 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
885 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
886 $foundintru = 1;
887 }
888 }
889 if (!$foundintru) {
890 $qualified = 1;
891 }
892 //var_dump($defkey.'-'.$qualified);
893 } else {
894 $qualified = 1;
895 }
896
897 if ($qualified) {
898 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
899 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
900 if ($out) {
901 $out .= ', ';
902 }
903 if ($paramname == 'sortfield') {
904 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
905 }
906 if ($paramname == 'sortorder') {
907 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
908 }
909 }
910 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
911 }
912 }
913 }
914 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
915 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
916 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
917 continue;
918 }
919 $qualified = 0;
920 if ($defkey != '_noquery_') {
921 $tmpqueryarraytohave = explode('&', $defkey);
922 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
923 $foundintru = 0;
924 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
925 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
926 $foundintru = 1;
927 }
928 }
929 if (!$foundintru) {
930 $qualified = 1;
931 }
932 //var_dump($defkey.'-'.$qualified);
933 } else {
934 $qualified = 1;
935 }
936
937 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
938 // We must keep $_POST and $_GET here
939 if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
940 // We made a search from quick search menu, do we still use default filter ?
941 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
942 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
943 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
944 }
945 } else {
946 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
947 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
948 }
949 break;
950 }
951 }
952 }
953 }
954 }
955 }
956 }
957 }
958
959 // 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)
960 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
961 // 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.
962 '@phan-var-force string $paramname';
963 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
964 $reg = array();
965 $maxloop = 20;
966 $loopnb = 0; // Protection against infinite loop
967 while (preg_match('/__([A-Z0-9]+_?[A-Z0-9]+)__/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.
968 $loopnb++;
969 $newout = '';
970
971 if ($reg[1] == 'DAY') {
972 $tmp = dol_getdate(dol_now(), true);
973 $newout = $tmp['mday'];
974 } elseif ($reg[1] == 'MONTH') {
975 $tmp = dol_getdate(dol_now(), true);
976 $newout = $tmp['mon'];
977 } elseif ($reg[1] == 'YEAR') {
978 $tmp = dol_getdate(dol_now(), true);
979 $newout = $tmp['year'];
980 } elseif ($reg[1] == 'PREVIOUS_DAY') {
981 $tmp = dol_getdate(dol_now(), true);
982 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
983 $newout = $tmp2['day'];
984 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
985 $tmp = dol_getdate(dol_now(), true);
986 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
987 $newout = $tmp2['month'];
988 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
989 $tmp = dol_getdate(dol_now(), true);
990 $newout = ($tmp['year'] - 1);
991 } elseif ($reg[1] == 'NEXT_DAY') {
992 $tmp = dol_getdate(dol_now(), true);
993 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
994 $newout = $tmp2['day'];
995 } elseif ($reg[1] == 'NEXT_MONTH') {
996 $tmp = dol_getdate(dol_now(), true);
997 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
998 $newout = $tmp2['month'];
999 } elseif ($reg[1] == 'NEXT_YEAR') {
1000 $tmp = dol_getdate(dol_now(), true);
1001 $newout = ($tmp['year'] + 1);
1002 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1003 $newout = $mysoc->country_id;
1004 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1005 $newout = $user->id;
1006 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1007 $newout = $user->fk_user;
1008 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1009 $newout = $conf->entity;
1010 } elseif ($reg[1] == 'ID') {
1011 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1012 } else {
1013 $newout = ''; // Key not found, we replace with empty string
1014 }
1015 //var_dump('__'.$reg[1].'__ -> '.$newout);
1016 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1017 }
1018 }
1019
1020 // Check type of variable and make sanitization according to this
1021 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1022 if (!is_array($out) || empty($out)) {
1023 $out = array();
1024 } else {
1025 $tmparray = explode(':', $check);
1026 if (!empty($tmparray[1])) {
1027 $tmpcheck = $tmparray[1];
1028 } else {
1029 $tmpcheck = 'alphanohtml';
1030 }
1031 foreach ($out as $outkey => $outval) {
1032 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1033 }
1034 }
1035 } else {
1036 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1037 // 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
1038 if (strpos($paramname, 'search_') === 0) {
1039 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1040 }
1041
1042 // @phan-suppress-next-line UnknownSanitizeType
1043 $out = sanitizeVal($out, $check, $filter, $options);
1044 }
1045
1046 // Sanitizing for special parameters.
1047 // 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.
1048 if (preg_match('/backtopage/', $paramname) || $paramname == 'backtolist' || $paramname == 'backtourl') {
1049 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1050 $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.
1051 do {
1052 $oldstringtoclean = $out;
1053 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1054 $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'
1055 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1056 } while ($oldstringtoclean != $out);
1057 }
1058
1059 // Code for search criteria persistence.
1060 // Save data into session if key start with 'search_'
1061 if (empty($method) || $method == 3 || $method == 4) {
1062 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1063 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1064
1065 // We save search key only if $out not empty that means:
1066 // - posted value not empty, or
1067 // - 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).
1068
1069 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1070 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1071 }
1072 }
1073 }
1074
1075 return $out;
1076}
1077
1087function GETPOSTINT($paramname, $method = 0)
1088{
1089 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1090}
1091
1092
1101function GETPOSTFLOAT($paramname, $rounding = '')
1102{
1103 // 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.)
1104 return (float) price2num(GETPOST($paramname), $rounding, 2);
1105}
1106
1107
1118function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1119{
1120 return sanitizeVal($out, $check, $filter, $options);
1121}
1122
1132function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1133{
1134 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1135 // Check is done after replacement
1136 switch ($check) {
1137 case 'none':
1138 break;
1139 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1140 if (!is_numeric($out)) {
1141 $out = '';
1142 }
1143 break;
1144 case 'intcomma':
1145 if (is_array($out)) {
1146 $out = implode(',', $out);
1147 }
1148 if (preg_match('/[^0-9,-]+/i', $out)) {
1149 $out = '';
1150 }
1151 break;
1152 case 'san_alpha':
1153 $out = filter_var($out, FILTER_SANITIZE_STRING);
1154 break;
1155 case 'email':
1156 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1157 break;
1158 case 'aZ':
1159 if (!is_array($out)) {
1160 $out = trim($out);
1161 if (preg_match('/[^a-z]+/i', $out)) {
1162 $out = '';
1163 }
1164 }
1165 break;
1166 case 'aZ09':
1167 if (!is_array($out)) {
1168 $out = trim($out);
1169 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1170 $out = '';
1171 }
1172 }
1173 break;
1174 case 'aZ09arobase': // great to sanitize $objecttype parameter
1175 if (!is_array($out)) {
1176 $out = trim($out);
1177 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1178 $out = '';
1179 }
1180 }
1181 break;
1182 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1183 if (!is_array($out)) {
1184 $out = trim($out);
1185 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1186 $out = '';
1187 }
1188 }
1189 break;
1190 case 'alpha': // No html and no ../ and "
1191 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1192 if (!is_array($out)) {
1193 $out = trim($out);
1194 do {
1195 $oldstringtoclean = $out;
1196 // Remove html tags
1197 $out = dol_string_nohtmltag($out, 0);
1198 // 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).
1199 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1200 // Remove also other dangerous string sequences
1201 // '../' or '..\' is dangerous because it allows dir transversals
1202 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1203 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1204 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1205 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1206 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1207 } while ($oldstringtoclean != $out);
1208 // keep lines feed
1209 }
1210 break;
1211 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email>". Less secured than 'alphanohtml'
1212 if (!is_array($out)) {
1213 $out = trim($out);
1214 do {
1215 $oldstringtoclean = $out;
1216 // Decode html entities
1217 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1218 // 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).
1219 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1220 // Remove also other dangerous string sequences
1221 // '../' or '..\' is dangerous because it allows dir transversals
1222 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1223 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1224 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1225 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1226 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1227 } while ($oldstringtoclean != $out);
1228 }
1229 break;
1230 case 'nohtml': // No html
1231 $out = dol_string_nohtmltag($out, 0);
1232 break;
1233 case 'restricthtmlnolink':
1234 case 'restricthtml': // Recommended for most html textarea
1235 case 'restricthtmlallowclass':
1236 case 'restricthtmlallowunvalid':
1237 $out = dol_htmlwithnojs($out, 1, $check);
1238 break;
1239 case 'custom':
1240 if (!empty($out)) {
1241 if (empty($filter)) {
1242 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1243 }
1244 if (is_null($options)) {
1245 $options = 0;
1246 }
1247 $out = filter_var($out, $filter, $options);
1248 }
1249 break;
1250 default:
1251 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1252 $out = GETPOST($out, 'alphanohtml');
1253 break;
1254 }
1255
1256 return $out;
1257}
1258
1259
1260if (!function_exists('dol_getprefix')) {
1271 function dol_getprefix($mode = '')
1272 {
1273 // If prefix is for email (we need to have $conf already loaded for this case)
1274 if ($mode == 'email') {
1275 global $conf;
1276
1277 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1278 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1279 return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1280 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1281 return $_SERVER["SERVER_NAME"];
1282 }
1283 }
1284
1285 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1286 if (!empty($conf->file->instance_unique_id)) {
1287 return sha1('dolibarr'.$conf->file->instance_unique_id);
1288 }
1289
1290 // For backward compatibility when instance_unique_id is not set
1291 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1292 }
1293
1294 // If prefix is for session (no need to have $conf loaded)
1295 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1296 $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
1297
1298 // The recommended value (may be not defined for old versions)
1299 if (!empty($tmp_instance_unique_id)) {
1300 return sha1('dolibarr'.$tmp_instance_unique_id);
1301 }
1302
1303 // For backward compatibility when instance_unique_id is not set
1304 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1305 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1306 } else {
1307 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1308 }
1309 }
1310}
1311
1322function dol_include_once($relpath, $classname = '')
1323{
1324 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']
1325
1326 $fullpath = dol_buildpath($relpath);
1327
1328 if (!file_exists($fullpath)) {
1329 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1330 return false;
1331 }
1332
1333 if (!empty($classname) && !class_exists($classname)) {
1334 return include $fullpath;
1335 } else {
1336 return include_once $fullpath;
1337 }
1338}
1339
1340
1354function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1355{
1356 global $conf;
1357
1358 $path = preg_replace('/^\//', '', $path);
1359
1360 if (empty($type)) { // For a filesystem path
1361 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1362 if (is_array($conf->file->dol_document_root)) {
1363 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1364 if ($key == 'main') {
1365 continue;
1366 }
1367 // if (@file_exists($dirroot.'/'.$path)) {
1368 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1369 $res = $dirroot.'/'.$path;
1370 return $res;
1371 }
1372 }
1373 }
1374 if ($returnemptyifnotfound) {
1375 // Not found into alternate dir
1376 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1377 return '';
1378 }
1379 }
1380 } else {
1381 // For an url path
1382 // We try to get local path of file on filesystem from url
1383 // Note that trying to know if a file on disk exist by forging path on disk from url
1384 // works only for some web server and some setup. This is bugged when
1385 // using proxy, rewriting, virtual path, etc...
1386 $res = '';
1387 if ($type == 1) {
1388 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1389 }
1390 if ($type == 2) {
1391 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1392 }
1393 if ($type == 3) {
1394 $res = DOL_URL_ROOT.'/'.$path;
1395 }
1396
1397 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1398 if ($key == 'main') {
1399 if ($type == 3) {
1400 /*global $dolibarr_main_url_root;*/
1401
1402 // Define $urlwithroot
1403 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1404 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1405 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1406
1407 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1408 }
1409 continue;
1410 }
1411 $regs = array();
1412 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1413 if (!empty($regs[1])) {
1414 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1415 //if (file_exists($dirroot.'/'.$regs[1])) {
1416 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1417 if ($type == 1) {
1418 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1419 } elseif ($type == 2) {
1420 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1421 } elseif ($type == 3) {
1422 /*global $dolibarr_main_url_root;*/
1423
1424 // Define $urlwithroot
1425 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1426 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1427 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1428
1429 $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
1430 }
1431 break;
1432 }
1433 }
1434 }
1435 }
1436
1437 return $res;
1438}
1439
1450function dol_get_object_properties($obj, $properties = [])
1451{
1452 // Get real properties using get_object_vars() if $properties is empty
1453 if (empty($properties)) {
1454 return get_object_vars($obj);
1455 }
1456
1457 $existingProperties = [];
1458 $realProperties = get_object_vars($obj);
1459
1460 // Get the real or magic property values
1461 foreach ($properties as $property) {
1462 if (array_key_exists($property, $realProperties)) {
1463 // Real property, add the value
1464 $existingProperties[$property] = $obj->{$property};
1465 } elseif (property_exists($obj, $property)) {
1466 // Magic property
1467 $existingProperties[$property] = $obj->{$property};
1468 }
1469 }
1470
1471 return $existingProperties;
1472}
1473
1474
1489function dol_clone($object, $native = 0)
1490{
1491 if ($native == 0) {
1492 // deprecated method, use the method with native = 2 instead
1493 $tmpsavdb = null;
1494 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1495 $tmpsavdb = $object->db;
1496 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1497 }
1498
1499 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1500
1501 if (!empty($tmpsavdb)) {
1502 $object->db = $tmpsavdb;
1503 }
1504 } elseif ($native == 2) {
1505 // recommended method to have a full isolated cloned object
1506 $myclone = new stdClass();
1507 $tmparray = get_object_vars($object); // return only public properties
1508
1509 if (is_array($tmparray)) {
1510 foreach ($tmparray as $propertykey => $propertyval) {
1511 if (is_scalar($propertyval) || is_array($propertyval)) {
1512 $myclone->$propertykey = $propertyval;
1513 }
1514 }
1515 }
1516 } else {
1517 $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)
1518 }
1519
1520 return $myclone;
1521}
1522
1532function dol_size($size, $type = '')
1533{
1534 global $conf;
1535 if (empty($conf->dol_optimize_smallscreen)) {
1536 return $size;
1537 }
1538 if ($type == 'width' && $size > 250) {
1539 return 250;
1540 } else {
1541 return 10;
1542 }
1543}
1544
1545
1557function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1558{
1559 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1560 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1561 // Char '/' and '\' are file delimiters.
1562 // 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
1563 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1564 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1565 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1566 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1567 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1568 $tmp = str_replace('..', '', $tmp);
1569 return $tmp;
1570}
1571
1572
1584function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1585{
1586 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1587 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1588 // 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
1589 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1590 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1591 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1592 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1593 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1594 $tmp = str_replace('..', '', $tmp);
1595 return $tmp;
1596}
1597
1605function dol_sanitizeUrl($stringtoclean, $type = 1)
1606{
1607 // 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)
1608 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1609 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1610 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1611 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1612
1613 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1614 if ($type == 1) {
1615 // removing : should disable links to external url like http:aaa)
1616 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1617 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1618 }
1619
1620 do {
1621 $oldstringtoclean = $stringtoclean;
1622 // removing '&colon' should disable links to external url like http:aaa)
1623 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1624 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1625 } while ($oldstringtoclean != $stringtoclean);
1626
1627 if ($type == 1) {
1628 // removing '//' should disable links to external url like //aaa or http//)
1629 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1630 }
1631
1632 return $stringtoclean;
1633}
1634
1641function dol_sanitizeEmail($stringtoclean)
1642{
1643 do {
1644 $oldstringtoclean = $stringtoclean;
1645 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1646 } while ($oldstringtoclean != $stringtoclean);
1647
1648 return $stringtoclean;
1649}
1650
1659function dol_string_unaccent($str)
1660{
1661 global $conf;
1662
1663 if (is_null($str)) {
1664 return '';
1665 }
1666
1667 if (utf8_check($str)) {
1668 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1669 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1670 return $transliterator->transliterate($str);
1671 }
1672 // See http://www.utf8-chartable.de/
1673 $string = rawurlencode($str);
1674 $replacements = array(
1675 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1676 '%C3%87' => 'C',
1677 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1678 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1679 '%C3%91' => 'N',
1680 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1681 '%C5%A0' => 'S',
1682 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1683 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1684 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1685 '%C3%A7' => 'c',
1686 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1687 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1688 '%C3%B1' => 'n',
1689 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1690 '%C5%A1' => 's',
1691 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1692 '%C3%BD' => 'y', '%C3%BF' => 'y'
1693 );
1694 $string = strtr($string, $replacements);
1695 return rawurldecode($string);
1696 } else {
1697 // See http://www.ascii-code.com/
1698 $string = strtr(
1699 $str,
1700 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1701 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1702 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1703 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1704 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1705 \xF9\xFA\xFB\xFC\xFD\xFF",
1706 "AAAAAAC
1707 EEEEIIIIDN
1708 OOOOOUUUY
1709 aaaaaaceeee
1710 iiiidnooooo
1711 uuuuyy"
1712 );
1713 $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"));
1714 return $string;
1715 }
1716}
1717
1731function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1732{
1733 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1734 if (empty($keepspaces)) {
1735 $forbidden_chars_to_replace[] = " ";
1736 }
1737 $forbidden_chars_to_remove = array();
1738 //$forbidden_chars_to_remove=array("(",")");
1739
1740 if (is_array($badcharstoreplace)) {
1741 $forbidden_chars_to_replace = $badcharstoreplace;
1742 }
1743 if (is_array($badcharstoremove)) {
1744 $forbidden_chars_to_remove = $badcharstoremove;
1745 }
1746
1747 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1748 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1749}
1750
1751
1765function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1766{
1767 if ($removetabcrlf) {
1768 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1769 } else {
1770 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
1771 }
1772}
1773
1782function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1783{
1784 if (is_null($stringtoescape)) {
1785 return '';
1786 }
1787
1788 // escape quotes and backslashes, newlines, etc.
1789 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1790 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1791 if (empty($noescapebackslashn)) {
1792 $substitjs["\n"] = '\\n';
1793 $substitjs['\\'] = '\\\\';
1794 }
1795 if (empty($mode)) {
1796 $substitjs["'"] = "\\'";
1797 $substitjs['"'] = "\\'";
1798 } elseif ($mode == 1) {
1799 $substitjs["'"] = "\\'";
1800 } elseif ($mode == 2) {
1801 $substitjs['"'] = '\\"';
1802 } elseif ($mode == 3) {
1803 $substitjs["'"] = "\\'";
1804 $substitjs['"'] = "\\\"";
1805 }
1806 return strtr($stringtoescape, $substitjs);
1807}
1808
1818function dol_escape_uri($stringtoescape)
1819{
1820 return rawurlencode($stringtoescape);
1821}
1822
1829function dol_escape_json($stringtoescape)
1830{
1831 return str_replace('"', '\"', $stringtoescape);
1832}
1833
1841function dol_escape_php($stringtoescape, $stringforquotes = 2)
1842{
1843 if (is_null($stringtoescape)) {
1844 return '';
1845 }
1846
1847 if ($stringforquotes == 2) {
1848 return str_replace('"', "'", $stringtoescape);
1849 } elseif ($stringforquotes == 1) {
1850 // We remove the \ char.
1851 // If we allow the \ char, we can have $stringtoescape =
1852 // abc\';phpcodedanger; so the escapement will become
1853 // abc\\';phpcodedanger; and injecting this into
1854 // $a='...' will give $ac='abc\\';phpcodedanger;
1855 $stringtoescape = str_replace('\\', '', $stringtoescape);
1856 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1857 }
1858
1859 return 'Bad parameter for stringforquotes in dol_escape_php';
1860}
1861
1868function dol_escape_all($stringtoescape)
1869{
1870 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1871}
1872
1879function dol_escape_xml($stringtoescape)
1880{
1881 return $stringtoescape;
1882}
1883
1891function dolPrintLabel($s)
1892{
1893 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1894}
1895
1905function dolPrintHTML($s, $allowiframe = 0)
1906{
1907 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1908}
1909
1917function dolPrintHTMLForAttribute($s)
1918{
1919 // The dol_htmlentitiesbr will convert simple text into html
1920 // The dol_escape_htmltag will escape html chars.
1921 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
1922}
1923
1932function dolPrintHTMLForTextArea($s, $allowiframe = 0)
1933{
1934 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
1935}
1936
1943function dolPrintPassword($s)
1944{
1945 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
1946}
1947
1948
1965function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1966{
1967 if ($noescapetags == 'common') {
1968 $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';
1969 // Add also html5 tags
1970 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
1971 }
1972 if ($cleanalsojavascript) {
1973 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1974 }
1975
1976 // escape quotes and backslashes, newlines, etc.
1977 if ($escapeonlyhtmltags) {
1978 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1979 } else {
1980 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
1981 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
1982 }
1983 if (!$keepb) {
1984 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
1985 }
1986 if (!$keepn) {
1987 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
1988 } elseif ($keepn == -1) {
1989 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
1990 }
1991
1992 if ($escapeonlyhtmltags) {
1993 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1994 } else {
1995 // Escape tags to keep
1996 $tmparrayoftags = array();
1997 if ($noescapetags) {
1998 $tmparrayoftags = explode(',', $noescapetags);
1999 }
2000 if (count($tmparrayoftags)) {
2001 $reg = array();
2002 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2003
2004 foreach ($tmparrayoftags as $tagtoreplace) {
2005 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2006 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2007 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2008
2009 // For case of tag with attribute
2010 do {
2011 $tmpold = $tmp;
2012
2013 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', $tmp, $reg)) {
2014 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must never have [ ] inside the attribute string
2015 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes);
2016 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2017 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2018 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2019 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2020 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2021 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2022 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2023 }
2024 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)\s+\/>/', $tmp, $reg)) {
2025 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
2026 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2027 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2028 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2029 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2030 }
2031
2032 $diff = strcmp($tmpold, $tmp);
2033 } while ($diff);
2034 }
2035 }
2036
2037 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2038
2039 //print $result;
2040
2041 if (count($tmparrayoftags)) {
2042 foreach ($tmparrayoftags as $tagtoreplace) {
2043 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2044 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2045 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2046 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2047 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2048 }
2049
2050 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2051 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2052 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2053 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2054 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2055 }
2056
2057 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2058
2059 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2060
2061 return $result;
2062 }
2063}
2064
2072function dol_strtolower($string, $encoding = "UTF-8")
2073{
2074 if (function_exists('mb_strtolower')) {
2075 return mb_strtolower($string, $encoding);
2076 } else {
2077 return strtolower($string);
2078 }
2079}
2080
2089function dol_strtoupper($string, $encoding = "UTF-8")
2090{
2091 if (function_exists('mb_strtoupper')) {
2092 return mb_strtoupper($string, $encoding);
2093 } else {
2094 return strtoupper($string);
2095 }
2096}
2097
2106function dol_ucfirst($string, $encoding = "UTF-8")
2107{
2108 if (function_exists('mb_substr')) {
2109 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2110 } else {
2111 return ucfirst($string);
2112 }
2113}
2114
2123function dol_ucwords($string, $encoding = "UTF-8")
2124{
2125 if (function_exists('mb_convert_case')) {
2126 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2127 } else {
2128 return ucwords($string);
2129 }
2130}
2131
2154function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2155{
2156 global $conf, $user, $debugbar;
2157
2158 // If syslog module enabled
2159 if (!isModEnabled('syslog')) {
2160 return;
2161 }
2162
2163 // Check if we are into execution of code of a website
2164 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2165 global $website, $websitekey;
2166 if (is_object($website) && !empty($website->ref)) {
2167 $suffixinfilename .= '_website_'.$website->ref;
2168 } elseif (!empty($websitekey)) {
2169 $suffixinfilename .= '_website_'.$websitekey;
2170 }
2171 }
2172
2173 // Check if we have a forced suffix
2174 if (defined('USESUFFIXINLOG')) {
2175 $suffixinfilename .= constant('USESUFFIXINLOG');
2176 }
2177
2178 if ($ident < 0) {
2179 foreach ($conf->loghandlers as $loghandlerinstance) {
2180 $loghandlerinstance->setIdent($ident);
2181 }
2182 }
2183
2184 if (!empty($message)) {
2185 // Test log level
2186 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2187 $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');
2188
2189 if (!array_key_exists($level, $logLevels)) {
2190 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2191 $level = $logLevels[LOG_ERR];
2192 }
2193 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2194 return;
2195 }
2196
2197 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2198 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2199 }
2200
2201 // If adding log inside HTML page is required
2202 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2203 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2204 $ospid = sprintf("%7s", dol_trunc(getmypid(), 7, 'right', 'UTF-8', 1));
2205 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2206
2207 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2208 }
2209
2210 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2211 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2212 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2213 print "\n\n<!-- Log start\n";
2214 print dol_escape_htmltag($message)."\n";
2215 print "Log end -->\n";
2216 }
2217
2218 $data = array(
2219 'message' => $message,
2220 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2221 'level' => $level,
2222 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2223 'ip' => false,
2224 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2225 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2226 );
2227
2228 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2229 if (!empty($remoteip)) {
2230 $data['ip'] = $remoteip;
2231 // This is when server run behind a reverse proxy
2232 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2233 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2234 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2235 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2236 }
2237 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2238 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2239 $data['ip'] = $_SERVER['SERVER_ADDR'];
2240 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2241 // 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).
2242 $data['ip'] = $_SERVER['COMPUTERNAME'];
2243 } else {
2244 $data['ip'] = '???';
2245 }
2246
2247 if (!empty($_SERVER['USERNAME'])) {
2248 // 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).
2249 $data['osuser'] = $_SERVER['USERNAME'];
2250 } elseif (!empty($_SERVER['LOGNAME'])) {
2251 // 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).
2252 $data['osuser'] = $_SERVER['LOGNAME'];
2253 }
2254
2255 // Loop on each log handler and send output
2256 foreach ($conf->loghandlers as $loghandlerinstance) {
2257 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2258 continue;
2259 }
2260 $loghandlerinstance->export($data, $suffixinfilename);
2261 }
2262 unset($data);
2263 }
2264
2265 if ($ident > 0) {
2266 foreach ($conf->loghandlers as $loghandlerinstance) {
2267 $loghandlerinstance->setIdent($ident);
2268 }
2269 }
2270}
2271
2283function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2284{
2285 global $langs, $db;
2286
2287 $form = new Form($db);
2288
2289 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2290 if (empty($templatenameforexport)) {
2291 $templatenameforexport = 'website_'.$website->ref;
2292 }
2293
2294 $out = '';
2295 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2296
2297 // for generate popup
2298 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2299 $out .= 'jQuery(document).ready(function () {';
2300 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2301 $out .= ' var dialogHtml = \'';
2302
2303 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2304 $dialogcontent .= ' <div style="margin-top: 20px;">';
2305 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2306 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2307 $dialogcontent .= ' </div>';
2308 $dialogcontent .= ' <br>';
2309 $dialogcontent .= ' <div style="margin-top: 20px;">';
2310 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2311 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2312 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2313 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2314 $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>';
2315 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2316 $dialogcontent .= ' </form>';
2317 $dialogcontent .= ' </div>';
2318 $dialogcontent .= ' </div>';
2319
2320 $out .= dol_escape_js($dialogcontent);
2321
2322 $out .= '\';';
2323
2324
2325 // Add the content of the dialog to the body of the page
2326 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2327 $out .= ' if ($dialog.length > 0) {
2328 $dialog.remove();
2329 }
2330 jQuery("body").append(dialogHtml);';
2331
2332 // Configuration of popup
2333 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2334 $out .= ' autoOpen: false,';
2335 $out .= ' modal: true,';
2336 $out .= ' height: 290,';
2337 $out .= ' width: "40%",';
2338 $out .= ' title: "' . dol_escape_js($label) . '",';
2339 $out .= ' });';
2340
2341 // Simulate a click on the original "submit" input to export the site.
2342 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2343 $out .= ' console.log("Clic on exportsite.");';
2344 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2345 $out .= ' console.log("element founded:", target.length > 0);';
2346 $out .= ' if (target.length > 0) { target.click(); }';
2347 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2348 $out .= ' });';
2349
2350 // open popup
2351 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2352 $out .= ' return false;';
2353 $out .= ' });';
2354 $out .= '});';
2355 $out .= '</script>';
2356
2357 return $out;
2358}
2359
2360
2377function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2378{
2379 global $conf;
2380
2381 if (strpos($url, '?') > 0) {
2382 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2383 } else {
2384 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2385 }
2386
2387 $out = '';
2388
2389 $backtopagejsfieldsid = '';
2390 $backtopagejsfieldslabel = '';
2391 if ($backtopagejsfields) {
2392 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2393 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2394 $backtopagejsfields = $name.":".$backtopagejsfields;
2395 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2396 } else {
2397 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2398 }
2399 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2400 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2401 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2402 }
2403
2404 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2405 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2406 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2407 if (empty($conf->use_javascript_ajax)) {
2408 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2409 } elseif ($jsonopen) {
2410 $out .= ' href="#" onclick="'.$jsonopen.'"';
2411 } else {
2412 $out .= ' href="#"';
2413 }
2414 $out .= '>'.$buttonstring.'</a>';
2415
2416 if (!empty($conf->use_javascript_ajax)) {
2417 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2418 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2419 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2420 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2421 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2422
2423 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2424 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2425 jQuery(document).ready(function () {
2426 jQuery(".button_'.$name.'").click(function () {
2427 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2428 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2429 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2430 $tmpdialog.dialog({
2431 autoOpen: false,
2432 modal: true,
2433 height: (window.innerHeight - 150),
2434 width: \'80%\',
2435 title: \''.dol_escape_js($label).'\',
2436 open: function (event, ui) {
2437 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2438 },
2439 close: function (event, ui) {
2440 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2441 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2442 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2443 if (returnedid != "" && returnedid != "div for returned id") {
2444 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2445 }
2446 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2447 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2448 }
2449 }
2450 });
2451
2452 $tmpdialog.dialog(\'open\');
2453 return false;
2454 });
2455 });
2456 </script>';
2457 }
2458 return $out;
2459}
2460
2477function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2478{
2479 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2480}
2481
2498function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2499{
2500 global $conf, $langs, $hookmanager;
2501
2502 // Show title
2503 $showtitle = 1;
2504 if (!empty($conf->dol_optimize_smallscreen)) {
2505 $showtitle = 0;
2506 }
2507
2508 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2509
2510 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2511 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2512 }
2513
2514 // Show right part
2515 if ($morehtmlright) {
2516 $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.
2517 }
2518
2519 // Show title
2520 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2521 $limittitle = 30;
2522 $out .= '<a class="tabTitle">';
2523 if ($picto) {
2524 $noprefix = $pictoisfullpath;
2525 if (strpos($picto, 'fontawesome_') !== false) {
2526 $noprefix = 1;
2527 }
2528 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2529 }
2530 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2531 $out .= '</a>';
2532 }
2533
2534 // Show tabs
2535
2536 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2537 $maxkey = -1;
2538 if (is_array($links) && !empty($links)) {
2539 $keys = array_keys($links);
2540 if (count($keys)) {
2541 $maxkey = max($keys);
2542 }
2543 }
2544
2545 // Show tabs
2546 // if =0 we don't use the feature
2547 if (empty($limittoshow)) {
2548 $limittoshow = (!getDolGlobalString('MAIN_MAXTABS_IN_CARD') ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2549 }
2550 if (!empty($conf->dol_optimize_smallscreen)) {
2551 $limittoshow = 2;
2552 }
2553
2554 $displaytab = 0;
2555 $nbintab = 0;
2556 $popuptab = 0;
2557 $outmore = '';
2558 for ($i = 0; $i <= $maxkey; $i++) {
2559 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2560 // If active tab is already present
2561 if ($i >= $limittoshow) {
2562 $limittoshow--;
2563 }
2564 }
2565 }
2566
2567 for ($i = 0; $i <= $maxkey; $i++) {
2568 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2569 $isactive = true;
2570 } else {
2571 $isactive = false;
2572 }
2573
2574 if ($i < $limittoshow || $isactive) {
2575 // Output entry with a visible tab
2576 $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])).' -->';
2577
2578 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2579 if (!empty($links[$i][0])) {
2580 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2581 } else {
2582 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2583 }
2584 } elseif (!empty($links[$i][1])) {
2585 //print "x $i $active ".$links[$i][2]." z";
2586 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2587 if (!empty($links[$i][0])) {
2588 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2589 $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).'">';
2590 }
2591 $out .= $links[$i][1];
2592 if (!empty($links[$i][0])) {
2593 $out .= '</a>'."\n";
2594 }
2595 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2596 $out .= '</div>';
2597 }
2598
2599 $out .= '</div>';
2600 } else {
2601 // Add entry into the combo popup with the other tabs
2602 if (!$popuptab) {
2603 $popuptab = 1;
2604 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2605 }
2606 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2607 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2608 if (!empty($links[$i][0])) {
2609 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2610 } else {
2611 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2612 }
2613 } elseif (!empty($links[$i][1])) {
2614 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2615 $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.
2616 $outmore .= '</a>'."\n";
2617 }
2618 $outmore .= '</div>';
2619
2620 $nbintab++;
2621 }
2622 $displaytab = $i;
2623 }
2624 if ($popuptab) {
2625 $outmore .= '</div>';
2626 }
2627
2628 if ($popuptab) { // If there is some tabs not shown
2629 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2630 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2631 $widthofpopup = 200;
2632
2633 $tabsname = $moretabssuffix;
2634 if (empty($tabsname)) {
2635 $tabsname = str_replace("@", "", $picto);
2636 }
2637 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2638 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2639 $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".
2640 }
2641 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2642 $out .= $outmore;
2643 $out .= '</div>';
2644 $out .= '<div></div>';
2645 $out .= "</div>\n";
2646
2647 $out .= '<script nonce="'.getNonce().'">';
2648 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2649 var x = this.offsetLeft, y = this.offsetTop;
2650 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2651 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2652 $('#moretabsList".$tabsname."').css('".$right."','8px');
2653 }
2654 $('#moretabsList".$tabsname."').css('".$left."','auto');
2655 });
2656 ";
2657 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2658 $out .= "</script>";
2659 }
2660
2661 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2662 $out .= "</div>\n";
2663 }
2664
2665 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2666 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom')));
2667 $out .= '">'."\n";
2668 }
2669 if (!empty($dragdropfile)) {
2670 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2671 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2672 }
2673 $parameters = array('tabname' => $active, 'out' => $out);
2674 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2675 if ($reshook > 0) {
2676 $out = $hookmanager->resPrint;
2677 }
2678
2679 return $out;
2680}
2681
2689function dol_fiche_end($notab = 0)
2690{
2691 print dol_get_fiche_end($notab);
2692}
2693
2700function dol_get_fiche_end($notab = 0)
2701{
2702 if (!$notab || $notab == -1) {
2703 return "\n</div>\n";
2704 } else {
2705 return '';
2706 }
2707}
2708
2728function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2729{
2730 global $conf, $form, $user, $langs, $hookmanager, $action;
2731
2732 $error = 0;
2733
2734 $maxvisiblephotos = 1;
2735 $showimage = 1;
2736 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2737 // @phan-suppress-next-line PhanUndeclaredMethod
2738 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2739 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2740 $showbarcode = 0;
2741 }
2742 $modulepart = 'unknown';
2743
2744 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2745 $modulepart = $object->element;
2746 } elseif ($object->element == 'member') {
2747 $modulepart = 'memberphoto';
2748 } elseif ($object->element == 'user') {
2749 $modulepart = 'userphoto';
2750 }
2751
2752 if (class_exists("Imagick")) {
2753 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2754 $modulepart = $object->element;
2755 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2756 $modulepart = 'ficheinter';
2757 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2758 $modulepart = 'contract';
2759 } elseif ($object->element == 'order_supplier') {
2760 $modulepart = 'supplier_order';
2761 } elseif ($object->element == 'invoice_supplier') {
2762 $modulepart = 'supplier_invoice';
2763 }
2764 }
2765
2766 if ($object->element == 'product') {
2768 '@phan-var-force Product $object';
2769 $width = 80;
2770 $cssclass = 'photowithmargin photoref';
2771 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2772 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2773 if ($conf->browser->layout == 'phone') {
2774 $maxvisiblephotos = 1;
2775 }
2776 if ($showimage) {
2777 $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>';
2778 } else {
2779 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2780 $nophoto = '';
2781 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2782 } else { // Show no photo link
2783 $nophoto = '/public/theme/common/nophoto.png';
2784 $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>';
2785 }
2786 }
2787 } elseif ($object->element == 'category') {
2789 '@phan-var-force Categorie $object';
2790 $width = 80;
2791 $cssclass = 'photowithmargin photoref';
2792 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2793 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2794 if ($conf->browser->layout == 'phone') {
2795 $maxvisiblephotos = 1;
2796 }
2797 if ($showimage) {
2798 $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>';
2799 } else {
2800 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2801 $nophoto = '';
2802 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2803 } else { // Show no photo link
2804 $nophoto = '/public/theme/common/nophoto.png';
2805 $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>';
2806 }
2807 }
2808 } elseif ($object->element == 'bom') {
2810 '@phan-var-force Bom $object';
2811 $width = 80;
2812 $cssclass = 'photowithmargin photoref';
2813 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2814 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2815 if ($conf->browser->layout == 'phone') {
2816 $maxvisiblephotos = 1;
2817 }
2818 if ($showimage) {
2819 $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>';
2820 } else {
2821 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2822 $nophoto = '';
2823 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2824 } else { // Show no photo link
2825 $nophoto = '/public/theme/common/nophoto.png';
2826 $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>';
2827 }
2828 }
2829 } elseif ($object->element == 'ticket') {
2830 $width = 80;
2831 $cssclass = 'photoref';
2833 '@phan-var-force Ticket $object';
2834 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2835 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2836 if ($conf->browser->layout == 'phone') {
2837 $maxvisiblephotos = 1;
2838 }
2839
2840 if ($showimage) {
2841 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2842 if ($object->nbphoto > 0) {
2843 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2844 } else {
2845 $showimage = 0;
2846 }
2847 }
2848 if (!$showimage) {
2849 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2850 $nophoto = '';
2851 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2852 } else { // Show no photo link
2853 $nophoto = img_picto('No photo', 'object_ticket');
2854 $morehtmlleft .= '<!-- No photo to show -->';
2855 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2856 $morehtmlleft .= $nophoto;
2857 $morehtmlleft .= '</div></div>';
2858 }
2859 }
2860 } else {
2861 if ($showimage) {
2862 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2863 $phototoshow = '';
2864 // Check if a preview file is available
2865 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2866 $objectref = dol_sanitizeFileName($object->ref);
2867 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2868 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2869 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2870 $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
2871 } else {
2872 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2873 }
2874 if (empty($subdir)) {
2875 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2876 }
2877
2878 $filepath = $dir_output.$subdir."/";
2879
2880 $filepdf = $filepath.$objectref.".pdf";
2881 $relativepath = $subdir.'/'.$objectref.'.pdf';
2882
2883 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2884 $fileimage = $filepdf.'_preview.png';
2885 $relativepathimage = $relativepath.'_preview.png';
2886
2887 $pdfexists = file_exists($filepdf);
2888
2889 // If PDF file exists
2890 if ($pdfexists) {
2891 // Conversion du PDF en image png si fichier png non existent
2892 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2893 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2894 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2895 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2896 if ($ret < 0) {
2897 $error++;
2898 }
2899 }
2900 }
2901 }
2902
2903 if ($pdfexists && !$error) {
2904 $heightforphotref = 80;
2905 if (!empty($conf->dol_optimize_smallscreen)) {
2906 $heightforphotref = 60;
2907 }
2908 // If the preview file is found
2909 if (file_exists($fileimage)) {
2910 $phototoshow = '<div class="photoref">';
2911 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2912 $phototoshow .= '</div>';
2913 }
2914 }
2915 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
2916 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2917 }
2918
2919 if ($phototoshow) {
2920 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2921 $morehtmlleft .= $phototoshow;
2922 $morehtmlleft .= '</div>';
2923 }
2924 }
2925
2926 if (empty($phototoshow)) { // Show No photo link (picto of object)
2927 if ($object->element == 'action') {
2928 $width = 80;
2929 $cssclass = 'photorefcenter';
2930 $nophoto = img_picto('No photo', 'title_agenda');
2931 } else {
2932 $width = 14;
2933 $cssclass = 'photorefcenter';
2934 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
2935 $prefix = 'object_';
2936 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
2937 $picto = 'project'; // instead of projectpub
2938 }
2939 if (strpos($picto, 'fontawesome_') !== false) {
2940 $prefix = '';
2941 }
2942 $nophoto = img_picto('No photo', $prefix.$picto);
2943 }
2944 $morehtmlleft .= '<!-- No photo to show -->';
2945 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2946 $morehtmlleft .= $nophoto;
2947 $morehtmlleft .= '</div></div>';
2948 }
2949 }
2950 }
2951
2952 // Show barcode
2953 if ($showbarcode) {
2954 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2955 }
2956
2957 if ($object->element == 'societe') {
2958 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2959 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2960 } else {
2961 $morehtmlstatus .= $object->getLibStatut(6);
2962 }
2963 } elseif ($object->element == 'product') {
2964 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2965 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2966 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2967 } else {
2968 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2969 }
2970 $morehtmlstatus .= ' &nbsp; ';
2971 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2972 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2973 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2974 } else {
2975 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2976 }
2977 } elseif (in_array($object->element, array('salary'))) {
2978 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
2979 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2980 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
2981 }
2982 $morehtmlstatus .= $tmptxt;
2983 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
2984 $totalallpayments = $object->getSommePaiement(0);
2985 $totalallpayments += $object->getSumCreditNotesUsed(0);
2986 $totalallpayments += $object->getSumDepositsUsed(0);
2987 $tmptxt = $object->getLibStatut(6, $totalallpayments);
2988 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2989 $tmptxt = $object->getLibStatut(5, $totalallpayments);
2990 }
2991 $morehtmlstatus .= $tmptxt;
2992 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
2993 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2994 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2995 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2996 }
2997 $morehtmlstatus .= $tmptxt;
2998 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2999 if ($object->statut == 0) {
3000 $morehtmlstatus .= $object->getLibStatut(5);
3001 } else {
3002 $morehtmlstatus .= $object->getLibStatut(4);
3003 }
3004 } elseif ($object->element == 'facturerec') {
3005 '@phan-var-force FactureRec $object';
3006 if ($object->frequency == 0) {
3007 $morehtmlstatus .= $object->getLibStatut(2);
3008 } else {
3009 $morehtmlstatus .= $object->getLibStatut(5);
3010 }
3011 } elseif ($object->element == 'project_task') {
3012 $object->fk_statut = 1;
3013 if ($object->progress > 0) {
3014 $object->fk_statut = 2;
3015 }
3016 if ($object->progress >= 100) {
3017 $object->fk_statut = 3;
3018 }
3019 $tmptxt = $object->getLibStatut(5);
3020 $morehtmlstatus .= $tmptxt; // No status on task
3021 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3022 $tmptxt = $object->getLibStatut(6);
3023 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3024 $tmptxt = $object->getLibStatut(5);
3025 }
3026 $morehtmlstatus .= $tmptxt;
3027 }
3028
3029 // Add if object was dispatched "into accountancy"
3030 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3031 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3032 if (method_exists($object, 'getVentilExportCompta')) {
3033 $accounted = $object->getVentilExportCompta();
3034 $langs->load("accountancy");
3035 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3036 }
3037 }
3038
3039 // Add alias for thirdparty
3040 if (!empty($object->name_alias)) {
3041 '@phan-var-force Societe $object';
3042 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3043 }
3044
3045 // Add label
3046 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3047 if (!empty($object->label)) {
3048 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3049 }
3050 }
3051 // Show address and email
3052 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3053 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3054 if ($moreaddress) {
3055 $morehtmlref .= '<div class="refidno refaddress">';
3056 $morehtmlref .= $moreaddress;
3057 $morehtmlref .= '</div>';
3058 }
3059 }
3060 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)) {
3061 $morehtmlref .= '<div style="clear: both;"></div>';
3062 $morehtmlref .= '<div class="refidno opacitymedium">';
3063 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3064 $morehtmlref .= '</div>';
3065 }
3066
3067 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3068 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3069 if ($reshook < 0) {
3070 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3071 } elseif (empty($reshook)) {
3072 $morehtmlref .= $hookmanager->resPrint;
3073 } elseif ($reshook > 0) {
3074 $morehtmlref = $hookmanager->resPrint;
3075 }
3076
3077 // $morehtml is the right part (link "Back to list")
3078 // $morehtmlleft is the picto or photo of banner
3079 // $morehtmlstatus is part under the status
3080 // $morehtmlright is part of htmlright
3081
3082 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3083 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3084 print '</div>';
3085 print '<div class="underrefbanner clearboth"></div>';
3086}
3087
3097function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3098{
3099 global $langs;
3100 $ret = '';
3101 if ($fieldrequired) {
3102 $ret .= '<span class="fieldrequired">';
3103 }
3104 $ret .= '<label for="'.$fieldkey.'">';
3105 $ret .= $langs->trans($langkey);
3106 $ret .= '</label>';
3107 if ($fieldrequired) {
3108 $ret .= '</span>';
3109 }
3110 return $ret;
3111}
3112
3120function dol_bc($var, $moreclass = '')
3121{
3122 global $bc;
3123 $ret = ' '.$bc[$var];
3124 if ($moreclass) {
3125 $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
3126 }
3127 return $ret;
3128}
3129
3143function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3144{
3145 global $langs, $hookmanager;
3146
3147 $ret = '';
3148 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3149
3150 // See format of addresses on https://en.wikipedia.org/wiki/Address
3151 // Address
3152 if (empty($mode)) {
3153 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3154 }
3155 // Zip/Town/State
3156 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3157 // US: title firstname name \n address lines \n town, state, zip \n country
3158 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3159 $ret .= (($ret && $town) ? $sep : '').$town;
3160
3161 if (!empty($object->state)) {
3162 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3163 }
3164 if (!empty($object->zip)) {
3165 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3166 }
3167 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3168 // UK: title firstname name \n address lines \n town state \n zip \n country
3169 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3170 $ret .= ($ret ? $sep : '').$town;
3171 if (!empty($object->state)) {
3172 $ret .= ($ret ? ", " : '').$object->state;
3173 }
3174 if (!empty($object->zip)) {
3175 $ret .= ($ret ? $sep : '').$object->zip;
3176 }
3177 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3178 // ES: title firstname name \n address lines \n zip town \n state \n country
3179 $ret .= ($ret ? $sep : '').$object->zip;
3180 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3181 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3182 if (!empty($object->state)) {
3183 $ret .= $sep.$object->state;
3184 }
3185 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3186 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3187 // See https://www.sljfaq.org/afaq/addresses.html
3188 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3189 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3190 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3191 // IT: title firstname name\n address lines \n zip town state_code \n country
3192 $ret .= ($ret ? $sep : '').$object->zip;
3193 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3194 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3195 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3196 } else {
3197 // Other: title firstname name \n address lines \n zip town[, state] \n country
3198 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3199 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3200 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3201 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3202 $ret .= ($ret ? ", " : '').$object->state;
3203 }
3204 }
3205
3206 if (!is_object($outputlangs)) {
3207 $outputlangs = $langs;
3208 }
3209 if ($withcountry) {
3210 $langs->load("dict");
3211 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3212 }
3213 if ($hookmanager) {
3214 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3215 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3216 if ($reshook > 0) {
3217 $ret = '';
3218 }
3219 $ret .= $hookmanager->resPrint;
3220 }
3221
3222 return $ret;
3223}
3224
3225
3226
3236function dol_strftime($fmt, $ts = false, $is_gmt = false)
3237{
3238 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3239 return dol_print_date($ts, $fmt, $is_gmt);
3240 } else {
3241 return 'Error date outside supported range';
3242 }
3243}
3244
3266function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3267{
3268 global $conf, $langs;
3269
3270 // If date undefined or "", we return ""
3271 if (dol_strlen($time) == 0) {
3272 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3273 }
3274
3275 if ($tzoutput === 'auto') {
3276 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3277 }
3278
3279 // Clean parameters
3280 $to_gmt = false;
3281 $offsettz = $offsetdst = 0;
3282 if ($tzoutput) {
3283 $to_gmt = true; // For backward compatibility
3284 if (is_string($tzoutput)) {
3285 if ($tzoutput == 'tzserver') {
3286 $to_gmt = false;
3287 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3288 // @phan-suppress-next-line PhanPluginRedundantAssignment
3289 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3290 // @phan-suppress-next-line PhanPluginRedundantAssignment
3291 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3292 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3293 $to_gmt = true;
3294 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3295
3296 if (class_exists('DateTimeZone')) {
3297 $user_date_tz = new DateTimeZone($offsettzstring);
3298 $user_dt = new DateTime();
3299 $user_dt->setTimezone($user_date_tz);
3300 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3301 $offsettz = $user_dt->getOffset(); // should include dst ?
3302 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3303 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3304 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3305 }
3306 }
3307 }
3308 }
3309 if (!is_object($outputlangs)) {
3310 $outputlangs = $langs;
3311 }
3312 if (!$format) {
3313 $format = 'daytextshort';
3314 }
3315
3316 // Do we have to reduce the length of date (year on 2 chars) to save space.
3317 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3318 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3319 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3320 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3321 if ($formatwithoutreduce != $format) {
3322 $format = $formatwithoutreduce;
3323 $reduceformat = 1;
3324 } // so format 'dayreduceformat' is processed like day
3325
3326 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3327 // TODO Add format daysmallyear and dayhoursmallyear
3328 if ($format == 'day') {
3329 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3330 } elseif ($format == 'hour') {
3331 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3332 } elseif ($format == 'hourduration') {
3333 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3334 } elseif ($format == 'daytext') {
3335 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3336 } elseif ($format == 'daytextshort') {
3337 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3338 } elseif ($format == 'dayhour') {
3339 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3340 } elseif ($format == 'dayhoursec') {
3341 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3342 } elseif ($format == 'dayhourtext') {
3343 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3344 } elseif ($format == 'dayhourtextshort') {
3345 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3346 } elseif ($format == 'dayhourlog') {
3347 // Format not sensitive to language
3348 $format = '%Y%m%d%H%M%S';
3349 } elseif ($format == 'dayhourlogsmall') {
3350 // Format not sensitive to language
3351 $format = '%y%m%d%H%M';
3352 } elseif ($format == 'dayhourldap') {
3353 $format = '%Y%m%d%H%M%SZ';
3354 } elseif ($format == 'dayhourxcard') {
3355 $format = '%Y%m%dT%H%M%SZ';
3356 } elseif ($format == 'dayxcard') {
3357 $format = '%Y%m%d';
3358 } elseif ($format == 'dayrfc') {
3359 $format = '%Y-%m-%d'; // DATE_RFC3339
3360 } elseif ($format == 'dayhourrfc') {
3361 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3362 } elseif ($format == 'standard') {
3363 $format = '%Y-%m-%d %H:%M:%S';
3364 }
3365
3366 if ($reduceformat) {
3367 $format = str_replace('%Y', '%y', $format);
3368 $format = str_replace('yyyy', 'yy', $format);
3369 }
3370
3371 // Clean format
3372 if (preg_match('/%b/i', $format)) { // There is some text to translate
3373 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3374 $format = str_replace('%b', '__b__', $format);
3375 $format = str_replace('%B', '__B__', $format);
3376 }
3377 if (preg_match('/%a/i', $format)) { // There is some text to translate
3378 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3379 $format = str_replace('%a', '__a__', $format);
3380 $format = str_replace('%A', '__A__', $format);
3381 }
3382
3383 // Analyze date
3384 $reg = array();
3385 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
3386 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"]));
3387 return '';
3388 } 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
3389 // This part of code should not be used anymore.
3390 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);
3391 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3392 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3393 $syear = (!empty($reg[1]) ? $reg[1] : '');
3394 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3395 $sday = (!empty($reg[3]) ? $reg[3] : '');
3396 $shour = (!empty($reg[4]) ? $reg[4] : '');
3397 $smin = (!empty($reg[5]) ? $reg[5] : '');
3398 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3399
3400 $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
3401
3402 if ($to_gmt) {
3403 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3404 } else {
3405 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3406 }
3407 $dtts = new DateTime();
3408 $dtts->setTimestamp($time);
3409 $dtts->setTimezone($tzo);
3410 $newformat = str_replace(
3411 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3412 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3413 $format
3414 );
3415 $ret = $dtts->format($newformat);
3416 $ret = str_replace(
3417 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3418 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3419 $ret
3420 );
3421 } else {
3422 // Date is a timestamps
3423 if ($time < 100000000000) { // Protection against bad date values
3424 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3425
3426 if ($to_gmt) {
3427 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3428 } else {
3429 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3430 }
3431 $dtts = new DateTime();
3432 $dtts->setTimestamp($timetouse);
3433 $dtts->setTimezone($tzo);
3434 $newformat = str_replace(
3435 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3436 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3437 $format
3438 );
3439 $ret = $dtts->format($newformat);
3440 $ret = str_replace(
3441 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3442 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3443 $ret
3444 );
3445 //var_dump($ret);exit;
3446 } else {
3447 $ret = 'Bad value '.$time.' for date';
3448 }
3449 }
3450
3451 if (preg_match('/__b__/i', $format)) {
3452 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3453
3454 if ($to_gmt) {
3455 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3456 } else {
3457 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3458 }
3459 $dtts = new DateTime();
3460 $dtts->setTimestamp($timetouse);
3461 $dtts->setTimezone($tzo);
3462 $month = (int) $dtts->format("m");
3463 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3464 if ($encodetooutput) {
3465 $monthtext = $outputlangs->transnoentities('Month'.$month);
3466 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3467 } else {
3468 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3469 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3470 }
3471 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3472 $ret = str_replace('__b__', $monthtextshort, $ret);
3473 $ret = str_replace('__B__', $monthtext, $ret);
3474 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3475 //return $ret;
3476 }
3477 if (preg_match('/__a__/i', $format)) {
3478 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3479 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3480
3481 if ($to_gmt) {
3482 $tzo = new DateTimeZone('UTC');
3483 } else {
3484 $tzo = new DateTimeZone(date_default_timezone_get());
3485 }
3486 $dtts = new DateTime();
3487 $dtts->setTimestamp($timetouse);
3488 $dtts->setTimezone($tzo);
3489 $w = $dtts->format("w");
3490 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3491
3492 $ret = str_replace('__A__', $dayweek, $ret);
3493 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3494 }
3495
3496 return $ret;
3497}
3498
3499
3520function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3521{
3522 if ($timestamp === '') {
3523 return array();
3524 }
3525
3526 $datetimeobj = new DateTime();
3527 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3528 if ($forcetimezone) {
3529 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3530 }
3531 $arrayinfo = array(
3532 'year' => ((int) date_format($datetimeobj, 'Y')),
3533 'mon' => ((int) date_format($datetimeobj, 'm')),
3534 'mday' => ((int) date_format($datetimeobj, 'd')),
3535 'wday' => ((int) date_format($datetimeobj, 'w')),
3536 'yday' => ((int) date_format($datetimeobj, 'z')),
3537 'hours' => ((int) date_format($datetimeobj, 'H')),
3538 'minutes' => ((int) date_format($datetimeobj, 'i')),
3539 'seconds' => ((int) date_format($datetimeobj, 's')),
3540 '0' => $timestamp
3541 );
3542
3543 return $arrayinfo;
3544}
3545
3567function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3568{
3569 global $conf;
3570 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3571
3572 if ($gm === 'auto') {
3573 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3574 }
3575 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3576
3577 // Clean parameters
3578 if ($hour == -1 || empty($hour)) {
3579 $hour = 0;
3580 }
3581 if ($minute == -1 || empty($minute)) {
3582 $minute = 0;
3583 }
3584 if ($second == -1 || empty($second)) {
3585 $second = 0;
3586 }
3587
3588 // Check parameters
3589 if ($check) {
3590 if (!$month || !$day) {
3591 return '';
3592 }
3593 if ($day > 31) {
3594 return '';
3595 }
3596 if ($month > 12) {
3597 return '';
3598 }
3599 if ($hour < 0 || $hour > 24) {
3600 return '';
3601 }
3602 if ($minute < 0 || $minute > 60) {
3603 return '';
3604 }
3605 if ($second < 0 || $second > 60) {
3606 return '';
3607 }
3608 }
3609
3610 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3611 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3612 $localtz = new DateTimeZone($default_timezone);
3613 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3614 // We use dol_tz_string first because it is more reliable.
3615 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3616 try {
3617 $localtz = new DateTimeZone($default_timezone);
3618 } catch (Exception $e) {
3619 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3620 $default_timezone = @date_default_timezone_get();
3621 }
3622 } elseif (strrpos($gm, "tz,") !== false) {
3623 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3624 try {
3625 $localtz = new DateTimeZone($timezone);
3626 } catch (Exception $e) {
3627 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3628 }
3629 }
3630
3631 if (empty($localtz)) {
3632 $localtz = new DateTimeZone('UTC');
3633 }
3634 //var_dump($localtz);
3635 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3636 $dt = new DateTime('now', $localtz);
3637 $dt->setDate((int) $year, (int) $month, (int) $day);
3638 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3639 $date = $dt->getTimestamp(); // should include daylight saving time
3640 //var_dump($date);
3641 return $date;
3642}
3643
3644
3655function dol_now($mode = 'auto')
3656{
3657 $ret = 0;
3658
3659 if ($mode === 'auto') {
3660 $mode = 'gmt';
3661 }
3662
3663 if ($mode == 'gmt') {
3664 $ret = time(); // Time for now at greenwich.
3665 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3666 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3667 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3668 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3669 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3670 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3671 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3672 // $ret=dol_now('gmt')+($tzsecond*3600);
3673 //}
3674 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3675 // Time for now with user timezone added
3676 //print 'time: '.time();
3677 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3678 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3679 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3680 }
3681
3682 return $ret;
3683}
3684
3685
3694function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3695{
3696 global $conf, $langs;
3697 $level = 1024;
3698
3699 if (!empty($conf->dol_optimize_smallscreen)) {
3700 $shortunit = 1;
3701 }
3702
3703 // Set value text
3704 if (empty($shortvalue) || $size < ($level * 10)) {
3705 $ret = $size;
3706 $textunitshort = $langs->trans("b");
3707 $textunitlong = $langs->trans("Bytes");
3708 } else {
3709 $ret = round($size / $level, 0);
3710 $textunitshort = $langs->trans("Kb");
3711 $textunitlong = $langs->trans("KiloBytes");
3712 }
3713 // Use long or short text unit
3714 if (empty($shortunit)) {
3715 $ret .= ' '.$textunitlong;
3716 } else {
3717 $ret .= ' '.$textunitshort;
3718 }
3719
3720 return $ret;
3721}
3722
3733function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3734{
3735 global $langs;
3736
3737 if (empty($url)) {
3738 return '';
3739 }
3740
3741 $linkstart = '<a href="';
3742 if (!preg_match('/^http/i', $url)) {
3743 $linkstart .= 'http://';
3744 }
3745 $linkstart .= $url;
3746 $linkstart .= '"';
3747 if ($target) {
3748 $linkstart .= ' target="'.$target.'"';
3749 }
3750 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3751 $linkstart .= '>';
3752
3753 $link = '';
3754 if (!preg_match('/^http/i', $url)) {
3755 $link .= 'http://';
3756 }
3757 $link .= dol_trunc($url, $max);
3758
3759 $linkend = '</a>';
3760
3761 if ($morecss == 'float') { // deprecated
3762 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3763 } else {
3764 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3765 }
3766}
3767
3780function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3781{
3782 global $user, $langs, $hookmanager;
3783
3784 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3785 //$showinvalid = 1; $email = 'rrrrr';
3786
3787 $newemail = dol_escape_htmltag($email);
3788
3789 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3790 $withpicto = 0;
3791 }
3792
3793 if (empty($email)) {
3794 return '&nbsp;';
3795 }
3796
3797 if (!empty($addlink)) {
3798 $newemail = '<a class="paddingrightonly" style="text-overflow: ellipsis;" href="';
3799 if (!preg_match('/^mailto:/i', $email)) {
3800 $newemail .= 'mailto:';
3801 }
3802 $newemail .= $email;
3803 $newemail .= '">';
3804
3805 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3806
3807 $newemail .= dol_trunc($email, $max);
3808 $newemail .= '</a>';
3809 if ($showinvalid && !isValidEmail($email)) {
3810 $langs->load("errors");
3811 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3812 }
3813
3814 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3815 $type = 'AC_EMAIL';
3816 $linktoaddaction = '';
3817 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3818 $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>';
3819 }
3820 if ($linktoaddaction) {
3821 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3822 }
3823 }
3824 } else {
3825 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3826
3827 if ($showinvalid && !isValidEmail($email)) {
3828 $langs->load("errors");
3829 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3830 }
3831 }
3832
3833 //$rep = '<div class="nospan" style="margin-right: 10px">';
3834 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3835 //$rep .= '</div>';
3836 $rep = $newemail;
3837
3838 if ($hookmanager) {
3839 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3840
3841 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3842 if ($reshook > 0) {
3843 $rep = '';
3844 }
3845 $rep .= $hookmanager->resPrint;
3846 }
3847
3848 return $rep;
3849}
3850
3856function getArrayOfSocialNetworks()
3857{
3858 global $conf, $db;
3859
3860 $socialnetworks = array();
3861 // Enable caching of array
3862 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3863 $cachekey = 'socialnetworks_' . $conf->entity;
3864 $dataretrieved = dol_getcache($cachekey);
3865 if (!is_null($dataretrieved)) {
3866 $socialnetworks = $dataretrieved;
3867 } else {
3868 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3869 $sql .= " WHERE entity=".$conf->entity;
3870 $resql = $db->query($sql);
3871 if ($resql) {
3872 while ($obj = $db->fetch_object($resql)) {
3873 $socialnetworks[$obj->code] = array(
3874 'rowid' => $obj->rowid,
3875 'label' => $obj->label,
3876 'url' => $obj->url,
3877 'icon' => $obj->icon,
3878 'active' => $obj->active,
3879 );
3880 }
3881 }
3882 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3883 }
3884 return $socialnetworks;
3885}
3886
3897function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3898{
3899 global $user, $langs;
3900
3901 $htmllink = $value;
3902
3903 if (empty($value)) {
3904 return '&nbsp;';
3905 }
3906
3907 if (!empty($type)) {
3908 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3909 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3910 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3911 if ($type == 'skype') {
3912 $htmllink .= dol_escape_htmltag($value);
3913 $htmllink .= '&nbsp; <a href="skype:';
3914 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3915 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3916 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3917 $htmllink .= '</a><a href="skype:';
3918 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3919 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3920 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3921 $htmllink .= '</a>';
3922 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3923 $addlink = 'AC_SKYPE';
3924 $link = '';
3925 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
3926 $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>';
3927 }
3928 $htmllink .= ($link ? ' '.$link : '');
3929 }
3930 } else {
3931 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
3932 if (getDolGlobalString($networkconstname)) {
3933 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
3934 if (preg_match('/^https?:\/\//i', $link)) {
3935 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3936 } else {
3937 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3938 }
3939 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
3940 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3941 if ($tmpvirginurl) {
3942 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3943 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3944
3945 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3946 if ($tmpvirginurl3) {
3947 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3948 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3949 }
3950
3951 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3952 if ($tmpvirginurl2) {
3953 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3954 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3955 }
3956 }
3957 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3958 if (preg_match('/^https?:\/\//i', $link)) {
3959 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3960 } else {
3961 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3962 }
3963 } else {
3964 $htmllink .= dol_escape_htmltag($value);
3965 }
3966 }
3967 $htmllink .= '</div>';
3968 } else {
3969 $langs->load("errors");
3970 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3971 }
3972 return $htmllink;
3973}
3974
3984function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
3985{
3986 global $mysoc;
3987
3988 if (empty($profID) || empty($profIDtype)) {
3989 return '';
3990 }
3991 if (empty($countrycode)) {
3992 $countrycode = $mysoc->country_code;
3993 }
3994 $newProfID = $profID;
3995 $id = substr($profIDtype, -1);
3996 $ret = '';
3997 if (strtoupper($countrycode) == 'FR') {
3998 // France
3999 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4000
4001 if ($id == 1 && dol_strlen($newProfID) == 9) {
4002 // SIREN (ex: 123 123 123)
4003 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4004 }
4005 if ($id == 2 && dol_strlen($newProfID) == 14) {
4006 // SIRET (ex: 123 123 123 12345)
4007 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4008 }
4009 if ($id == 3 && dol_strlen($newProfID) == 5) {
4010 // NAF/APE (ex: 69.20Z)
4011 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4012 }
4013 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4014 // TVA intracommunautaire (ex: FR12 123 123 123)
4015 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4016 }
4017 }
4018 if (!empty($addcpButton)) {
4019 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4020 } else {
4021 $ret = $newProfID;
4022 }
4023 return $ret;
4024}
4025
4041function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = '')
4042{
4043 global $conf, $user, $langs, $mysoc, $hookmanager;
4044
4045 // Clean phone parameter
4046 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4047 if (empty($phone)) {
4048 return '';
4049 }
4050 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4051 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4052 }
4053 if (empty($countrycode) && is_object($mysoc)) {
4054 $countrycode = $mysoc->country_code;
4055 }
4056
4057 // Short format for small screens
4058 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4059 $separ = '';
4060 }
4061
4062 $newphone = $phone;
4063 $newphonewa = $phone;
4064 if (strtoupper($countrycode) == "FR") {
4065 // France
4066 if (dol_strlen($phone) == 10) {
4067 $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);
4068 } elseif (dol_strlen($phone) == 7) {
4069 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4070 } elseif (dol_strlen($phone) == 9) {
4071 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4072 } elseif (dol_strlen($phone) == 11) {
4073 $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);
4074 } elseif (dol_strlen($phone) == 12) {
4075 $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);
4076 } elseif (dol_strlen($phone) == 13) {
4077 $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);
4078 }
4079 } elseif (strtoupper($countrycode) == "CA") {
4080 if (dol_strlen($phone) == 10) {
4081 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4082 }
4083 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4084 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4085 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4086 }
4087 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4088 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4089 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4090 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4091 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4092 }
4093 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4094 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4095 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4096 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4097 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4098 }
4099 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4100 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4101 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4102 }
4103 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4104 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4105 $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);
4106 }
4107 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4108 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4109 $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);
4110 }
4111 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4112 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4113 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4114 }
4115 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4116 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4117 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4118 }
4119 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4120 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4121 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4122 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4123 $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);
4124 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4125 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4126 }
4127 } elseif (strtoupper($countrycode) == "ML") {//Mali
4128 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4129 $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);
4130 }
4131 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4132 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4133 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4134 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4135 $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);
4136 }
4137 } elseif (strtoupper($countrycode) == "MU") {
4138 //Maurice
4139 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4140 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4141 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4142 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4143 }
4144 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4145 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4146 $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);
4147 }
4148 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4149 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4150 $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);
4151 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4152 $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);
4153 }
4154 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4155 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4156 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4157 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4158 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4159 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4160 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4161 }
4162 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4163 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4164 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4165 }
4166 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4167 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4168 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4169 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4170 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4171 }
4172 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4173 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4174 $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);
4175 }
4176 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4177 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4178 $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);
4179 }
4180 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4181 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4182 $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);
4183 }
4184 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4185 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4186 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4187 }
4188 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4189 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4190 $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);
4191 }
4192 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4193 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4194 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4195 }
4196 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4197 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4198 $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);
4199 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4200 $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);
4201 }
4202 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4203 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4204 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4205 }
4206 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4207 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4208 $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);
4209 }
4210 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4211 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4212 $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);
4213 }
4214 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4215 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4216 $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);
4217 }
4218 } elseif (strtoupper($countrycode) == "IT") {//Italie
4219 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4220 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4221 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4222 $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);
4223 }
4224 } elseif (strtoupper($countrycode) == "AU") {
4225 //Australie
4226 if (dol_strlen($phone) == 12) {
4227 //ex: +61_A_BCDE_FGHI
4228 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4229 }
4230 } elseif (strtoupper($countrycode) == "LU") {
4231 // Luxembourg
4232 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4233 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4234 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4235 $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);
4236 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4237 $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);
4238 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4239 $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);
4240 }
4241 } elseif (strtoupper($countrycode) == "PE") {
4242 // Peru
4243 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4244 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4245 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4246 $newphonewa = '+51'.$newphone;
4247 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4248 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4249 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4250 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4251 $newphonewa = $newphone;
4252 $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);
4253 }
4254 }
4255
4256 $newphoneastart = $newphoneaend = '';
4257 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4258 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
4259 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4260 $newphoneaend .= '</a>';
4261 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4262 if (empty($user->clicktodial_loaded)) {
4263 $user->fetch_clicktodial();
4264 }
4265
4266 // Define urlmask
4267 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4268 if (!empty($user->clicktodial_url)) {
4269 $urlmask = $user->clicktodial_url;
4270 }
4271
4272 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4273 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4274 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4275 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4276 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4277 // Those lines are for substitution
4278 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4279 '__PHONETO__' => urlencode($phone),
4280 '__LOGIN__' => $clicktodial_login,
4281 '__PASS__' => $clicktodial_password);
4282 $url = make_substitutions($url, $substitarray);
4283 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4284 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4285 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4286 $newphoneaend = '</a>';
4287 } else {
4288 // Old method
4289 $newphoneastart = '<a href="'.$url.'"';
4290 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4291 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4292 }
4293 $newphoneastart .= '>';
4294 $newphoneaend .= '</a>';
4295 }
4296 }
4297
4298 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4299 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4300 $type = 'AC_TEL';
4301 $addlinktoagenda = '';
4302 if ($addlink == 'AC_FAX') {
4303 $type = 'AC_FAX';
4304 }
4305 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4306 $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>';
4307 }
4308 if ($addlinktoagenda) {
4309 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4310 }
4311 }
4312 }
4313
4314 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4315 // Link to Whatsapp
4316 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4317 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4318 }
4319
4320 if (empty($titlealt)) {
4321 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4322 }
4323 $rep = '';
4324
4325 if ($hookmanager) {
4326 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4327 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4328 $rep .= $hookmanager->resPrint;
4329 }
4330 if (empty($reshook)) {
4331 $picto = '';
4332 if ($withpicto) {
4333 if ($withpicto == 'fax') {
4334 $picto = 'phoning_fax';
4335 } elseif ($withpicto == 'phone') {
4336 $picto = 'phoning';
4337 } elseif ($withpicto == 'mobile') {
4338 $picto = 'phoning_mobile';
4339 } else {
4340 $picto = '';
4341 }
4342 }
4343 if ($adddivfloat == 1) {
4344 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">';
4345 } elseif (empty($adddivfloat)) {
4346 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').' style="margin-right: 10px;">';
4347 }
4348
4349 $rep .= $newphoneastart;
4350 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4351 if ($separ != 'hidenum') {
4352 $rep .= ($withpicto ? ' ' : '').$newphone;
4353 }
4354 $rep .= $newphoneaend;
4355
4356 if ($adddivfloat == 1) {
4357 $rep .= '</div>';
4358 } elseif (empty($adddivfloat)) {
4359 $rep .= '</span>';
4360 }
4361 }
4362
4363 return $rep;
4364}
4365
4373function dol_print_ip($ip, $mode = 0)
4374{
4375 global $langs;
4376
4377 $ret = '';
4378
4379 if (empty($mode)) {
4380 $ret .= $ip;
4381 }
4382
4383 if ($mode != 2) {
4384 $countrycode = dolGetCountryCodeFromIp($ip);
4385 if ($countrycode) { // If success, countrycode is us, fr, ...
4386 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4387 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4388 } else {
4389 $ret .= ' ('.$countrycode.')';
4390 }
4391 } else {
4392 // Nothing
4393 }
4394 }
4395
4396 return $ret;
4397}
4398
4407function getUserRemoteIP()
4408{
4409 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4410 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4411 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4412 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4413 } else {
4414 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4415 }
4416 } else {
4417 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4418 }
4419 } else {
4420 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4421 }
4422 return $ip;
4423}
4424
4433function isHTTPS()
4434{
4435 $isSecure = false;
4436 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4437 $isSecure = true;
4438 } 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') {
4439 $isSecure = true;
4440 }
4441 return $isSecure;
4442}
4443
4450function dolGetCountryCodeFromIp($ip)
4451{
4452 global $conf;
4453
4454 $countrycode = '';
4455
4456 if (!empty($conf->geoipmaxmind->enabled)) {
4457 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4458 //$ip='24.24.24.24';
4459 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4460 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4461 $geoip = new DolGeoIP('country', $datafile);
4462 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4463 $countrycode = $geoip->getCountryCodeFromIP($ip);
4464 }
4465
4466 return $countrycode;
4467}
4468
4469
4476function dol_user_country()
4477{
4478 global $conf, $langs, $user;
4479
4480 //$ret=$user->xxx;
4481 $ret = '';
4482 if (!empty($conf->geoipmaxmind->enabled)) {
4483 $ip = getUserRemoteIP();
4484 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4485 //$ip='24.24.24.24';
4486 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4487 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4488 $geoip = new DolGeoIP('country', $datafile);
4489 $countrycode = $geoip->getCountryCodeFromIP($ip);
4490 $ret = $countrycode;
4491 }
4492 return $ret;
4493}
4494
4507function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4508{
4509 global $conf, $user, $langs, $hookmanager;
4510
4511 $out = '';
4512
4513 if ($address) {
4514 if ($hookmanager) {
4515 $parameters = array('element' => $element, 'id' => $id);
4516 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4517 $out .= $hookmanager->resPrint;
4518 }
4519 if (empty($reshook)) {
4520 if (empty($charfornl)) {
4521 $out .= nl2br($address);
4522 } else {
4523 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4524 }
4525
4526 // TODO Remove this block, we can add this using the hook now
4527 $showgmap = $showomap = 0;
4528 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4529 $showgmap = 1;
4530 }
4531 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4532 $showgmap = 1;
4533 }
4534 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4535 $showgmap = 1;
4536 }
4537 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4538 $showgmap = 1;
4539 }
4540 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4541 $showomap = 1;
4542 }
4543 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4544 $showomap = 1;
4545 }
4546 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4547 $showomap = 1;
4548 }
4549 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4550 $showomap = 1;
4551 }
4552 if ($showgmap) {
4553 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4554 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4555 }
4556 if ($showomap) {
4557 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4558 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4559 }
4560 }
4561 }
4562 if ($noprint) {
4563 return $out;
4564 } else {
4565 print $out;
4566 }
4567}
4568
4569
4579function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4580{
4581 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4582 return true;
4583 }
4584 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4585 return true;
4586 }
4587 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4588 return true;
4589 }
4590
4591 return false;
4592}
4593
4603function isValidMXRecord($domain)
4604{
4605 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4606 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4607 return 0;
4608 }
4609 if (function_exists('getmxrr')) {
4610 $mxhosts = array();
4611 $weight = array();
4612 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4613 if (count($mxhosts) > 1) {
4614 return 1;
4615 }
4616 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4617 return 1;
4618 }
4619
4620 return 0;
4621 }
4622 }
4623
4624 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4625 return -1;
4626}
4627
4635function isValidPhone($phone)
4636{
4637 return true;
4638}
4639
4640
4650function dolGetFirstLetters($s, $nbofchar = 1)
4651{
4652 $ret = '';
4653 $tmparray = explode(' ', $s);
4654 foreach ($tmparray as $tmps) {
4655 $ret .= dol_substr($tmps, 0, $nbofchar);
4656 }
4657
4658 return $ret;
4659}
4660
4661
4669function dol_strlen($string, $stringencoding = 'UTF-8')
4670{
4671 if (is_null($string)) {
4672 return 0;
4673 }
4674
4675 if (function_exists('mb_strlen')) {
4676 return mb_strlen($string, $stringencoding);
4677 } else {
4678 return strlen($string);
4679 }
4680}
4681
4692function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4693{
4694 global $langs;
4695
4696 if (empty($stringencoding)) {
4697 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4698 }
4699
4700 $ret = '';
4701 if (empty($trunconbytes)) {
4702 if (function_exists('mb_substr')) {
4703 $ret = mb_substr($string, $start, $length, $stringencoding);
4704 } else {
4705 $ret = substr($string, $start, $length);
4706 }
4707 } else {
4708 if (function_exists('mb_strcut')) {
4709 $ret = mb_strcut($string, $start, $length, $stringencoding);
4710 } else {
4711 $ret = substr($string, $start, $length);
4712 }
4713 }
4714 return $ret;
4715}
4716
4717
4731function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4732{
4733 global $conf;
4734
4735 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4736 return $string;
4737 }
4738
4739 if (empty($stringencoding)) {
4740 $stringencoding = 'UTF-8';
4741 }
4742 // reduce for small screen
4743 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4744 $size = round($size / 3);
4745 }
4746
4747 // We go always here
4748 if ($trunc == 'right') {
4749 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4750 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4751 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4752 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4753 } else {
4754 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4755 return $string;
4756 }
4757 } elseif ($trunc == 'middle') {
4758 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4759 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4760 $size1 = round($size / 2);
4761 $size2 = round($size / 2);
4762 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4763 } else {
4764 return $string;
4765 }
4766 } elseif ($trunc == 'left') {
4767 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4768 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4769 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4770 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4771 } else {
4772 return $string;
4773 }
4774 } elseif ($trunc == 'wrap') {
4775 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4776 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4777 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4778 } else {
4779 return $string;
4780 }
4781 } else {
4782 return 'BadParam3CallingDolTrunc';
4783 }
4784}
4785
4793function getPictoForType($key, $morecss = '')
4794{
4795 // Set array with type -> picto
4796 $type2picto = array(
4797 'varchar' => 'font',
4798 'text' => 'font',
4799 'html' => 'code',
4800 'int' => 'sort-numeric-down',
4801 'double' => 'sort-numeric-down',
4802 'price' => 'currency',
4803 'pricecy' => 'multicurrency',
4804 'password' => 'key',
4805 'boolean' => 'check-square',
4806 'date' => 'calendar',
4807 'datetime' => 'calendar',
4808 'phone' => 'phone',
4809 'mail' => 'email',
4810 'url' => 'url',
4811 'ip' => 'country',
4812 'select' => 'list',
4813 'sellist' => 'list',
4814 'radio' => 'check-circle',
4815 'checkbox' => 'list',
4816 'chkbxlst' => 'list',
4817 'link' => 'link',
4818 'icon' => "question",
4819 'point' => "country",
4820 'multipts' => 'country',
4821 'linestrg' => "country",
4822 'polygon' => "country",
4823 'separate' => 'minus'
4824 );
4825
4826 if (!empty($type2picto[$key])) {
4827 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4828 }
4829
4830 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4831}
4832
4833
4855function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4856{
4857 global $conf;
4858
4859 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4860 $url = DOL_URL_ROOT;
4861 $theme = isset($conf->theme) ? $conf->theme : null;
4862 $path = 'theme/'.$theme;
4863 if (empty($picto)) {
4864 $picto = 'generic';
4865 }
4866
4867 // Define fullpathpicto to use into src
4868 if ($pictoisfullpath) {
4869 // Clean parameters
4870 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4871 $picto .= '.png';
4872 }
4873 $fullpathpicto = $picto;
4874 $reg = array();
4875 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4876 $morecss .= ($morecss ? ' ' : '').$reg[1];
4877 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4878 }
4879 } else {
4880 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
4881 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4882 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4883
4884 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
4885 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
4886 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4887 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4888
4889 // Compatibility with old fontawesome versions
4890 if ($pictowithouttext == 'file-o') {
4891 $pictowithouttext = 'file';
4892 }
4893
4894 $pictowithouttextarray = explode('_', $pictowithouttext);
4895 $marginleftonlyshort = 0;
4896
4897 if (!empty($pictowithouttextarray[1])) {
4898 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4899 $fakey = 'fa-'.$pictowithouttextarray[0];
4900 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
4901 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4902 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4903 } else {
4904 $fakey = 'fa-'.$pictowithouttext;
4905 $faprefix = 'fas';
4906 $facolor = '';
4907 $fasize = '';
4908 }
4909
4910 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4911 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4912 $morestyle = '';
4913 $reg = array();
4914 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4915 $morecss .= ($morecss ? ' ' : '').$reg[1];
4916 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4917 }
4918 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4919 $morestyle = $reg[1];
4920 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4921 }
4922 $moreatt = trim($moreatt);
4923
4924 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4925 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4926 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4927 $enabledisablehtml .= $titlealt;
4928 }*/
4929 $enabledisablehtml .= '</span>';
4930
4931 return $enabledisablehtml;
4932 }
4933
4934 if (empty($srconly) && in_array($pictowithouttext, array(
4935 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4936 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
4937 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
4938 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4939 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4940 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
4941 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
4942 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
4943 'commercial', 'companies',
4944 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4945 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4946 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
4947 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4948 'hands-helping', 'help', 'holiday',
4949 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4950 'key', 'knowledgemanagement',
4951 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4952 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4953 'off', 'on', 'order',
4954 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4955 'stock', 'resize', 'service', 'stats',
4956 'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_warning', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
4957 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4958 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4959 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4960 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4961 'technic', 'ticket',
4962 'error', 'warning',
4963 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4964 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4965 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
4966 'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4967 'conferenceorbooth', 'eventorganization',
4968 'stamp', 'signature',
4969 'webportal'
4970 ))) {
4971 $fakey = $pictowithouttext;
4972 $facolor = '';
4973 $fasize = '';
4974 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
4975 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'))) {
4976 $fa = 'far';
4977 }
4978 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4979 $fa = 'fab';
4980 }
4981
4982 $arrayconvpictotofa = array(
4983 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
4984 'asset' => 'money-check-alt', 'autofill' => 'fill',
4985 'bank_account' => 'university',
4986 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
4987 'bookcal' => 'calendar-check',
4988 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
4989 'bom' => 'shapes',
4990 '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',
4991 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
4992 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
4993 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
4994 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
4995 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
4996 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
4997 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
4998 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
4999 'generic' => 'file', 'holiday' => 'umbrella-beach',
5000 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5001 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5002 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5003 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5004 'sign-out' => 'sign-out-alt',
5005 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
5006 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5007 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5008 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5009 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5010 'other' => 'square',
5011 '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',
5012 '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',
5013 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5014 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5015 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5016 'service' => 'concierge-bell',
5017 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5018 'status' => 'stop-circle',
5019 'stripe' => 'stripe-s', 'supplier' => 'building',
5020 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5021 'title_agenda' => 'calendar-alt',
5022 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5023 'jabber' => 'comment-o',
5024 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5025 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5026 'webportal' => 'door-open'
5027 );
5028 if ($conf->currency == 'EUR') {
5029 $arrayconvpictotofa['currency'] = 'euro-sign';
5030 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5031 } else {
5032 $arrayconvpictotofa['currency'] = 'dollar-sign';
5033 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5034 }
5035 if ($pictowithouttext == 'off') {
5036 $fakey = 'fa-square';
5037 $fasize = '1.3em';
5038 } elseif ($pictowithouttext == 'on') {
5039 $fakey = 'fa-check-square';
5040 $fasize = '1.3em';
5041 } elseif ($pictowithouttext == 'listlight') {
5042 $fakey = 'fa-download';
5043 $marginleftonlyshort = 1;
5044 } elseif ($pictowithouttext == 'printer') {
5045 $fakey = 'fa-print';
5046 $fasize = '1.2em';
5047 } elseif ($pictowithouttext == 'note') {
5048 $fakey = 'fa-sticky-note';
5049 $marginleftonlyshort = 1;
5050 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5051 $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');
5052 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5053 if (preg_match('/selected/', $pictowithouttext)) {
5054 $facolor = '#888';
5055 }
5056 $marginleftonlyshort = 1;
5057 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5058 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5059 } else {
5060 $fakey = 'fa-'.$pictowithouttext;
5061 }
5062
5063 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5064 $morecss .= ' em092';
5065 }
5066 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5067 $morecss .= ' em088';
5068 }
5069 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5070 $morecss .= ' em080';
5071 }
5072
5073 // Define $marginleftonlyshort
5074 $arrayconvpictotomarginleftonly = array(
5075 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5076 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
5077 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5078 );
5079 if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
5080 $marginleftonlyshort = 0;
5081 }
5082
5083 // Add CSS
5084 $arrayconvpictotomorcess = array(
5085 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5086 'bank_account' => 'infobox-bank_account',
5087 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5088 'bookcal' => 'infobox-action',
5089 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5090 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5091 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5092 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5093 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5094 'incoterm' => 'infobox-supplier_proposal',
5095 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5096 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5097 'order' => 'infobox-commande',
5098 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5099 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5100 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5101 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5102 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5103 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5104 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5105 'resource' => 'infobox-action',
5106 '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',
5107 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5108 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5109 'vat' => 'infobox-bank_account',
5110 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5111 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5112 );
5113 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5114 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5115 }
5116
5117 // Define $color
5118 $arrayconvpictotocolor = array(
5119 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5120 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5121 'dynamicprice' => '#a69944',
5122 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5123 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5124 'lock' => '#ddd', 'lot' => '#a69944',
5125 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5126 'other' => '#ddd', 'world' => '#986c6a',
5127 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5128 //'shipment'=>'#a69944',
5129 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5130 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5131 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5132 'website' => '#304', 'workstation' => '#a69944'
5133 );
5134 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5135 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5136 }
5137
5138 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5139 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5140 $morestyle = '';
5141 $reg = array();
5142 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5143 $morecss .= ($morecss ? ' ' : '').$reg[1];
5144 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5145 }
5146 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5147 $morestyle = $reg[1];
5148 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5149 }
5150 $moreatt = trim($moreatt);
5151
5152 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5153 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5154 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5155 $enabledisablehtml .= $titlealt;
5156 }*/
5157 $enabledisablehtml .= '</span>';
5158
5159 return $enabledisablehtml;
5160 }
5161
5162 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5163 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5164 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5165 $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
5166 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5167 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5168 }
5169
5170 // If we ask an image into $url/$mymodule/img (instead of default path)
5171 $regs = array();
5172 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5173 $picto = $regs[1];
5174 $path = $regs[2]; // $path is $mymodule
5175 }
5176
5177 // Clean parameters
5178 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5179 $picto .= '.png';
5180 }
5181 // If alt path are defined, define url where img file is, according to physical path
5182 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5183 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5184 if ($type == 'main') {
5185 continue;
5186 }
5187 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5188 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5189 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5190 break;
5191 }
5192 }
5193
5194 // $url is '' or '/custom', $path is current theme or
5195 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5196 }
5197
5198 if ($srconly) {
5199 return $fullpathpicto;
5200 }
5201
5202 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5203 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
5204}
5205
5219function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5220{
5221 if (strpos($picto, '^') === 0) {
5222 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5223 } else {
5224 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5225 }
5226}
5227
5239function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5240{
5241 global $conf;
5242
5243 if (is_numeric($picto)) {
5244 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5245 //$picto = $leveltopicto[$picto];
5246 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5247 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5248 $picto .= '.png';
5249 }
5250
5251 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5252
5253 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5254}
5255
5267function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5268{
5269 global $conf;
5270
5271 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5272 $picto .= '.png';
5273 }
5274
5275 if ($pictoisfullpath) {
5276 $path = $picto;
5277 } else {
5278 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5279
5280 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5281 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5282
5283 if (file_exists($themepath)) {
5284 $path = $themepath;
5285 }
5286 }
5287 }
5288
5289 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5290}
5291
5305function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5306{
5307 global $langs;
5308
5309 if (empty($titlealt) || $titlealt == 'default') {
5310 if ($numaction == '-1' || $numaction == 'ST_NO') {
5311 $numaction = -1;
5312 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5313 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5314 $numaction = 0;
5315 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5316 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5317 $numaction = 1;
5318 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5319 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5320 $numaction = 2;
5321 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5322 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5323 $numaction = 3;
5324 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5325 } else {
5326 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5327 $numaction = 0;
5328 }
5329 }
5330 if (!is_numeric($numaction)) {
5331 $numaction = 0;
5332 }
5333
5334 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5335}
5336
5344function img_pdf($titlealt = 'default', $size = 3)
5345{
5346 global $langs;
5347
5348 if ($titlealt == 'default') {
5349 $titlealt = $langs->trans('Show');
5350 }
5351
5352 return img_picto($titlealt, 'pdf'.$size.'.png');
5353}
5354
5362function img_edit_add($titlealt = 'default', $other = '')
5363{
5364 global $langs;
5365
5366 if ($titlealt == 'default') {
5367 $titlealt = $langs->trans('Add');
5368 }
5369
5370 return img_picto($titlealt, 'edit_add.png', $other);
5371}
5379function img_edit_remove($titlealt = 'default', $other = '')
5380{
5381 global $langs;
5382
5383 if ($titlealt == 'default') {
5384 $titlealt = $langs->trans('Remove');
5385 }
5386
5387 return img_picto($titlealt, 'edit_remove.png', $other);
5388}
5389
5398function img_edit($titlealt = 'default', $float = 0, $other = '')
5399{
5400 global $langs;
5401
5402 if ($titlealt == 'default') {
5403 $titlealt = $langs->trans('Modify');
5404 }
5405
5406 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5407}
5408
5417function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5418{
5419 global $langs;
5420
5421 if ($titlealt == 'default') {
5422 $titlealt = $langs->trans('View');
5423 }
5424
5425 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5426
5427 return img_picto($titlealt, 'eye', $moreatt);
5428}
5429
5438function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5439{
5440 global $langs;
5441
5442 if ($titlealt == 'default') {
5443 $titlealt = $langs->trans('Delete');
5444 }
5445
5446 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5447}
5448
5456function img_printer($titlealt = "default", $other = '')
5457{
5458 global $langs;
5459 if ($titlealt == "default") {
5460 $titlealt = $langs->trans("Print");
5461 }
5462 return img_picto($titlealt, 'printer.png', $other);
5463}
5464
5472function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5473{
5474 global $langs;
5475
5476 if ($titlealt == 'default') {
5477 $titlealt = $langs->trans('Split');
5478 }
5479
5480 return img_picto($titlealt, 'split.png', $other);
5481}
5482
5490function img_help($usehelpcursor = 1, $usealttitle = 1)
5491{
5492 global $langs;
5493
5494 if ($usealttitle) {
5495 if (is_string($usealttitle)) {
5496 $usealttitle = dol_escape_htmltag($usealttitle);
5497 } else {
5498 $usealttitle = $langs->trans('Info');
5499 }
5500 }
5501
5502 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5503}
5504
5511function img_info($titlealt = 'default')
5512{
5513 global $langs;
5514
5515 if ($titlealt == 'default') {
5516 $titlealt = $langs->trans('Informations');
5517 }
5518
5519 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5520}
5521
5530function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5531{
5532 global $langs;
5533
5534 if ($titlealt == 'default') {
5535 $titlealt = $langs->trans('Warning');
5536 }
5537
5538 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5539 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5540}
5541
5548function img_error($titlealt = 'default')
5549{
5550 global $langs;
5551
5552 if ($titlealt == 'default') {
5553 $titlealt = $langs->trans('Error');
5554 }
5555
5556 return img_picto($titlealt, 'error.png');
5557}
5558
5566function img_next($titlealt = 'default', $moreatt = '')
5567{
5568 global $langs;
5569
5570 if ($titlealt == 'default') {
5571 $titlealt = $langs->trans('Next');
5572 }
5573
5574 //return img_picto($titlealt, 'next.png', $moreatt);
5575 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5576}
5577
5585function img_previous($titlealt = 'default', $moreatt = '')
5586{
5587 global $langs;
5588
5589 if ($titlealt == 'default') {
5590 $titlealt = $langs->trans('Previous');
5591 }
5592
5593 //return img_picto($titlealt, 'previous.png', $moreatt);
5594 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5595}
5596
5605function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5606{
5607 global $langs;
5608
5609 if ($titlealt == 'default') {
5610 $titlealt = $langs->trans('Down');
5611 }
5612
5613 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5614}
5615
5624function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5625{
5626 global $langs;
5627
5628 if ($titlealt == 'default') {
5629 $titlealt = $langs->trans('Up');
5630 }
5631
5632 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5633}
5634
5643function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5644{
5645 global $langs;
5646
5647 if ($titlealt == 'default') {
5648 $titlealt = $langs->trans('Left');
5649 }
5650
5651 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5652}
5653
5662function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5663{
5664 global $langs;
5665
5666 if ($titlealt == 'default') {
5667 $titlealt = $langs->trans('Right');
5668 }
5669
5670 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5671}
5672
5680function img_allow($allow, $titlealt = 'default')
5681{
5682 global $langs;
5683
5684 if ($titlealt == 'default') {
5685 $titlealt = $langs->trans('Active');
5686 }
5687
5688 if ($allow == 1) {
5689 return img_picto($titlealt, 'tick.png');
5690 }
5691
5692 return '-';
5693}
5694
5702function img_credit_card($brand, $morecss = null)
5703{
5704 if (is_null($morecss)) {
5705 $morecss = 'fa-2x';
5706 }
5707
5708 if ($brand == 'visa' || $brand == 'Visa') {
5709 $brand = 'cc-visa';
5710 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5711 $brand = 'cc-mastercard';
5712 } elseif ($brand == 'amex' || $brand == 'American Express') {
5713 $brand = 'cc-amex';
5714 } elseif ($brand == 'discover' || $brand == 'Discover') {
5715 $brand = 'cc-discover';
5716 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5717 $brand = 'cc-jcb';
5718 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5719 $brand = 'cc-diners-club';
5720 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5721 $brand = 'credit-card';
5722 }
5723
5724 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5725}
5726
5735function img_mime($file, $titlealt = '', $morecss = '')
5736{
5737 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5738
5739 $mimetype = dol_mimetype($file, '', 1);
5740 $mimeimg = dol_mimetype($file, '', 2);
5741 $mimefa = dol_mimetype($file, '', 4);
5742
5743 if (empty($titlealt)) {
5744 $titlealt = 'Mime type: '.$mimetype;
5745 }
5746
5747 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5748 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5749}
5750
5751
5759function img_search($titlealt = 'default', $other = '')
5760{
5761 global $langs;
5762
5763 if ($titlealt == 'default') {
5764 $titlealt = $langs->trans('Search');
5765 }
5766
5767 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5768
5769 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5770 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5771
5772 return $input;
5773}
5774
5782function img_searchclear($titlealt = 'default', $other = '')
5783{
5784 global $langs;
5785
5786 if ($titlealt == 'default') {
5787 $titlealt = $langs->trans('Search');
5788 }
5789
5790 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5791
5792 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5793 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5794
5795 return $input;
5796}
5797
5810function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5811{
5812 global $conf, $langs;
5813
5814 if ($infoonimgalt) {
5815 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5816 } else {
5817 if (empty($conf->use_javascript_ajax)) {
5818 $textfordropdown = '';
5819 }
5820
5821 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5822 $fa = 'info-circle';
5823 if ($picto == 'warning') {
5824 $fa = 'exclamation-triangle';
5825 }
5826 $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> ';
5827 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5828 $result .= ($nodiv ? '' : '</div>');
5829
5830 if ($textfordropdown) {
5831 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5832 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5833 jQuery(document).ready(function() {
5834 jQuery(".'.$class.'text").click(function() {
5835 console.log("toggle text");
5836 jQuery(".'.$class.'").toggle();
5837 });
5838 });
5839 </script>';
5840
5841 $result = $tmpresult.$result;
5842 }
5843 }
5844
5845 return $result;
5846}
5847
5848
5860function dol_print_error($db = null, $error = '', $errors = null)
5861{
5862 global $conf, $langs, $user, $argv;
5863 global $dolibarr_main_prod;
5864
5865 $out = '';
5866 $syslog = '';
5867
5868 // If error occurs before the $lang object was loaded
5869 if (!$langs) {
5870 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5871 $langs = new Translate('', $conf);
5872 $langs->load("main");
5873 }
5874
5875 // Load translation files required by the error messages
5876 $langs->loadLangs(array('main', 'errors'));
5877
5878 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5879 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5880 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5881 $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";
5882 }
5883 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5884
5885 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5886 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5887 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5888 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5889 }
5890 if ($user instanceof User) {
5891 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
5892 }
5893 if (function_exists("phpversion")) {
5894 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5895 }
5896 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5897 if (function_exists("php_uname")) {
5898 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5899 }
5900 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5901 $out .= "<br>\n";
5902 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5903 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5904 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5905 $out .= "<br>\n";
5906 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5907 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5908 } else { // Mode CLI
5909 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5910 $syslog .= "pid=".dol_getmypid();
5911 }
5912
5913 if (!empty($conf->modules)) {
5914 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
5915 }
5916
5917 if (is_object($db)) {
5918 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5919 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5920 $lastqueryerror = $db->lastqueryerror();
5921 if (!utf8_check($lastqueryerror)) {
5922 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5923 }
5924 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5925 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5926 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5927 $out .= "<br>\n";
5928 } else { // Mode CLI
5929 // No dol_escape_htmltag for output, we are in CLI mode
5930 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5931 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5932 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5933 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5934 }
5935 $syslog .= ", sql=".$db->lastquery();
5936 $syslog .= ", db_error=".$db->lasterror();
5937 }
5938
5939 if ($error || $errors) {
5940 // Merge all into $errors array
5941 if (is_array($error) && is_array($errors)) {
5942 $errors = array_merge($error, $errors);
5943 } elseif (is_array($error)) { // deprecated, use second parameters
5944 $errors = $error;
5945 } elseif (is_array($errors) && !empty($error)) {
5946 $errors = array_merge(array($error), $errors);
5947 } elseif (!empty($error)) {
5948 $errors = array_merge(array($error), array($errors));
5949 }
5950
5951 $langs->load("errors");
5952
5953 foreach ($errors as $msg) {
5954 if (empty($msg)) {
5955 continue;
5956 }
5957 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5958 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5959 } else { // Mode CLI
5960 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5961 }
5962 $syslog .= ", msg=".$msg;
5963 }
5964 }
5965 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5966 xdebug_print_function_stack();
5967 $out .= '<b>XDebug information:</b>'."<br>\n";
5968 $out .= 'File: '.xdebug_call_file()."<br>\n";
5969 $out .= 'Line: '.xdebug_call_line()."<br>\n";
5970 $out .= 'Function: '.xdebug_call_function()."<br>\n";
5971 $out .= "<br>\n";
5972 }
5973
5974 // Return a http header with error code if possible
5975 if (!headers_sent()) {
5976 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5977 top_httphead();
5978 }
5979 //http_response_code(500); // If we use 500, message is not output with some command line tools
5980 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
5981 }
5982
5983 if (empty($dolibarr_main_prod)) {
5984 print $out;
5985 } else {
5986 if (empty($langs->defaultlang)) {
5987 $langs->setDefaultLang();
5988 }
5989 $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.
5990 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5991 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";
5992 print $langs->trans("DolibarrHasDetectedError").'. ';
5993 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5994 if (!defined("MAIN_CORE_ERROR")) {
5995 define("MAIN_CORE_ERROR", 1);
5996 }
5997 }
5998
5999 dol_syslog("Error ".$syslog, LOG_ERR);
6000}
6001
6012function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6013{
6014 global $langs;
6015
6016 if (empty($email)) {
6017 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6018 }
6019
6020 $langs->load("errors");
6021 $now = dol_now();
6022
6023 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6024 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6025 if ($errormessage) {
6026 print '<br><br>'.$errormessage;
6027 }
6028 if (is_array($errormessages) && count($errormessages)) {
6029 foreach ($errormessages as $mesgtoshow) {
6030 print '<br><br>'.$mesgtoshow;
6031 }
6032 }
6033 print '</div></div>';
6034}
6035
6052function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6053{
6054 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6055}
6056
6075function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6076{
6077 global $langs, $form;
6078 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6079
6080 if ($moreattrib == 'class="right"') {
6081 $prefix .= 'right '; // For backward compatibility
6082 }
6083
6084 $sortorder = strtoupper($sortorder);
6085 $out = '';
6086 $sortimg = '';
6087
6088 $tag = 'th';
6089 if ($thead == 2) {
6090 $tag = 'div';
6091 }
6092
6093 $tmpsortfield = explode(',', $sortfield);
6094 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6095 $tmpfield = explode(',', $field);
6096 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6097
6098 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6099 $prefix = 'wrapcolumntitle '.$prefix;
6100 }
6101
6102 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6103 // If field is used as sort criteria we use a specific css class liste_titre_sel
6104 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6105 $liste_titre = 'liste_titre';
6106 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6107 $liste_titre = 'liste_titre_sel';
6108 }
6109
6110 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6111 //$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)).'"' : '');
6112 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6113 $tagstart .= '>';
6114
6115 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6116 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6117 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6118 $options = preg_replace('/&+/i', '&', $options);
6119 if (!preg_match('/^&/', $options)) {
6120 $options = '&'.$options;
6121 }
6122
6123 $sortordertouseinlink = '';
6124 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6125 if (preg_match('/^DESC/i', $sortorder)) {
6126 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6127 } else { // We reverse the var $sortordertouseinlink
6128 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6129 }
6130 } else { // We are on field that is the first current sorting criteria
6131 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6132 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6133 } else {
6134 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6135 }
6136 }
6137 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6138 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6139 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6140 $out .= '>';
6141 }
6142 if ($tooltip) {
6143 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6144 if (preg_match('/:\w+$/', $tooltip)) {
6145 $tmptooltip = explode(':', $tooltip);
6146 } else {
6147 $tmptooltip = array($tooltip);
6148 }
6149 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6150 } else {
6151 $out .= $langs->trans($name);
6152 }
6153
6154 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6155 $out .= '</a>';
6156 }
6157
6158 if (empty($thead) && $field) { // If this is a sort field
6159 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6160 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6161 $options = preg_replace('/&+/i', '&', $options);
6162 if (!preg_match('/^&/', $options)) {
6163 $options = '&'.$options;
6164 }
6165
6166 if (!$sortorder || ($field1 != $sortfield1)) {
6167 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6168 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6169 } else {
6170 if (preg_match('/^DESC/', $sortorder)) {
6171 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6172 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6173 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6174 }
6175 if (preg_match('/^ASC/', $sortorder)) {
6176 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6177 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6178 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6179 }
6180 }
6181 }
6182
6183 $tagend = '</'.$tag.'>';
6184
6185 $out = $tagstart.$sortimg.$out.$tagend;
6186
6187 return $out;
6188}
6189
6198function print_titre($title)
6199{
6200 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6201
6202 print '<div class="titre">'.$title.'</div>';
6203}
6204
6216function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6217{
6218 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6219}
6220
6234function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6235{
6236 $return = '';
6237
6238 if ($picto == 'setup') {
6239 $picto = 'generic';
6240 }
6241
6242 $return .= "\n";
6243 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
6244 $return .= '<tr class="titre">';
6245 if ($picto) {
6246 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6247 }
6248 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6249 $return .= '<div class="titre inline-block">';
6250 $return .= $title; // $title is already HTML sanitized content
6251 $return .= '</div>';
6252 $return .= '</td>';
6253 if (dol_strlen($morehtmlcenter)) {
6254 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6255 }
6256 if (dol_strlen($morehtmlright)) {
6257 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6258 }
6259 $return .= '</tr></table>'."\n";
6260
6261 return $return;
6262}
6263
6287function print_barre_liste($title, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
6288{
6289 global $conf;
6290
6291 $savlimit = $limit;
6292 $savtotalnboflines = $totalnboflines;
6293 if (is_numeric($totalnboflines)) {
6294 $totalnboflines = abs($totalnboflines);
6295 }
6296
6297 $page = (int) $page;
6298
6299 if ($picto == 'setup') {
6300 $picto = 'title_setup.png';
6301 }
6302 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6303 $picto = 'title.gif';
6304 }
6305 if ($limit < 0) {
6306 $limit = $conf->liste_limit;
6307 }
6308
6309 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6310 $nextpage = 1;
6311 } else {
6312 $nextpage = 0;
6313 }
6314 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
6315
6316 print "\n";
6317 print "<!-- Begin title -->\n";
6318 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
6319
6320 // Left
6321
6322 if ($picto && $title) {
6323 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
6324 }
6325
6326 print '<td class="nobordernopadding valignmiddle col-title">';
6327 print '<div class="titre inline-block">';
6328 print $title; // $title may contains HTML
6329 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
6330 print '<span class="opacitymedium colorblack paddingleft totalnboflines">('.$totalnboflines.')</span>';
6331 }
6332 print '</div></td>';
6333
6334 // Center
6335 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6336 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6337 }
6338
6339 // Right
6340 print '<td class="nobordernopadding valignmiddle right col-right">';
6341 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6342 if ($sortfield) {
6343 $options .= "&sortfield=".urlencode($sortfield);
6344 }
6345 if ($sortorder) {
6346 $options .= "&sortorder=".urlencode($sortorder);
6347 }
6348 // Show navigation bar
6349 $pagelist = '';
6350 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6351 if ($totalnboflines) { // If we know total nb of lines
6352 // Define nb of extra page links before and after selected page + ... + first or last
6353 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6354
6355 if ($limit > 0) {
6356 $nbpages = ceil($totalnboflines / $limit);
6357 } else {
6358 $nbpages = 1;
6359 }
6360 $cpt = ($page - $maxnbofpage);
6361 if ($cpt < 0) {
6362 $cpt = 0;
6363 }
6364
6365 if ($cpt >= 1) {
6366 if (empty($pagenavastextinput)) {
6367 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6368 if ($cpt > 2) {
6369 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6370 } elseif ($cpt == 2) {
6371 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6372 }
6373 }
6374 }
6375
6376 do {
6377 if ($pagenavastextinput) {
6378 if ($cpt == $page) {
6379 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6380 $pagelist .= '/';
6381 }
6382 } else {
6383 if ($cpt == $page) {
6384 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6385 } else {
6386 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6387 }
6388 }
6389 $cpt++;
6390 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6391
6392 if (empty($pagenavastextinput)) {
6393 if ($cpt < $nbpages) {
6394 if ($cpt < $nbpages - 2) {
6395 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6396 } elseif ($cpt == $nbpages - 2) {
6397 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6398 }
6399 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6400 }
6401 } else {
6402 //var_dump($page.' '.$cpt.' '.$nbpages);
6403 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6404 }
6405 } else {
6406 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6407 }
6408 }
6409
6410 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6411 print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit, $morehtmlrightbeforearrow, $hidenavigation); // output the div and ul for previous/last completed with page numbers into $pagelist
6412 }
6413
6414 // js to autoselect page field on focus
6415 if ($pagenavastextinput) {
6416 print ajax_autoselect('.pageplusone');
6417 }
6418
6419 print '</td>';
6420 print '</tr>';
6421
6422 print '</table>'."\n";
6423
6424 // Center
6425 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6426 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6427 }
6428
6429 print "<!-- End title -->\n\n";
6430}
6431
6448function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
6449{
6450 global $conf, $langs;
6451
6452 print '<div class="pagination"><ul>';
6453 if ($beforearrows) {
6454 print '<li class="paginationbeforearrows">';
6455 print $beforearrows;
6456 print '</li>';
6457 }
6458
6459 if (empty($hidenavigation)) {
6460 if ((int) $limit > 0 && empty($hideselectlimit)) {
6461 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6462 $pagesizechoices .= ',5000:5000,10000:10000';
6463 //$pagesizechoices .= ',20000:20000'; // Memory trouble on browsers
6464 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6465 //$pagesizechoices .= ',2:2';
6466 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6467 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6468 }
6469
6470 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6471 print '<li class="pagination">';
6472 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.'">';
6473 print '<datalist id="limitlist">';
6474 } else {
6475 print '<li class="paginationcombolimit valignmiddle">';
6476 print '<select id="limit" class="flat selectlimit nopadding maxwidth75 center" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6477 }
6478 $tmpchoice = explode(',', $pagesizechoices);
6479 $tmpkey = $limit.':'.$limit;
6480 if (!in_array($tmpkey, $tmpchoice)) {
6481 $tmpchoice[] = $tmpkey;
6482 }
6483 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6484 if (!in_array($tmpkey, $tmpchoice)) {
6485 $tmpchoice[] = $tmpkey;
6486 }
6487 asort($tmpchoice, SORT_NUMERIC);
6488 foreach ($tmpchoice as $val) {
6489 $selected = '';
6490 $tmp = explode(':', $val);
6491 $key = $tmp[0];
6492 $val = $tmp[1];
6493 if ($key != '' && $val != '') {
6494 if ((int) $key == (int) $limit) {
6495 $selected = ' selected="selected"';
6496 }
6497 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6498 }
6499 }
6500 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6501 print '</datalist>';
6502 } else {
6503 print '</select>';
6504 print ajax_combobox("limit", array(), 0, 0, 'resolve', -1, 'limit');
6505 //print ajax_combobox("limit");
6506 }
6507
6508 if ($conf->use_javascript_ajax) {
6509 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6510 <script>
6511 jQuery(document).ready(function () {
6512 jQuery(".selectlimit").change(function() {
6513 console.log("Change limit. Send submit");
6514 $(this).parents(\'form:first\').submit();
6515 });
6516 });
6517 </script>
6518 ';
6519 }
6520 print '</li>';
6521 }
6522 if ($page > 0) {
6523 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>';
6524 }
6525 if ($betweenarrows) {
6526 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6527 print $betweenarrows;
6528 print '<!--</div>-->';
6529 }
6530 if ($nextpage > 0) {
6531 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>';
6532 }
6533 if ($afterarrows) {
6534 print '<li class="paginationafterarrows">';
6535 print $afterarrows;
6536 print '</li>';
6537 }
6538 }
6539 print '</ul></div>'."\n";
6540}
6541
6542
6554function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6555{
6556 $morelabel = '';
6557
6558 if (preg_match('/%/', $rate)) {
6559 $rate = str_replace('%', '', $rate);
6560 $addpercent = true;
6561 }
6562 $reg = array();
6563 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6564 $morelabel = ' ('.$reg[1].')';
6565 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6566 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6567 }
6568 if (preg_match('/\*/', $rate)) {
6569 $rate = str_replace('*', '', $rate);
6570 $info_bits |= 1;
6571 }
6572
6573 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6574 if (!preg_match('/\//', $rate)) {
6575 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6576 } else {
6577 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6578 $ret = $rate.($addpercent ? '%' : '');
6579 }
6580 if (($info_bits & 1) && $usestarfornpr >= 0) {
6581 $ret .= ' *';
6582 }
6583 $ret .= $morelabel;
6584 return $ret;
6585}
6586
6587
6603function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6604{
6605 global $langs, $conf;
6606
6607 // Clean parameters
6608 if (empty($amount)) {
6609 $amount = 0; // To have a numeric value if amount not defined or = ''
6610 }
6611 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6612 if ($rounding == -1) {
6613 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6614 }
6615 $nbdecimal = $rounding;
6616
6617 if ($outlangs === 'none') {
6618 // Use international separators
6619 $dec = '.';
6620 $thousand = '';
6621 } else {
6622 // Output separators by default (french)
6623 $dec = ',';
6624 $thousand = ' ';
6625
6626 // If $outlangs not forced, we use use language
6627 if (!($outlangs instanceof Translate)) {
6628 $outlangs = $langs;
6629 }
6630
6631 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6632 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6633 }
6634 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6635 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6636 }
6637 if ($thousand == 'None') {
6638 $thousand = '';
6639 } elseif ($thousand == 'Space') {
6640 $thousand = ' ';
6641 }
6642 }
6643 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6644
6645 //print "amount=".$amount."-";
6646 $amount = str_replace(',', '.', $amount); // should be useless
6647 //print $amount."-";
6648 $data = explode('.', $amount);
6649 $decpart = isset($data[1]) ? $data[1] : '';
6650 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6651 //print "decpart=".$decpart."<br>";
6652 $end = '';
6653
6654 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6655 if (dol_strlen($decpart) > $nbdecimal) {
6656 $nbdecimal = dol_strlen($decpart);
6657 }
6658
6659 // If nbdecimal is higher than max to show
6660 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6661 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6662 $nbdecimal = $nbdecimalmaxshown;
6663 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6664 // If output is truncated, we show ...
6665 $end = '...';
6666 }
6667 }
6668
6669 // If force rounding
6670 if ((string) $forcerounding != '-1') {
6671 if ($forcerounding === 'MU') {
6672 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6673 } elseif ($forcerounding === 'MT') {
6674 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
6675 } elseif ($forcerounding >= 0) {
6676 $nbdecimal = $forcerounding;
6677 }
6678 }
6679
6680 // Format number
6681 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6682 if ($form) {
6683 $output = preg_replace('/\s/', '&nbsp;', $output);
6684 $output = preg_replace('/\'/', '&#039;', $output);
6685 }
6686 // Add symbol of currency if requested
6687 $cursymbolbefore = $cursymbolafter = '';
6688 if ($currency_code && is_object($outlangs)) {
6689 if ($currency_code == 'auto') {
6690 $currency_code = $conf->currency;
6691 }
6692
6693 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6694 $listoflanguagesbefore = array('nl_NL');
6695 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6696 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6697 } else {
6698 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6699 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6700 }
6701 }
6702 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6703
6704 return $output;
6705}
6706
6731function price2num($amount, $rounding = '', $option = 0)
6732{
6733 global $langs, $conf;
6734
6735 // Clean parameters
6736 if (is_null($amount)) {
6737 $amount = '';
6738 }
6739
6740 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6741 // Numbers must be '1234.56'
6742 // Decimal delimiter for PHP and database SQL requests must be '.'
6743 $dec = ',';
6744 $thousand = ' ';
6745 if (is_null($langs)) { // $langs is not defined, we use english values.
6746 $dec = '.';
6747 $thousand = ',';
6748 } else {
6749 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6750 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6751 }
6752 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6753 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6754 }
6755 }
6756 if ($thousand == 'None') {
6757 $thousand = '';
6758 } elseif ($thousand == 'Space') {
6759 $thousand = ' ';
6760 }
6761 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6762
6763 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6764 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6765 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6766 if (!is_numeric($amount)) {
6767 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6768 }
6769
6770 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
6771 $amount = str_replace($thousand, '', $amount);
6772 }
6773
6774 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6775 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6776 // So if number was already a good number, it is converted into local Dolibarr setup.
6777 if (is_numeric($amount)) {
6778 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6779 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6780 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6781 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6782 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6783 }
6784 //print "QQ".$amount."<br>\n";
6785
6786 // Now make replace (the main goal of function)
6787 if ($thousand != ',' && $thousand != '.') {
6788 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6789 }
6790
6791 $amount = str_replace(' ', '', $amount); // To avoid spaces
6792 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6793 $amount = str_replace($dec, '.', $amount);
6794
6795 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6796 }
6797 //print ' XX'.$amount.' '.$rounding;
6798
6799 // Now, $amount is a real PHP float number. We make a rounding if required.
6800 if ($rounding) {
6801 $nbofdectoround = '';
6802 if ($rounding == 'MU') {
6803 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6804 } elseif ($rounding == 'MT') {
6805 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6806 } elseif ($rounding == 'MS') {
6807 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6808 } elseif ($rounding == 'CU') {
6809 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6810 } elseif ($rounding == 'CT') {
6811 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6812 } elseif (is_numeric($rounding)) {
6813 $nbofdectoround = (int) $rounding;
6814 }
6815
6816 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6817 if (dol_strlen($nbofdectoround)) {
6818 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6819 } else {
6820 return 'ErrorBadParameterProvidedToFunction';
6821 }
6822 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6823
6824 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6825 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6826 if (is_numeric($amount)) {
6827 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6828 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6829 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6830 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6831 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6832 }
6833 //print "TT".$amount.'<br>';
6834
6835 // Always make replace because each math function (like round) replace
6836 // with local values and we want a number that has a SQL string format x.y
6837 if ($thousand != ',' && $thousand != '.') {
6838 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6839 }
6840
6841 $amount = str_replace(' ', '', $amount); // To avoid spaces
6842 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6843 $amount = str_replace($dec, '.', $amount);
6844
6845 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6846 }
6847
6848 return $amount;
6849}
6850
6863function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6864{
6865 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6866
6867 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6868 $dimension *= 1000000;
6869 $unit -= 6;
6870 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6871 $dimension *= 1000;
6872 $unit -= 3;
6873 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6874 $dimension /= 1000000;
6875 $unit += 6;
6876 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6877 $dimension /= 1000;
6878 $unit += 3;
6879 }
6880 // Special case when we want output unit into pound or ounce
6881 /* TODO
6882 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6883 {
6884 $dimension = // convert dimension from standard unit into ounce or pound
6885 $unit = $forceunitoutput;
6886 }
6887 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6888 {
6889 $dimension = // convert dimension from standard unit into ounce or pound
6890 $unit = $forceunitoutput;
6891 }*/
6892
6893 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6894 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
6895 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6896
6897 return $ret;
6898}
6899
6900
6913function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
6914{
6915 global $db, $conf, $mysoc;
6916
6917 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6918 $thirdparty_seller = $mysoc;
6919 }
6920
6921 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);
6922
6923 $vatratecleaned = $vatrate;
6924 $reg = array();
6925 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
6926 $vatratecleaned = trim($reg[1]);
6927 $vatratecode = $reg[2];
6928 }
6929
6930 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6931 {
6932 return 0;
6933 }*/
6934
6935 // Some test to guess with no need to make database access
6936 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6937 if ($local == 1) {
6938 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6939 return 0;
6940 }
6941 if ($thirdparty_seller->id == $mysoc->id) {
6942 if (!$thirdparty_buyer->localtax1_assuj) {
6943 return 0;
6944 }
6945 } else {
6946 if (!$thirdparty_seller->localtax1_assuj) {
6947 return 0;
6948 }
6949 }
6950 }
6951
6952 if ($local == 2) {
6953 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6954 if (!$mysoc->localtax2_assuj) {
6955 return 0; // If main vat is 0, IRPF may be different than 0.
6956 }
6957 if ($thirdparty_seller->id == $mysoc->id) {
6958 if (!$thirdparty_buyer->localtax2_assuj) {
6959 return 0;
6960 }
6961 } else {
6962 if (!$thirdparty_seller->localtax2_assuj) {
6963 return 0;
6964 }
6965 }
6966 }
6967 } else {
6968 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6969 return 0;
6970 }
6971 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6972 return 0;
6973 }
6974 }
6975
6976 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6977 if (in_array($mysoc->country_code, array('ES'))) {
6978 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6979 }
6980
6981 // Search local taxes
6982 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
6983 if ($local == 1) {
6984 if ($thirdparty_seller != $mysoc) {
6985 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6986 return $thirdparty_seller->localtax1_value;
6987 }
6988 } else { // i am the seller
6989 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6990 return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6991 }
6992 }
6993 }
6994 if ($local == 2) {
6995 if ($thirdparty_seller != $mysoc) {
6996 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6997 // TODO We should also return value defined on thirdparty only if defined
6998 return $thirdparty_seller->localtax2_value;
6999 }
7000 } else { // i am the seller
7001 if (in_array($mysoc->country_code, array('ES'))) {
7002 return $thirdparty_buyer->localtax2_value;
7003 } else {
7004 return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
7005 }
7006 }
7007 }
7008 }
7009
7010 // By default, search value of local tax on line of common tax
7011 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7012 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7013 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7014 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7015 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7016 if (!empty($vatratecode)) {
7017 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7018 } else {
7019 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7020 }
7021
7022 $resql = $db->query($sql);
7023
7024 if ($resql) {
7025 $obj = $db->fetch_object($resql);
7026 if ($obj) {
7027 if ($local == 1) {
7028 return $obj->localtax1;
7029 } elseif ($local == 2) {
7030 return $obj->localtax2;
7031 }
7032 }
7033 }
7034
7035 return 0;
7036}
7037
7038
7047function isOnlyOneLocalTax($local)
7048{
7049 $tax = get_localtax_by_third($local);
7050
7051 $valors = explode(":", $tax);
7052
7053 if (count($valors) > 1) {
7054 return false;
7055 } else {
7056 return true;
7057 }
7058}
7059
7066function get_localtax_by_third($local)
7067{
7068 global $db, $mysoc;
7069
7070 $sql = " SELECT t.localtax".$local." as localtax";
7071 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7072 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7073 $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";
7074 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7075 $sql .= " AND t.localtax".$local."_type <> '0'";
7076 $sql .= " ORDER BY t.rowid DESC";
7077
7078 $resql = $db->query($sql);
7079 if ($resql) {
7080 $obj = $db->fetch_object($resql);
7081 if ($obj) {
7082 return $obj->localtax;
7083 } else {
7084 return '0';
7085 }
7086 }
7087
7088 return 'Error';
7089}
7090
7091
7103function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7104{
7105 global $db;
7106
7107 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7108
7109 // Search local taxes
7110 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7111 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7112 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7113 if ($firstparamisid) {
7114 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7115 } else {
7116 $vatratecleaned = $vatrate;
7117 $vatratecode = '';
7118 $reg = array();
7119 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7120 $vatratecleaned = $reg[1];
7121 $vatratecode = $reg[2];
7122 }
7123
7124 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7125 /*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 ??
7126 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7127 $sql .= " WHERE t.fk_pays = c.rowid";
7128 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7129 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7130 } else {
7131 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7132 }
7133 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7134 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7135 if ($vatratecode) {
7136 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7137 }
7138 }
7139
7140 $resql = $db->query($sql);
7141 if ($resql) {
7142 $obj = $db->fetch_object($resql);
7143 if ($obj) {
7144 return array(
7145 'rowid' => $obj->rowid,
7146 'code' => $obj->code,
7147 'rate' => $obj->rate,
7148 'localtax1' => $obj->localtax1,
7149 'localtax1_type' => $obj->localtax1_type,
7150 'localtax2' => $obj->localtax2,
7151 'localtax2_type' => $obj->localtax2_type,
7152 'npr' => $obj->npr,
7153 'accountancy_code_sell' => $obj->accountancy_code_sell,
7154 'accountancy_code_buy' => $obj->accountancy_code_buy
7155 );
7156 } else {
7157 return array();
7158 }
7159 } else {
7160 dol_print_error($db);
7161 }
7162
7163 return array();
7164}
7165
7182function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7183{
7184 global $db, $mysoc;
7185
7186 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7187
7188 // Search local taxes
7189 $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";
7190 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7191 if ($firstparamisid) {
7192 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7193 } else {
7194 $vatratecleaned = $vatrate;
7195 $vatratecode = '';
7196 $reg = array();
7197 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7198 $vatratecleaned = $reg[1];
7199 $vatratecode = $reg[2];
7200 }
7201
7202 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7203 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7204 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7205 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7206 } else {
7207 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7208 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7209 }
7210 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7211 if ($vatratecode) {
7212 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7213 }
7214 }
7215
7216 $resql = $db->query($sql);
7217 if ($resql) {
7218 $obj = $db->fetch_object($resql);
7219
7220 if ($obj) {
7221 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7222
7223 if ($local == 1) {
7224 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7225 } elseif ($local == 2) {
7226 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7227 } else {
7228 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);
7229 }
7230 }
7231 }
7232
7233 return array();
7234}
7235
7246function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7247{
7248 global $db, $mysoc;
7249
7250 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7251
7252 $ret = 0;
7253 $found = 0;
7254
7255 if ($idprod > 0) {
7256 // Load product
7257 $product = new Product($db);
7258 $product->fetch($idprod);
7259
7260 if (($mysoc->country_code == $thirdpartytouse->country_code)
7261 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7262 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7263 ) {
7264 // If country of thirdparty to consider is ours
7265 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7266 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7267 if ($result > 0) {
7268 $ret = $product->vatrate_supplier;
7269 if ($product->default_vat_code_supplier) {
7270 $ret .= ' ('.$product->default_vat_code_supplier.')';
7271 }
7272 $found = 1;
7273 }
7274 }
7275 if (!$found) {
7276 $ret = $product->tva_tx; // Default sales vat of product
7277 if ($product->default_vat_code) {
7278 $ret .= ' ('.$product->default_vat_code.')';
7279 }
7280 $found = 1;
7281 }
7282 } else {
7283 // TODO Read default product vat according to product and an other countrycode.
7284 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7285 }
7286 }
7287
7288 if (!$found) {
7289 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7290 // 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).
7291 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7292 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7293 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7294 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7295 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7296 $sql .= $db->plimit(1);
7297
7298 $resql = $db->query($sql);
7299 if ($resql) {
7300 $obj = $db->fetch_object($resql);
7301 if ($obj) {
7302 $ret = $obj->vat_rate;
7303 if ($obj->default_vat_code) {
7304 $ret .= ' ('.$obj->default_vat_code.')';
7305 }
7306 }
7307 $db->free($resql);
7308 } else {
7309 dol_print_error($db);
7310 }
7311 } else {
7312 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7313 // '1.23'
7314 // or '1.23 (CODE)'
7315 $defaulttx = '';
7316 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7317 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7318 }
7319 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7320 $defaultcode = $reg[1];
7321 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7322 }*/
7323
7324 $ret = $defaulttx;
7325 }
7326 }
7327
7328 dol_syslog("get_product_vat_for_country: ret=".$ret);
7329 return $ret;
7330}
7331
7341function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7342{
7343 global $db, $mysoc;
7344
7345 if (!class_exists('Product')) {
7346 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7347 }
7348
7349 $ret = 0;
7350 $found = 0;
7351
7352 if ($idprod > 0) {
7353 // Load product
7354 $product = new Product($db);
7355 $result = $product->fetch($idprod);
7356
7357 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7358 /* Not defined yet, so we don't use this
7359 if ($local==1) $ret=$product->localtax1_tx;
7360 elseif ($local==2) $ret=$product->localtax2_tx;
7361 $found=1;
7362 */
7363 } else {
7364 // TODO Read default product vat according to product and another countrycode.
7365 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7366 }
7367 }
7368
7369 if (!$found) {
7370 // If vat of product for the country not found or not defined, we return higher vat of country.
7371 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7372 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7373 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7374 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7375 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7376 $sql .= $db->plimit(1);
7377
7378 $resql = $db->query($sql);
7379 if ($resql) {
7380 $obj = $db->fetch_object($resql);
7381 if ($obj) {
7382 if ($local == 1) {
7383 $ret = $obj->localtax1;
7384 } elseif ($local == 2) {
7385 $ret = $obj->localtax2;
7386 }
7387 }
7388 } else {
7389 dol_print_error($db);
7390 }
7391 }
7392
7393 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7394 return $ret;
7395}
7396
7413function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7414{
7415 global $conf;
7416
7417 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7418
7419 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7420 $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;
7421
7422 $seller_country_code = $thirdparty_seller->country_code;
7423 $seller_in_cee = isInEEC($thirdparty_seller);
7424
7425 $buyer_country_code = $thirdparty_buyer->country_code;
7426 $buyer_in_cee = isInEEC($thirdparty_buyer);
7427
7428 dol_syslog("get_default_tva: seller use vat=".$seller_use_vat.", seller country=".$seller_country_code.", seller in cee=".((string) (int) $seller_in_cee).", buyer vat number=".$thirdparty_buyer->tva_intra." buyer country=".$buyer_country_code.", buyer in cee=".((string) (int) $buyer_in_cee).", idprod=".$idprod.", idprodfournprice=".$idprodfournprice.", SERVICE_ARE_ECOMMERCE_200238EC=".(getDolGlobalString('SERVICES_ARE_ECOMMERCE_200238EC') ? $conf->global->SERVICES_ARE_ECOMMERCE_200238EC : ''));
7429
7430 // 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)
7431 // we use the buyer VAT.
7432 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7433 if ($seller_in_cee && $buyer_in_cee) {
7434 $isacompany = $thirdparty_buyer->isACompany();
7435 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7436 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7437 if (!isValidVATID($thirdparty_buyer)) {
7438 $isacompany = 0;
7439 }
7440 }
7441
7442 if (!$isacompany) {
7443 //print 'VATRULE 0';
7444 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7445 }
7446 }
7447 }
7448
7449 // If seller does not use VAT, default VAT is 0. End of rule.
7450 if (!$seller_use_vat) {
7451 //print 'VATRULE 1';
7452 return 0;
7453 }
7454
7455 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7456 if (($seller_country_code == $buyer_country_code)
7457 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7458 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7459 ) { // Warning ->country_code not always defined
7460 //print 'VATRULE 2';
7461 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7462
7463 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7464 // Special case for india.
7465 //print 'VATRULE 2b';
7466 $reg = array();
7467 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7468 // we must revert the C+S into I
7469 $tmpvat = str_replace("C+S", "I", $tmpvat);
7470 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7471 // we must revert the I into C+S
7472 $tmpvat = str_replace("I", "C+S", $tmpvat);
7473 }
7474 }
7475
7476 return $tmpvat;
7477 }
7478
7479 // 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.
7480 // 'VATRULE 3' - Not supported
7481
7482 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7483 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7484 if (($seller_in_cee && $buyer_in_cee)) {
7485 $isacompany = $thirdparty_buyer->isACompany();
7486 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7487 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7488 if (!isValidVATID($thirdparty_buyer)) {
7489 $isacompany = 0;
7490 }
7491 }
7492
7493 if (!$isacompany) {
7494 //print 'VATRULE 4';
7495 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7496 } else {
7497 //print 'VATRULE 5';
7498 return 0;
7499 }
7500 }
7501
7502 // 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
7503 // I don't see any use case that need this rule.
7504 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7505 $isacompany = $thirdparty_buyer->isACompany();
7506 if (!$isacompany) {
7507 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7508 //print 'VATRULE extra';
7509 }
7510 }
7511
7512 // Otherwise the VAT proposed by default=0. End of rule.
7513 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7514 //print 'VATRULE 6';
7515 return 0;
7516}
7517
7518
7529function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7530{
7531 global $db;
7532
7533 if ($idprodfournprice > 0) {
7534 if (!class_exists('ProductFournisseur')) {
7535 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7536 }
7537 $prodprice = new ProductFournisseur($db);
7538 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7539 return $prodprice->fourn_tva_npr;
7540 } elseif ($idprod > 0) {
7541 if (!class_exists('Product')) {
7542 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7543 }
7544 $prod = new Product($db);
7545 $prod->fetch($idprod);
7546 return $prod->tva_npr;
7547 }
7548
7549 return 0;
7550}
7551
7565function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7566{
7567 global $mysoc;
7568
7569 if (!is_object($thirdparty_seller)) {
7570 return -1;
7571 }
7572 if (!is_object($thirdparty_buyer)) {
7573 return -1;
7574 }
7575
7576 if ($local == 1) { // Localtax 1
7577 if ($mysoc->country_code == 'ES') {
7578 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7579 return 0;
7580 }
7581 } else {
7582 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7583 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7584 return 0;
7585 }
7586 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7587 return 0;
7588 }
7589 }
7590 } elseif ($local == 2) { //I Localtax 2
7591 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7592 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7593 return 0;
7594 }
7595 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7596 return 0;
7597 }
7598 }
7599
7600 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7601 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7602 }
7603
7604 return 0;
7605}
7606
7615function yn($yesno, $case = 1, $color = 0)
7616{
7617 global $langs;
7618
7619 $result = 'unknown';
7620 $classname = '';
7621 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7622 $result = $langs->trans('yes');
7623 if ($case == 1 || $case == 3) {
7624 $result = $langs->trans("Yes");
7625 }
7626 if ($case == 2) {
7627 $result = '<input type="checkbox" value="1" checked disabled>';
7628 }
7629 if ($case == 3) {
7630 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7631 }
7632 if ($case == 4) {
7633 $result = img_picto('check', 'check');
7634 }
7635
7636 $classname = 'ok';
7637 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7638 $result = $langs->trans("no");
7639 if ($case == 1 || $case == 3) {
7640 $result = $langs->trans("No");
7641 }
7642 if ($case == 2) {
7643 $result = '<input type="checkbox" value="0" disabled>';
7644 }
7645 if ($case == 3) {
7646 $result = '<input type="checkbox" value="0" disabled> '.$result;
7647 }
7648 if ($case == 4) {
7649 $result = img_picto('uncheck', 'uncheck');
7650 }
7651
7652 if ($color == 2) {
7653 $classname = 'ok';
7654 } else {
7655 $classname = 'error';
7656 }
7657 }
7658 if ($color) {
7659 return '<span class="'.$classname.'">'.$result.'</span>';
7660 }
7661 return $result;
7662}
7663
7682function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7683{
7684 if (empty($modulepart) && is_object($object)) {
7685 if (!empty($object->module)) {
7686 $modulepart = $object->module;
7687 } elseif (!empty($object->element)) {
7688 $modulepart = $object->element;
7689 }
7690 }
7691
7692 $path = '';
7693
7694 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7695 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7696 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7697 $arrayforoldpath['product'] = 2;
7698 }
7699
7700 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7701 $level = $arrayforoldpath[$modulepart];
7702 }
7703
7704 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7705 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7706 if (empty($num) && is_object($object)) {
7707 $num = $object->id;
7708 }
7709 if (empty($alpha)) {
7710 $num = preg_replace('/([^0-9])/i', '', $num);
7711 } else {
7712 $num = preg_replace('/^.*\-/i', '', $num);
7713 }
7714 $num = substr("000".$num, -$level);
7715 if ($level == 1) {
7716 $path = substr($num, 0, 1);
7717 }
7718 if ($level == 2) {
7719 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7720 }
7721 if ($level == 3) {
7722 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7723 }
7724 } else {
7725 // We will enhance here a common way of forging path for document storage.
7726 // In a future, we may distribute directories on several levels depending on setup and object.
7727 // Here, $object->id, $object->ref and $modulepart are required.
7728 //var_dump($modulepart);
7729 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7730 }
7731
7732 if (empty($withoutslash) && !empty($path)) {
7733 $path .= '/';
7734 }
7735
7736 return $path;
7737}
7738
7747function dol_mkdir($dir, $dataroot = '', $newmask = '')
7748{
7749 global $conf;
7750
7751 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7752
7753 $dir_osencoded = dol_osencode($dir);
7754 if (@is_dir($dir_osencoded)) {
7755 return 0;
7756 }
7757
7758 $nberr = 0;
7759 $nbcreated = 0;
7760
7761 $ccdir = '';
7762 if (!empty($dataroot)) {
7763 // Remove data root from loop
7764 $dir = str_replace($dataroot.'/', '', $dir);
7765 $ccdir = $dataroot.'/';
7766 }
7767
7768 $cdir = explode("/", $dir);
7769 $num = count($cdir);
7770 for ($i = 0; $i < $num; $i++) {
7771 if ($i > 0) {
7772 $ccdir .= '/'.$cdir[$i];
7773 } else {
7774 $ccdir .= $cdir[$i];
7775 }
7776 $regs = array();
7777 if (preg_match("/^.:$/", $ccdir, $regs)) {
7778 continue; // If the Windows path is incomplete, continue with next directory
7779 }
7780
7781 // Attention, is_dir() can fail event if the directory exists
7782 // (i.e. according the open_basedir configuration)
7783 if ($ccdir) {
7784 $ccdir_osencoded = dol_osencode($ccdir);
7785 if (!@is_dir($ccdir_osencoded)) {
7786 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7787
7788 umask(0);
7789 $dirmaskdec = octdec((string) $newmask);
7790 if (empty($newmask)) {
7791 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7792 }
7793 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7794 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7795 // If the is_dir has returned a false information, we arrive here
7796 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7797 $nberr++;
7798 } else {
7799 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7800 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7801 $nbcreated++;
7802 }
7803 } else {
7804 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7805 }
7806 }
7807 }
7808 return ($nberr ? -$nberr : $nbcreated);
7809}
7810
7811
7819function dolChmod($filepath, $newmask = '')
7820{
7821 global $conf;
7822
7823 if (!empty($newmask)) {
7824 @chmod($filepath, octdec($newmask));
7825 } elseif (getDolGlobalString('MAIN_UMASK')) {
7826 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7827 }
7828}
7829
7830
7836function picto_required()
7837{
7838 return '<span class="fieldrequired">*</span>';
7839}
7840
7841
7858function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
7859{
7860 if (is_null($stringtoclean)) {
7861 return '';
7862 }
7863
7864 if ($removelinefeed == 2) {
7865 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
7866 }
7867 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
7868
7869 // 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)
7870 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7871
7872 $temp = str_replace('< ', '__ltspace__', $temp);
7873 $temp = str_replace('<:', '__lttwopoints__', $temp);
7874
7875 if ($strip_tags) {
7876 $temp = strip_tags($temp);
7877 } else {
7878 // 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).
7879 $pattern = "/<[^<>]+>/";
7880 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
7881 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
7882 // pass 2 - $temp after pass 2: 0000-021
7883 $tempbis = $temp;
7884 do {
7885 $temp = $tempbis;
7886 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
7887 $tempbis = preg_replace($pattern, '', $tempbis);
7888 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
7889 } while ($tempbis != $temp);
7890
7891 $temp = $tempbis;
7892
7893 // 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).
7894 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
7895 }
7896
7897 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
7898
7899 // Remove also carriage returns
7900 if ($removelinefeed == 1) {
7901 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
7902 }
7903
7904 // And double quotes
7905 if ($removedoublespaces) {
7906 while (strpos($temp, " ")) {
7907 $temp = str_replace(" ", " ", $temp);
7908 }
7909 }
7910
7911 $temp = str_replace('__ltspace__', '< ', $temp);
7912 $temp = str_replace('__lttwopoints__', '<:', $temp);
7913
7914 return trim($temp);
7915}
7916
7932function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0)
7933{
7934 if (empty($allowed_tags)) {
7935 $allowed_tags = array(
7936 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
7937 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
7938 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
7939 );
7940 }
7941 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
7942 if ($allowiframe) {
7943 if (!in_array('iframe', $allowed_tags)) {
7944 $allowed_tags[] = "iframe";
7945 }
7946 }
7947 if ($allowlink) {
7948 if (!in_array('link', $allowed_tags)) {
7949 $allowed_tags[] = "link";
7950 }
7951 }
7952
7953 $allowed_tags_string = implode("><", $allowed_tags);
7954 $allowed_tags_string = '<'.$allowed_tags_string.'>';
7955
7956 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
7957
7958 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
7959
7960 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
7961 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
7962
7963 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
7964 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
7965
7966 // Remove all HTML tags
7967 $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
7968
7969 if ($cleanalsosomestyles) { // Clean for remaining html tags
7970 $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
7971 }
7972 if ($removeclassattribute) { // Clean for remaining html tags
7973 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
7974 }
7975
7976 // Remove 'javascript:' that we should not find into a text with
7977 // 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)).
7978 if ($cleanalsojavascript) {
7979 $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);
7980 }
7981
7982 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
7983
7984 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
7985
7986
7987 return $temp;
7988}
7989
7990
8003function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8004{
8005 if (is_null($allowed_attributes)) {
8006 $allowed_attributes = array(
8007 "allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width",
8008 // HTML5
8009 "header", "footer", "nav", "section", "menu", "menuitem"
8010 );
8011 }
8012
8013 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8014 $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
8015
8016 // Warning: loadHTML does not support HTML5 on old libxml versions.
8017 $dom = new DOMDocument('', 'UTF-8');
8018 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8019 $savwarning = error_reporting();
8020 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8021 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8022 error_reporting($savwarning);
8023
8024 if ($dom instanceof DOMDocument) {
8025 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8026 $el = $els->item($i);
8027 if (!$el instanceof DOMElement) {
8028 continue;
8029 }
8030 $attrs = $el->attributes;
8031 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8032 //var_dump($attrs->item($ii));
8033 if (!empty($attrs->item($ii)->name)) {
8034 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8035 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8036 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8037 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8038 // If attribute is 'style'
8039 $valuetoclean = $attrs->item($ii)->value;
8040
8041 if (isset($valuetoclean)) {
8042 do {
8043 $oldvaluetoclean = $valuetoclean;
8044 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8045 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8046 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8047 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8048 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8049 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8050 }
8051
8052 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8053 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8054 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8055 } while ($oldvaluetoclean != $valuetoclean);
8056 }
8057
8058 $attrs->item($ii)->value = $valuetoclean;
8059 }
8060 }
8061 }
8062 }
8063 }
8064
8065 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8066 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8067
8068 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8069 $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
8070 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
8071 return trim($return);
8072 } else {
8073 return $stringtoclean;
8074 }
8075}
8076
8088function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8089{
8090 $temp = $stringtoclean;
8091 foreach ($disallowed_tags as $tagtoremove) {
8092 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8093 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8094 }
8095
8096 if ($cleanalsosomestyles) {
8097 $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
8098 }
8099
8100 return $temp;
8101}
8102
8103
8113function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8114{
8115 if ($nboflines == 1) {
8116 if (dol_textishtml($text)) {
8117 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8118 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8119 } else {
8120 if (isset($text)) {
8121 $firstline = preg_replace('/[\n\r].*/', '', $text);
8122 } else {
8123 $firstline = '';
8124 }
8125 }
8126 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8127 } else {
8128 $ishtml = 0;
8129 if (dol_textishtml($text)) {
8130 $text = preg_replace('/\n/', '', $text);
8131 $ishtml = 1;
8132 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8133 } else {
8134 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8135 }
8136
8137 $text = strtr($text, $repTable);
8138 if ($charset == 'UTF-8') {
8139 $pattern = '/(<br[^>]*>)/Uu';
8140 } else {
8141 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8142 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8143 }
8144 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8145
8146 $firstline = '';
8147 $i = 0;
8148 $countline = 0;
8149 $lastaddediscontent = 1;
8150 while ($countline < $nboflines && isset($a[$i])) {
8151 if (preg_match('/<br[^>]*>/', $a[$i])) {
8152 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8153 $firstline .= ($ishtml ? "<br>\n" : "\n");
8154 // Is it a br for a new line of after a printed line ?
8155 if (!$lastaddediscontent) {
8156 $countline++;
8157 }
8158 $lastaddediscontent = 0;
8159 }
8160 } else {
8161 $firstline .= $a[$i];
8162 $lastaddediscontent = 1;
8163 $countline++;
8164 }
8165 $i++;
8166 }
8167
8168 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8169 //unset($a);
8170 $ret = $firstline.($adddots ? '...' : '');
8171 //exit;
8172 return $ret;
8173 }
8174}
8175
8176
8188function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8189{
8190 if (is_null($stringtoencode)) {
8191 return '';
8192 }
8193
8194 if (!$nl2brmode) {
8195 return nl2br($stringtoencode, $forxml);
8196 } else {
8197 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8198 return $ret;
8199 }
8200}
8201
8211function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8212{
8213 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8214 // TODO using sandbox on inline html content is not possible yet with current browsers
8215 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8216 //$s .= $stringtoencode;
8217 //$s .= '</body></html></iframe>';
8218 return $stringtoencode;
8219 } else {
8220 $out = $stringtoencode;
8221
8222 // First clean HTML content
8223 do {
8224 $oldstringtoclean = $out;
8225
8226 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8227 try {
8228 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8229 if (LIBXML_VERSION < 20900) {
8230 // Avoid load of external entities (security problem).
8231 // Required only if LIBXML_VERSION < 20900
8232 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8233 libxml_disable_entity_loader(true);
8234 }
8235
8236 $dom = new DOMDocument();
8237 // Add a trick to solve pb with text without parent tag
8238 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8239 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8240
8241 if (dol_textishtml($out)) {
8242 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.$out.'</div>';
8243 } else {
8244 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.dol_nl2br($out).'</div>';
8245 }
8246
8247 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8248 $out = trim($dom->saveHTML());
8249
8250 // Remove the trick added to solve pb with text without parent tag
8251 $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8252 $out = preg_replace('/<\/div>$/', '', $out);
8253 } catch (Exception $e) {
8254 // If error, invalid HTML string with no way to clean it
8255 //print $e->getMessage();
8256 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8257 }
8258 }
8259
8260 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && $check != 'restricthtmlallowunvalid') {
8261 try {
8262 // Try cleaning using tidy
8263 if (extension_loaded('tidy') && class_exists("tidy")) {
8264 //print "aaa".$out."\n";
8265
8266 // See options at https://tidy.sourceforge.net/docs/quickref.html
8267 $config = array(
8268 'clean' => false,
8269 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8270 'doctype' => 'strict',
8271 'show-body-only' => true,
8272 "indent-attributes" => false,
8273 "vertical-space" => false,
8274 //'ident' => false, // Not always supported
8275 "wrap" => 0
8276 // HTML5 tags
8277 //'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',
8278 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8279 //'new-empty-tags' => 'command embed keygen source track wbr',
8280 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8281 );
8282
8283 // Tidy
8284 $tidy = new tidy();
8285 $out = $tidy->repairString($out, $config, 'utf8');
8286
8287 //print "xxx".$out;exit;
8288 }
8289 } catch (Exception $e) {
8290 // If error, invalid HTML string with no way to clean it
8291 //print $e->getMessage();
8292 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8293 }
8294 }
8295
8296 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8297 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8298
8299 // Clean some html entities that are useless so text is cleaner
8300 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8301
8302 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8303 // encoded using text entities) so we can then exclude all numeric entities.
8304 $out = preg_replace('/&#39;/i', '&apos;', $out);
8305
8306 // 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).
8307 // 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
8308 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8309 $out = preg_replace_callback(
8310 '/&#(x?[0-9][0-9a-f]+;?)/i',
8315 static function ($m) {
8316 return realCharForNumericEntities($m);
8317 },
8318 $out
8319 );
8320
8321 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8322 $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'.
8323
8324 // Keep only some html tags and remove also some 'javascript:' strings
8325 if ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8326 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8327 } else {
8328 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8329 }
8330
8331 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8332 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8334 }
8335
8336 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8337 $out = preg_replace('/&apos;/i', "&#39;", $out);
8338
8339 // Now remove js
8340 // 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
8341 $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)>
8342 $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);
8343 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8344 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8345 $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);
8346 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8347 // More not into the previous list
8348 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8349 } while ($oldstringtoclean != $out);
8350
8351 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8352 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8353 // 'url(' to avoid inline style like background: url(http...
8354 // '<link' to avoid <link href="http...">
8355 $reg = array();
8356 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8357 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8358 $nblinks = count($reg[0]);
8359 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8360 $out = 'ErrorTooManyLinksIntoHTMLString';
8361 }
8362
8363 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8364 if ($nblinks > 0) {
8365 $out = 'ErrorHTMLLinksNotAllowed';
8366 }
8367 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8368 $nblinks = 0;
8369 // Loop on each url in src= and url(
8370 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8371
8372 $matches = array();
8373 if (preg_match_all($pattern, $out, $matches)) {
8374 // URLs are into $matches[1]
8375 $urls = $matches[1];
8376
8377 // Affiche les URLs
8378 foreach ($urls as $url) {
8379 $nblinks++;
8380 echo "Found url = ".$url . "\n";
8381 }
8382 if ($nblinks > 0) {
8383 $out = 'ErrorHTMLExternalLinksNotAllowed';
8384 }
8385 }
8386 }
8387
8388 return $out;
8389 }
8390}
8391
8412function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8413{
8414 if (is_null($stringtoencode)) {
8415 return '';
8416 }
8417
8418 $newstring = $stringtoencode;
8419 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8420 $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.
8421 if ($removelasteolbr) {
8422 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8423 }
8424 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8425 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8426 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8427 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8428 } else {
8429 if ($removelasteolbr) {
8430 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8431 }
8432 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8433 }
8434 // Other substitutions that htmlentities does not do
8435 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8436 return $newstring;
8437}
8438
8446function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8447{
8448 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8449 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8450 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8451 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8452 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8453 return $ret;
8454}
8455
8462function dol_htmlcleanlastbr($stringtodecode)
8463{
8464 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8465 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8466 return $ret;
8467}
8468
8478function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8479{
8480 $newstring = $a;
8481 if ($keepsomeentities) {
8482 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8483 }
8484 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8485 if ($keepsomeentities) {
8486 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8487 }
8488 return $newstring;
8489}
8490
8502function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8503{
8504 return htmlentities($string, $flags, $encoding, $double_encode);
8505}
8506
8518function dol_string_is_good_iso($s, $clean = 0)
8519{
8520 $len = dol_strlen($s);
8521 $out = '';
8522 $ok = 1;
8523 for ($scursor = 0; $scursor < $len; $scursor++) {
8524 $ordchar = ord($s[$scursor]);
8525 //print $scursor.'-'.$ordchar.'<br>';
8526 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8527 $ok = 0;
8528 break;
8529 } elseif ($ordchar > 126 && $ordchar < 160) {
8530 $ok = 0;
8531 break;
8532 } elseif ($clean) {
8533 $out .= $s[$scursor];
8534 }
8535 }
8536 if ($clean) {
8537 return $out;
8538 }
8539 return $ok;
8540}
8541
8550function dol_nboflines($s, $maxchar = 0)
8551{
8552 if ($s == '') {
8553 return 0;
8554 }
8555 $arraystring = explode("\n", $s);
8556 $nb = count($arraystring);
8557
8558 return $nb;
8559}
8560
8561
8571function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8572{
8573 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8574 if (dol_textishtml($text)) {
8575 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8576 }
8577
8578 $text = strtr($text, $repTable);
8579 if ($charset == 'UTF-8') {
8580 $pattern = '/(<br[^>]*>)/Uu';
8581 } else {
8582 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8583 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8584 }
8585 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8586
8587 $nblines = (int) floor((count($a) + 1) / 2);
8588 // count possible auto line breaks
8589 if ($maxlinesize) {
8590 foreach ($a as $line) {
8591 if (dol_strlen($line) > $maxlinesize) {
8592 //$line_dec = html_entity_decode(strip_tags($line));
8593 $line_dec = html_entity_decode($line);
8594 if (dol_strlen($line_dec) > $maxlinesize) {
8595 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8596 $nblines += substr_count($line_dec, '\n');
8597 }
8598 }
8599 }
8600 }
8601
8602 unset($a);
8603 return $nblines;
8604}
8605
8614function dol_textishtml($msg, $option = 0)
8615{
8616 if (is_null($msg)) {
8617 return false;
8618 }
8619
8620 if ($option == 1) {
8621 if (preg_match('/<html/i', $msg)) {
8622 return true;
8623 } elseif (preg_match('/<body/i', $msg)) {
8624 return true;
8625 } elseif (preg_match('/<\/textarea/i', $msg)) {
8626 return true;
8627 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8628 return true;
8629 } elseif (preg_match('/<br/i', $msg)) {
8630 return true;
8631 }
8632 return false;
8633 } else {
8634 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8635 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8636 if (preg_match('/<html/i', $msg)) {
8637 return true;
8638 } elseif (preg_match('/<body/i', $msg)) {
8639 return true;
8640 } elseif (preg_match('/<\/textarea/i', $msg)) {
8641 return true;
8642 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8643 return true;
8644 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8645 return true;
8646 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8647 return true;
8648 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8649 return true;
8650 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8651 return true; // must accept <img src="http://example.com/aaa.png" />
8652 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8653 return true; // must accept <a href="http://example.com/aaa.png" />
8654 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8655 return true;
8656 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8657 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8658 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8659 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8660 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8661 }
8662
8663 return false;
8664 }
8665}
8666
8681function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8682{
8683 if (!empty($invert)) {
8684 $tmp = $text1;
8685 $text1 = $text2;
8686 $text2 = $tmp;
8687 }
8688
8689 $ret = '';
8690 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8691 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8692 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8693 return $ret;
8694}
8695
8696
8697
8711function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8712{
8713 global $db, $conf, $mysoc, $user, $extrafields;
8714
8715 $substitutionarray = array();
8716
8717 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
8718 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8719 // this will include signature content first and then replace var found into content of signature
8720 //var_dump($onlykey);
8721 $emailsendersignature = $user->signature; // dy default, we use the signature of current user. We must complete substitution with signature in c_email_senderprofile of array after calling getCommonSubstitutionArray()
8722 $usersignature = $user->signature;
8723 $substitutionarray = array_merge($substitutionarray, array(
8724 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8725 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8726 ));
8727
8728 if (is_object($user) && ($user instanceof User)) {
8729 $substitutionarray = array_merge($substitutionarray, array(
8730 '__USER_ID__' => (string) $user->id,
8731 '__USER_LOGIN__' => (string) $user->login,
8732 '__USER_EMAIL__' => (string) $user->email,
8733 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8734 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8735 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8736 '__USER_FAX__' => (string) $user->office_fax,
8737 '__USER_LASTNAME__' => (string) $user->lastname,
8738 '__USER_FIRSTNAME__' => (string) $user->firstname,
8739 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8740 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8741 '__USER_JOB__' => (string) $user->job,
8742 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8743 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8744 ));
8745 }
8746 }
8747 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8748 $substitutionarray = array_merge($substitutionarray, array(
8749 '__MYCOMPANY_NAME__' => $mysoc->name,
8750 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8751 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8752 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8753 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8754 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8755 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8756 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8757 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8758 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8759 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8760 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8761 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8762 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8763 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8764 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8765 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8766 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8767 '__MYCOMPANY_TOWN__' => $mysoc->town,
8768 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
8769 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
8770 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
8771 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
8772 ));
8773 }
8774
8775 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
8776 if ($onlykey) {
8777 $substitutionarray['__ID__'] = '__ID__';
8778 $substitutionarray['__REF__'] = '__REF__';
8779 $substitutionarray['__NEWREF__'] = '__NEWREF__';
8780 $substitutionarray['__LABEL__'] = '__LABEL__';
8781 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
8782 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
8783 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
8784 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
8785 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
8786
8787 if (isModEnabled("societe")) { // Most objects are concerned
8788 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
8789 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
8790 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
8791 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
8792 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
8793 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
8794 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
8795 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
8796 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
8797 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
8798 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
8799 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
8800 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
8801 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
8802 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
8803 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
8804 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
8805 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
8806 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
8807 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
8808 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
8809 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
8810 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
8811 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
8812 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
8813 }
8814 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
8815 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
8816 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
8817 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
8818 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
8819 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
8820 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
8821 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
8822 }
8823 // add substitution variables for ticket
8824 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
8825 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
8826 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
8827 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
8828 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
8829 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
8830 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
8831 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
8832 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
8833 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
8834 }
8835
8836 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
8837 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
8838 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
8839 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
8840 }
8841 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
8842 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
8843 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
8844 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
8845 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
8846 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
8847 }
8848 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
8849 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
8850 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
8851 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
8852 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
8853 }
8854 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
8855 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
8856 }
8857 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
8858 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
8859 }
8860 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
8861 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
8862 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
8863 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
8864 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
8865 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
8866 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
8867
8868 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
8869 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
8870 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
8871 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
8872 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
8873
8874 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
8875 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
8876 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
8877 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
8878 }
8879 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
8880 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
8881 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
8882 }
8883 } else {
8884 '@phan-var-force Adherent|Delivery $object';
8885 $substitutionarray['__ID__'] = $object->id;
8886 $substitutionarray['__REF__'] = $object->ref;
8887 $substitutionarray['__NEWREF__'] = $object->newref;
8888 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
8889 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8890 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8891 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
8892 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
8893 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', 0, $outputlangs) : '');
8894 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', 0, $outputlangs) : '');
8895 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', 0, $outputlangs) : '');
8896 $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', 0, $outputlangs) : '');
8897 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%d") : '');
8898 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%A") : '');
8899 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%m") : '');
8900 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%b") : '');
8901 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%Y") : '');
8902 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%H") : '');
8903 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%M") : '');
8904 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%S") : '');
8905
8906 // For backward compatibility (deprecated)
8907 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8908 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8909 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->delivery_date) ? dol_print_date($object->delivery_date, 'day', 0, $outputlangs) : '');
8910 $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 : '')) : '');
8911 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
8912
8913 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
8914 '@phan-var-force Adherent $object';
8915 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
8916
8917 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
8918 if (method_exists($object, 'getCivilityLabel')) {
8919 $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
8920 }
8921 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
8922 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
8923 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
8924 if (method_exists($object, 'getFullName')) {
8925 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
8926 }
8927 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
8928 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
8929 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
8930 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
8931 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
8932 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
8933 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
8934 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
8935 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
8936 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
8937 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
8938 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
8939 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
8940 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
8941 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
8942
8943 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
8944 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
8945 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
8946 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
8947 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
8948 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
8949 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
8950 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
8951 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
8952 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
8953 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
8954 }
8955
8956 if (is_object($object) && $object->element == 'societe') {
8957 '@phan-var-force Societe $object';
8958 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
8959 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
8960 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
8961 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
8962 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
8963 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
8964 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
8965 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
8966 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
8967 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
8968 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
8969 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
8970 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
8971 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
8972 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
8973 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
8974 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
8975 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
8976 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
8977 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
8978 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
8979 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
8980 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
8981 } elseif (is_object($object->thirdparty)) {
8982 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
8983 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
8984 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
8985 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
8986 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
8987 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
8988 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
8989 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
8990 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
8991 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
8992 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
8993 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
8994 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
8995 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
8996 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
8997 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
8998 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
8999 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9000 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9001 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9002 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9003 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9004 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9005 }
9006
9007 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9008 '@phan-var-force RecruitmentCandidature $object';
9009 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9010 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9011 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9012 }
9013 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9014 '@phan-var-force ConferenceOrBoothAttendee $object';
9015 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9016 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9017 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9018 }
9019
9020 if (is_object($object) && $object->element == 'project') {
9021 '@phan-var-force Project $object';
9022 $substitutionarray['__PROJECT_ID__'] = $object->id;
9023 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9024 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9025 } elseif (is_object($object)) {
9026 $project = null;
9027 if (!empty($object->project)) {
9028 $project = $object->project;
9029 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9030 $project = $object->projet;
9031 }
9032 if (!is_null($project) && is_object($project)) {
9033 $substitutionarray['__PROJECT_ID__'] = $project->id;
9034 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9035 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9036 } else {
9037 // can substitute variables for project : uses lazy load in "make_substitutions" method
9038 $project_id = 0;
9039 if (!empty($object->fk_project) && $object->fk_project > 0) {
9040 $project_id = $object->fk_project;
9041 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9042 $project_id = $object->fk_project;
9043 }
9044 if ($project_id > 0) {
9045 // path:class:method:id
9046 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9047 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9048 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9049 }
9050 }
9051 }
9052
9053 if (is_object($object) && $object->element == 'facture') {
9054 '@phan-var-force Facture $object';
9055 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9056 }
9057 if (is_object($object) && $object->element == 'shipping') {
9058 '@phan-var-force Expedition $object';
9059 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9060 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9061 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9062 }
9063 if (is_object($object) && $object->element == 'reception') {
9064 '@phan-var-force Reception $object';
9065 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9066 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9067 }
9068
9069 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9070 '@phan-var-force Contrat $object';
9071 $dateplannedstart = '';
9072 $datenextexpiration = '';
9073 foreach ($object->lines as $line) {
9074 if ($line->date_start > $dateplannedstart) {
9075 $dateplannedstart = $line->date_start;
9076 }
9077 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9078 $datenextexpiration = $line->date_end;
9079 }
9080 }
9081 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9082 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9083 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9084
9085 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9086 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9087 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9088 }
9089 // add substitution variables for ticket
9090 if (is_object($object) && $object->element == 'ticket') {
9091 '@phan-var-force Ticket $object';
9092 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9093 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9094 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9095 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9096 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9097 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9098 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9099 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9100 $userstat = new User($db);
9101 if ($object->fk_user_assign > 0) {
9102 $userstat->fetch($object->fk_user_assign);
9103 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9104 }
9105
9106 if ($object->fk_user_create > 0) {
9107 $userstat->fetch($object->fk_user_create);
9108 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9109 }
9110 }
9111
9112 // Create dynamic tags for __EXTRAFIELD_FIELD__
9113 if ($object->table_element && $object->id > 0) {
9114 if (!is_object($extrafields)) {
9115 $extrafields = new ExtraFields($db);
9116 }
9117 $extrafields->fetch_name_optionals_label($object->table_element, true);
9118
9119 if ($object->fetch_optionals() > 0) {
9120 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9121 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9122 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9123 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9124 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9125 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9126 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9127 $datetime = $object->array_options['options_'.$key];
9128 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9129 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9130 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9131 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9132 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9133 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9134 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9135 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9136 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9137 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9138 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9139 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9140 }
9141 }
9142 }
9143 }
9144 }
9145
9146 // Complete substitution array with the url to make online payment
9147 if (empty($substitutionarray['__REF__'])) {
9148 $paymenturl = '';
9149 } else {
9150 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9151 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9152 $outputlangs->loadLangs(array('paypal', 'other'));
9153
9154 $amounttouse = 0;
9155 $typeforonlinepayment = 'free';
9156 if (is_object($object) && $object->element == 'commande') {
9157 $typeforonlinepayment = 'order';
9158 }
9159 if (is_object($object) && $object->element == 'facture') {
9160 $typeforonlinepayment = 'invoice';
9161 }
9162 if (is_object($object) && $object->element == 'member') {
9163 $typeforonlinepayment = 'member';
9164 if (!empty($object->last_subscription_amount)) {
9165 $amounttouse = $object->last_subscription_amount;
9166 }
9167 }
9168 if (is_object($object) && $object->element == 'contrat') {
9169 $typeforonlinepayment = 'contract';
9170 }
9171 if (is_object($object) && $object->element == 'fichinter') {
9172 $typeforonlinepayment = 'ficheinter';
9173 }
9174
9175 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9176 $paymenturl = $url;
9177 }
9178
9179 if ($object->id > 0) {
9180 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9181 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9182
9183 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9184 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9185 } else {
9186 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9187 }
9188 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9189 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9190 } else {
9191 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9192 }
9193 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9194 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9195 } else {
9196 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9197 }
9198 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9199 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9200 } else {
9201 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9202 }
9203 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9204 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9205 } else {
9206 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9207 }
9208 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9209 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9210 } else {
9211 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9212 }
9213
9214 if (is_object($object) && $object->element == 'propal') {
9215 '@phan-var-force Propal $object';
9216 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9217 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9218 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9219 }
9220 if (is_object($object) && $object->element == 'commande') {
9221 '@phan-var-force Commande $object';
9222 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9223 }
9224 if (is_object($object) && $object->element == 'facture') {
9225 '@phan-var-force Facture $object';
9226 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9227 }
9228 if (is_object($object) && $object->element == 'contrat') {
9229 '@phan-var-force Contrat $object';
9230 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9231 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9232 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9233 }
9234 if (is_object($object) && $object->element == 'fichinter') {
9235 '@phan-var-force Fichinter $object';
9236 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9237 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9238 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9239 }
9240 if (is_object($object) && $object->element == 'supplier_proposal') {
9241 '@phan-var-force SupplierProposal $object';
9242 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9243 }
9244 if (is_object($object) && $object->element == 'invoice_supplier') {
9245 '@phan-var-force FactureFournisseur $object';
9246 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9247 }
9248 if (is_object($object) && $object->element == 'shipping') {
9249 '@phan-var-force Expedition $object';
9250 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9251 }
9252 }
9253
9254 if (is_object($object) && $object->element == 'action') {
9255 '@phan-var-force ActionComm $object';
9256 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9257 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9258 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9259 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9260 }
9261 }
9262 }
9263 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9264 '@phan-var-force Facture|FactureRec $object';
9265 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9266
9267 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
9268 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
9269 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', 0, $outputlangs) : null) : '';
9270 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', 0, $outputlangs) : null) : '';
9271
9272 $already_payed_all = 0;
9273 if (is_object($object) && ($object instanceof Facture)) {
9274 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9275 }
9276
9277 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9278 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9279 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9280
9281 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9282 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9283 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9284
9285 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9286
9287 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9288 $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)) : '';
9289 $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)) : '';
9290
9291 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9292 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9293 }
9294 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9295 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9296 }
9297
9298 // Amount keys formatted in a currency
9299 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9300 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9301 $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) : '';
9302 $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)) : '';
9303 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9304 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9305 }
9306 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9307 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9308 }
9309 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9310 if ($onlykey != 2) {
9311 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9312 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9313 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9314 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9315 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9316 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9317 }
9318 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9319 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9320 }
9321 }
9322
9323 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9324 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9325 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9326 // TODO Add other keys for foreign multicurrency
9327
9328 // For backward compatibility
9329 if ($onlykey != 2) {
9330 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9331 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9332 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9333 }
9334 }
9335
9336
9337 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9338 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9339
9340 $now = dol_now();
9341
9342 $tmp = dol_getdate($now, true);
9343 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9344 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9345 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9346 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9347
9348 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9349
9350 $substitutionarray = array_merge($substitutionarray, array(
9351 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9352 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9353 '__DAY__' => (string) $tmp['mday'],
9354 '__DAY_TEXT__' => $daytext, // Monday
9355 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9356 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9357 '__MONTH__' => (string) $tmp['mon'],
9358 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9359 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9360 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9361 '__YEAR__' => (string) $tmp['year'],
9362 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9363 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9364 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9365 '__NEXT_DAY__' => (string) $tmp4['day'],
9366 '__NEXT_MONTH__' => (string) $tmp5['month'],
9367 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9368 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9369 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9370 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9371 ));
9372 }
9373
9374 if (isModEnabled('multicompany')) {
9375 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9376 }
9377 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9378 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9379 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9380 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9381 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9382 }
9383
9384 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9385
9386 return $substitutionarray;
9387}
9388
9405function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9406{
9407 global $conf, $db, $langs;
9408
9409 if (!is_array($substitutionarray)) {
9410 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9411 }
9412
9413 if (empty($outputlangs)) {
9414 $outputlangs = $langs;
9415 }
9416
9417 // Is initial text HTML or simple text ?
9418 $msgishtml = 0;
9419 if (dol_textishtml($text, 1)) {
9420 $msgishtml = 1;
9421 }
9422
9423 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9424 if (is_object($outputlangs)) {
9425 $reg = array();
9426 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9427 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9428 $tmp = explode('|', $reg[1]);
9429 if (!empty($tmp[1])) {
9430 $outputlangs->load($tmp[1]);
9431 }
9432
9433 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9434
9435 if (empty($converttextinhtmlifnecessary)) {
9436 // convert $newval into HTML is necessary
9437 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9438 } else {
9439 if (! $msgishtml) {
9440 $valueishtml = dol_textishtml($value, 1);
9441 //var_dump("valueishtml=".$valueishtml);
9442
9443 if ($valueishtml) {
9444 $text = dol_htmlentitiesbr($text);
9445 $msgishtml = 1;
9446 }
9447 } else {
9448 $value = dol_nl2br("$value");
9449 }
9450
9451 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9452 }
9453 }
9454 }
9455
9456 // Make substitution for constant keys.
9457 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9458 $reg = array();
9459 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9460 $keyfound = $reg[1];
9461 if (isASecretKey($keyfound)) {
9462 $value = '*****forbidden*****';
9463 } else {
9464 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9465 }
9466
9467 if (empty($converttextinhtmlifnecessary)) {
9468 // convert $newval into HTML is necessary
9469 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9470 } else {
9471 if (! $msgishtml) {
9472 $valueishtml = dol_textishtml($value, 1);
9473
9474 if ($valueishtml) {
9475 $text = dol_htmlentitiesbr($text);
9476 $msgishtml = 1;
9477 }
9478 } else {
9479 $value = dol_nl2br("$value");
9480 }
9481
9482 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9483 }
9484 }
9485
9486 // Make substitution for array $substitutionarray
9487 foreach ($substitutionarray as $key => $value) {
9488 if (!isset($value)) {
9489 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9490 }
9491
9492 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9493 $value = ''; // Protection
9494 }
9495
9496 if (empty($converttextinhtmlifnecessary)) {
9497 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9498 } else {
9499 if (! $msgishtml) {
9500 $valueishtml = dol_textishtml($value, 1);
9501
9502 if ($valueishtml) {
9503 $text = dol_htmlentitiesbr($text);
9504 $msgishtml = 1;
9505 }
9506 } else {
9507 $value = dol_nl2br("$value");
9508 }
9509 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9510 }
9511 }
9512
9513 // TODO Implement the lazyload substitution
9514 /*
9515 add a loop to scan $substitutionarray:
9516 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.
9517 If no, we don't need to make replacement, so we do nothing.
9518 If yes, we can make the substitution:
9519
9520 include_once $path;
9521 $tmpobj = new $class($db);
9522 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9523 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9524 */
9525 $memory_object_list = array();
9526 foreach ($substitutionarray as $key => $value) {
9527 $lazy_load_arr = array();
9528 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9529 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9530 $key_to_substitute = $lazy_load_arr[1];
9531 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9532 $param_arr = explode(':', $value);
9533 // path:class:method:id
9534 if (count($param_arr) == 4) {
9535 $path = $param_arr[0];
9536 $class = $param_arr[1];
9537 $method = $param_arr[2];
9538 $id = (int) $param_arr[3];
9539
9540 // load class file and init object list in memory
9541 if (!isset($memory_object_list[$class])) {
9542 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9543 require_once DOL_DOCUMENT_ROOT . $path;
9544 if (class_exists($class)) {
9545 $memory_object_list[$class] = array(
9546 'list' => array(),
9547 );
9548 }
9549 }
9550 }
9551
9552 // fetch object and set substitution
9553 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9554 if (method_exists($class, $method)) {
9555 if (!isset($memory_object_list[$class]['list'][$id])) {
9556 $tmpobj = new $class($db);
9557 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9558 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9559 $memory_object_list[$class]['list'][$id] = $tmpobj;
9560 } else {
9561 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9562 $tmpobj = $memory_object_list[$class]['list'][$id];
9563 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9564 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9565 }
9566
9567 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9568 }
9569 }
9570 }
9571 }
9572 }
9573 }
9574 }
9575
9576 return $text;
9577}
9578
9591function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9592{
9593 global $conf, $user;
9594
9595 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9596
9597 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9598
9599 // Check if there is external substitution to do, requested by plugins
9600 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9601
9602 foreach ($dirsubstitutions as $reldir) {
9603 $dir = dol_buildpath($reldir, 0);
9604
9605 // Check if directory exists
9606 if (!dol_is_dir($dir)) {
9607 continue;
9608 }
9609
9610 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9611 foreach ($substitfiles as $substitfile) {
9612 $reg = array();
9613 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9614 $module = $reg[1];
9615
9616 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9617 // Include the user's functions file
9618 require_once $dir.$substitfile['name'];
9619 // Call the user's function, and only if it is defined
9620 $function_name = $module."_".$callfunc;
9621 if (function_exists($function_name)) {
9622 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9623 }
9624 }
9625 }
9626 }
9627 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9628 // to list all tags in odt template
9629 $tags = '';
9630 foreach ($substitutionarray as $key => $value) {
9631 $tags .= '{'.$key.'} => '.$value."\n";
9632 }
9633 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9634 }
9635}
9636
9646function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9647{
9648 print get_date_range($date_start, $date_end, $format, $outputlangs);
9649}
9650
9661function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9662{
9663 global $langs;
9664
9665 $out = '';
9666
9667 if (!is_object($outputlangs)) {
9668 $outputlangs = $langs;
9669 }
9670
9671 if ($date_start && $date_end) {
9672 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9673 }
9674 if ($date_start && !$date_end) {
9675 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9676 }
9677 if (!$date_start && $date_end) {
9678 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9679 }
9680
9681 return $out;
9682}
9683
9692function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9693{
9694 global $conf;
9695
9696 $ret = '';
9697 // If order not defined, we use the setup
9698 if ($nameorder < 0) {
9699 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9700 }
9701 if ($nameorder == 1) {
9702 $ret .= $firstname;
9703 if ($firstname && $lastname) {
9704 $ret .= ' ';
9705 }
9706 $ret .= $lastname;
9707 } elseif ($nameorder == 2 || $nameorder == 3) {
9708 $ret .= $firstname;
9709 if (empty($ret) && $nameorder == 3) {
9710 $ret .= $lastname;
9711 }
9712 } else { // 0, 4 or 5
9713 $ret .= $lastname;
9714 if (empty($ret) && $nameorder == 5) {
9715 $ret .= $firstname;
9716 }
9717 if ($nameorder == 0) {
9718 if ($firstname && $lastname) {
9719 $ret .= ' ';
9720 }
9721 $ret .= $firstname;
9722 }
9723 }
9724 return $ret;
9725}
9726
9727
9739function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0)
9740{
9741 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9742 if (!is_array($mesgs)) {
9743 $mesgs = trim((string) $mesgs);
9744 // If mesgs is a not an empty string
9745 if ($mesgs) {
9746 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9747 return;
9748 }
9749 $_SESSION['dol_events'][$style][] = $mesgs;
9750 }
9751 } else {
9752 // If mesgs is an array
9753 foreach ($mesgs as $mesg) {
9754 $mesg = trim((string) $mesg);
9755 if ($mesg) {
9756 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
9757 return;
9758 }
9759 $_SESSION['dol_events'][$style][] = $mesg;
9760 }
9761 }
9762 }
9763}
9764
9777function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0)
9778{
9779 if (empty($mesg) && empty($mesgs)) {
9780 dol_syslog("Try to add a message in stack, but value to add is empty message", LOG_WARNING);
9781 } else {
9782 if ($messagekey) {
9783 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
9784 // TODO
9785 $mesg .= '';
9786 }
9787 if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
9788 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
9789 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
9790 }
9791 if (empty($mesgs)) {
9792 setEventMessage($mesg, $style, $noduplicate);
9793 } else {
9794 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
9795 setEventMessage($mesg, $style, $noduplicate); // Add message string if not already into array
9796 }
9797 setEventMessage($mesgs, $style, $noduplicate);
9798 }
9799 }
9800 }
9801}
9802
9812function dol_htmloutput_events($disabledoutputofmessages = 0)
9813{
9814 // Show mesgs
9815 if (isset($_SESSION['dol_events']['mesgs'])) {
9816 if (empty($disabledoutputofmessages)) {
9817 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
9818 }
9819 unset($_SESSION['dol_events']['mesgs']);
9820 }
9821 // Show errors
9822 if (isset($_SESSION['dol_events']['errors'])) {
9823 if (empty($disabledoutputofmessages)) {
9824 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
9825 }
9826 unset($_SESSION['dol_events']['errors']);
9827 }
9828
9829 // Show warnings
9830 if (isset($_SESSION['dol_events']['warnings'])) {
9831 if (empty($disabledoutputofmessages)) {
9832 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
9833 }
9834 unset($_SESSION['dol_events']['warnings']);
9835 }
9836}
9837
9852function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
9853{
9854 global $conf, $langs;
9855
9856 $ret = 0;
9857 $return = '';
9858 $out = '';
9859 $divstart = $divend = '';
9860
9861 // If inline message with no format, we add it.
9862 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
9863 $divstart = '<div class="'.$style.' clearboth">';
9864 $divend = '</div>';
9865 }
9866
9867 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
9868 $langs->load("errors");
9869 $out .= $divstart;
9870 if (is_array($mesgarray) && count($mesgarray)) {
9871 foreach ($mesgarray as $message) {
9872 $ret++;
9873 $out .= $langs->trans($message);
9874 if ($ret < count($mesgarray)) {
9875 $out .= "<br>\n";
9876 }
9877 }
9878 }
9879 if ($mesgstring) {
9880 $ret++;
9881 $out .= $langs->trans($mesgstring);
9882 }
9883 $out .= $divend;
9884 }
9885
9886 if ($out) {
9887 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
9888 $return = '<script nonce="'.getNonce().'">
9889 $(document).ready(function() {
9890 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
9891 if (block) {
9892 $.dolEventValid("","'.dol_escape_js($out).'");
9893 } else {
9894 /* jnotify(message, preset of message type, keepmessage) */
9895 $.jnotify("'.dol_escape_js($out).'",
9896 "'.($style == "ok" ? 3000 : $style).'",
9897 '.($style == "ok" ? "false" : "true").',
9898 { remove: function (){} } );
9899 }
9900 });
9901 </script>';
9902 } else {
9903 $return = $out;
9904 }
9905 }
9906
9907 return $return;
9908}
9909
9921function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
9922{
9923 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
9924}
9925
9939function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
9940{
9941 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
9942 return;
9943 }
9944
9945 $iserror = 0;
9946 $iswarning = 0;
9947 if (is_array($mesgarray)) {
9948 foreach ($mesgarray as $val) {
9949 if ($val && preg_match('/class="error"/i', $val)) {
9950 $iserror++;
9951 break;
9952 }
9953 if ($val && preg_match('/class="warning"/i', $val)) {
9954 $iswarning++;
9955 break;
9956 }
9957 }
9958 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
9959 $iserror++;
9960 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
9961 $iswarning++;
9962 }
9963 if ($style == 'error') {
9964 $iserror++;
9965 }
9966 if ($style == 'warning') {
9967 $iswarning++;
9968 }
9969
9970 if ($iserror || $iswarning) {
9971 // Remove div from texts
9972 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
9973 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
9974 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
9975 // Remove div from texts array
9976 if (is_array($mesgarray)) {
9977 $newmesgarray = array();
9978 foreach ($mesgarray as $val) {
9979 if (is_string($val)) {
9980 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
9981 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
9982 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
9983 $newmesgarray[] = $tmpmesgstring;
9984 } else {
9985 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
9986 }
9987 }
9988 $mesgarray = $newmesgarray;
9989 }
9990 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
9991 } else {
9992 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
9993 }
9994}
9995
10007function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10008{
10009 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10010}
10011
10025function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10026{
10027 // Clean parameters
10028 $order = strtolower($order);
10029
10030 if (is_array($array)) {
10031 $sizearray = count($array);
10032 if ($sizearray > 0) {
10033 $temp = array();
10034 foreach (array_keys($array) as $key) {
10035 if (is_object($array[$key])) {
10036 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10037 } else {
10038 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10039 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10040 }
10041 if ($natsort == -1) {
10042 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10043 }
10044 }
10045
10046 if (empty($natsort) || $natsort == -1) {
10047 if ($order == 'asc') {
10048 asort($temp);
10049 } else {
10050 arsort($temp);
10051 }
10052 } else {
10053 if ($case_sensitive) {
10054 natsort($temp);
10055 } else {
10056 natcasesort($temp); // natecasesort is not sensible to case
10057 }
10058 if ($order != 'asc') {
10059 $temp = array_reverse($temp, true);
10060 }
10061 }
10062
10063 $sorted = array();
10064
10065 foreach (array_keys($temp) as $key) {
10066 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10067 }
10068
10069 return $sorted;
10070 }
10071 }
10072 return $array;
10073}
10074
10075
10083function utf8_check($str)
10084{
10085 $str = (string) $str; // Sometimes string is an int.
10086
10087 // We must use here a binary strlen function (so not dol_strlen)
10088 $strLength = strlen($str);
10089 for ($i = 0; $i < $strLength; $i++) {
10090 if (ord($str[$i]) < 0x80) {
10091 continue; // 0bbbbbbb
10092 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10093 $n = 1; // 110bbbbb
10094 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10095 $n = 2; // 1110bbbb
10096 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10097 $n = 3; // 11110bbb
10098 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10099 $n = 4; // 111110bb
10100 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10101 $n = 5; // 1111110b
10102 } else {
10103 return false; // Does not match any model
10104 }
10105 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10106 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10107 return false;
10108 }
10109 }
10110 }
10111 return true;
10112}
10113
10121function utf8_valid($str)
10122{
10123 /* 2 other methods to test if string is utf8
10124 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10125 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10126 */
10127 return preg_match('//u', $str) ? true : false;
10128}
10129
10130
10137function ascii_check($str)
10138{
10139 if (function_exists('mb_check_encoding')) {
10140 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10141 if (!mb_check_encoding($str, 'ASCII')) {
10142 return false;
10143 }
10144 } else {
10145 if (preg_match('/[^\x00-\x7f]/', $str)) {
10146 return false; // Contains a byte > 7f
10147 }
10148 }
10149
10150 return true;
10151}
10152
10153
10161function dol_osencode($str)
10162{
10163 $tmp = ini_get("unicode.filesystem_encoding");
10164 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10165 $tmp = 'iso-8859-1'; // By default for windows
10166 }
10167 if (empty($tmp)) {
10168 $tmp = 'utf-8'; // By default for other
10169 }
10170 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10171 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10172 }
10173
10174 if ($tmp == 'iso-8859-1') {
10175 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10176 }
10177 return $str;
10178}
10179
10180
10195function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
10196{
10197 global $conf;
10198
10199 // If key empty
10200 if ($key == '') {
10201 return 0;
10202 }
10203
10204 // Check in cache
10205 if (isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10206 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10207 }
10208
10209 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10210
10211 $sql = "SELECT ".$fieldid." as valuetoget";
10212 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10213 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10214 if (!empty($entityfilter)) {
10215 $sql .= " AND entity IN (".getEntity($tablename).")";
10216 }
10217 if ($filters) {
10218 $sql .= $filters;
10219 }
10220
10221 $resql = $db->query($sql);
10222 if ($resql) {
10223 $obj = $db->fetch_object($resql);
10224 if ($obj) {
10225 $conf->cache['codeid'][$tablename][$key][$fieldid] = $obj->valuetoget;
10226 } else {
10227 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10228 }
10229 $db->free($resql);
10230
10231 return $conf->cache['codeid'][$tablename][$key][$fieldid];
10232 } else {
10233 return -1;
10234 }
10235}
10236
10246function isStringVarMatching($var, $regextext, $matchrule = 1)
10247{
10248 if ($matchrule == 1) {
10249 if ($var == 'mainmenu') {
10250 global $mainmenu;
10251 return (preg_match('/^'.$regextext.'/', $mainmenu));
10252 } elseif ($var == 'leftmenu') {
10253 global $leftmenu;
10254 return (preg_match('/^'.$regextext.'/', $leftmenu));
10255 } else {
10256 return 'This variable is not accessible with dol_eval';
10257 }
10258 } else {
10259 return 'This value for matchrule is not implemented';
10260 }
10261}
10262
10263
10273function verifCond($strToEvaluate, $onlysimplestring = '1')
10274{
10275 //print $strToEvaluate."<br>\n";
10276 $rights = true;
10277 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10278 //var_dump($strToEvaluate);
10279 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10280 $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
10281 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10282 //var_dump($rights);
10283 }
10284 return $rights;
10285}
10286
10301function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10302{
10303 // Only this global variables can be read by eval function and returned to caller
10304 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10305 global $db, $langs, $user, $website, $websitepage;
10306 global $action, $mainmenu, $leftmenu;
10307 global $mysoc;
10308 global $objectoffield; // To allow the use of $objectoffield in computed fields
10309
10310 // Old variables used
10311 global $object;
10312 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10313
10314 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10315 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10316 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10317 }
10318
10319 try {
10320 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10321 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10322 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10323 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10324 // We must accept with 2: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found"
10325
10326 // Check if there is dynamic call (first we check chars are all into use a whitelist chars)
10327 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10328 if ($onlysimplestring == '2') {
10329 $specialcharsallowed .= '[]';
10330 }
10331 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10332 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10333 }
10334 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10335 if ($returnvalue) {
10336 return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s;
10337 } else {
10338 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s, LOG_WARNING);
10339 return '';
10340 }
10341 }
10342
10343 // Check if there is dynamic call (first we use black list patterns)
10344 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10345 if ($returnvalue) {
10346 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;
10347 } else {
10348 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);
10349 return '';
10350 }
10351 }
10352
10353 // Now we check if we try dynamic call (by removing white list pattern of using parenthesis then testing if a parenthesis exists)
10354 $savescheck = '';
10355 $scheck = $s;
10356 while ($scheck && $savescheck != $scheck) {
10357 $savescheck = $scheck;
10358 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10359 $scheck = preg_replace('/^\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10360 $scheck = preg_replace('/\s\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in 'if ($a == 1)'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10361 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10362 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10363 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10364 }
10365 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10366 if (strpos($scheck, '(') !== false) {
10367 if ($returnvalue) {
10368 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10369 } else {
10370 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);
10371 return '';
10372 }
10373 }
10374
10375 // TODO
10376 // We can exclude $ char that are not:
10377 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10378 }
10379 if (is_array($s) || $s === 'Array') {
10380 if ($returnvalue) {
10381 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10382 } else {
10383 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10384 return '';
10385 }
10386 }
10387 if (strpos($s, '::') !== false) {
10388 if ($returnvalue) {
10389 return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
10390 } else {
10391 dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s, LOG_WARNING);
10392 return '';
10393 }
10394 }
10395 if (strpos($s, '`') !== false) {
10396 if ($returnvalue) {
10397 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10398 } else {
10399 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10400 return '';
10401 }
10402 }
10403 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10404 if ($returnvalue) {
10405 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10406 } else {
10407 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10408 return '';
10409 }
10410 }
10411
10412 // We block use of php exec or php file functions
10413 $forbiddenphpstrings = array('$$', '$_', '}[');
10414 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10415
10416 $forbiddenphpfunctions = array();
10417 // @phpcs:ignore
10418 $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
10419 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10420 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10421 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10422 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
10423 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10424 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10425 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10426 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
10427 $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
10428
10429 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10430
10431 $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
10432
10433 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10434
10435 do {
10436 $oldstringtoclean = $s;
10437 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10438 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10439 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10440 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10441 } while ($oldstringtoclean != $s);
10442
10443
10444 if (strpos($s, '__forbiddenstring__') !== false) {
10445 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10446 if ($returnvalue) {
10447 return 'Bad string syntax to evaluate: '.$s;
10448 } else {
10449 dol_syslog('Bad string syntax to evaluate: '.$s);
10450 return '';
10451 }
10452 }
10453
10454 //print $s."<br>\n";
10455 if ($returnvalue) {
10456 if ($hideerrors) {
10457 ob_start(); // An evaluation has no reason to output data
10458 $isObBufferActive = true;
10459 $tmps = @eval('return '.$s.';');
10460 $tmpo = ob_get_clean();
10461 $isObBufferActive = false;
10462 if ($tmpo) {
10463 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10464 }
10465 return $tmps;
10466 } else {
10467 ob_start(); // An evaluation has no reason to output data
10468 $isObBufferActive = true;
10469 $tmps = eval('return '.$s.';');
10470 $tmpo = ob_get_clean();
10471 $isObBufferActive = false;
10472 if ($tmpo) {
10473 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10474 }
10475 return $tmps;
10476 }
10477 } else {
10478 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10479 if ($hideerrors) {
10480 @eval($s);
10481 } else {
10482 eval($s);
10483 }
10484 return '';
10485 }
10486 } catch (Error $e) {
10487 if ($isObBufferActive) {
10488 // Clean up buffer which was left behind due to exception.
10489 $tmpo = ob_get_clean();
10490 $isObBufferActive = false;
10491 }
10492 $error = 'dol_eval try/catch error : ';
10493 $error .= $e->getMessage();
10494 dol_syslog($error, LOG_WARNING);
10495 if ($returnvalue) {
10496 return 'Exception during evaluation: '.$s;
10497 } else {
10498 return '';
10499 }
10500 }
10501}
10502
10510function dol_validElement($element)
10511{
10512 return (trim($element) != '');
10513}
10514
10523function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10524{
10525 if (empty($codelang)) {
10526 return '';
10527 }
10528
10529 if ($codelang == 'auto') {
10530 return '<span class="fa fa-language"></span>';
10531 }
10532
10533 $langtocountryflag = array(
10534 'ar_AR' => '',
10535 'ca_ES' => 'catalonia',
10536 'da_DA' => 'dk',
10537 'fr_CA' => 'mq',
10538 'sv_SV' => 'se',
10539 'sw_SW' => 'unknown',
10540 'AQ' => 'unknown',
10541 'CW' => 'unknown',
10542 'IM' => 'unknown',
10543 'JE' => 'unknown',
10544 'MF' => 'unknown',
10545 'BL' => 'unknown',
10546 'SX' => 'unknown'
10547 );
10548
10549 if (isset($langtocountryflag[$codelang])) {
10550 $flagImage = $langtocountryflag[$codelang];
10551 } else {
10552 $tmparray = explode('_', $codelang);
10553 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10554 }
10555
10556 $morecss = '';
10557 $reg = array();
10558 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10559 $morecss = $reg[1];
10560 $moreatt = "";
10561 }
10562
10563 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10564 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10565}
10566
10574function getLanguageCodeFromCountryCode($countrycode)
10575{
10576 global $mysoc;
10577
10578 if (empty($countrycode)) {
10579 return null;
10580 }
10581
10582 if (strtoupper($countrycode) == 'MQ') {
10583 return 'fr_CA';
10584 }
10585 if (strtoupper($countrycode) == 'SE') {
10586 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10587 }
10588 if (strtoupper($countrycode) == 'CH') {
10589 if ($mysoc->country_code == 'FR') {
10590 return 'fr_CH';
10591 }
10592 if ($mysoc->country_code == 'DE') {
10593 return 'de_CH';
10594 }
10595 if ($mysoc->country_code == 'IT') {
10596 return 'it_CH';
10597 }
10598 }
10599
10600 // Locale list taken from:
10601 // http://stackoverflow.com/questions/3191664/
10602 // list-of-all-locales-and-their-short-codes
10603 $locales = array(
10604 'af-ZA',
10605 'am-ET',
10606 'ar-AE',
10607 'ar-BH',
10608 'ar-DZ',
10609 'ar-EG',
10610 'ar-IQ',
10611 'ar-JO',
10612 'ar-KW',
10613 'ar-LB',
10614 'ar-LY',
10615 'ar-MA',
10616 'ar-OM',
10617 'ar-QA',
10618 'ar-SA',
10619 'ar-SY',
10620 'ar-TN',
10621 'ar-YE',
10622 //'as-IN', // Moved after en-IN
10623 'ba-RU',
10624 'be-BY',
10625 'bg-BG',
10626 'bn-BD',
10627 //'bn-IN', // Moved after en-IN
10628 'bo-CN',
10629 'br-FR',
10630 'ca-ES',
10631 'co-FR',
10632 'cs-CZ',
10633 'cy-GB',
10634 'da-DK',
10635 'de-AT',
10636 'de-CH',
10637 'de-DE',
10638 'de-LI',
10639 'de-LU',
10640 'dv-MV',
10641 'el-GR',
10642 'en-AU',
10643 'en-BZ',
10644 'en-CA',
10645 'en-GB',
10646 'en-IE',
10647 'en-IN',
10648 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10649 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10650 'en-JM',
10651 'en-MY',
10652 'en-NZ',
10653 'en-PH',
10654 'en-SG',
10655 'en-TT',
10656 'en-US',
10657 'en-ZA',
10658 'en-ZW',
10659 'es-AR',
10660 'es-BO',
10661 'es-CL',
10662 'es-CO',
10663 'es-CR',
10664 'es-DO',
10665 'es-EC',
10666 'es-ES',
10667 'es-GT',
10668 'es-HN',
10669 'es-MX',
10670 'es-NI',
10671 'es-PA',
10672 'es-PE',
10673 'es-PR',
10674 'es-PY',
10675 'es-SV',
10676 'es-US',
10677 'es-UY',
10678 'es-VE',
10679 'et-EE',
10680 'eu-ES',
10681 'fa-IR',
10682 'fi-FI',
10683 'fo-FO',
10684 'fr-BE',
10685 'fr-CA',
10686 'fr-CH',
10687 'fr-FR',
10688 'fr-LU',
10689 'fr-MC',
10690 'fy-NL',
10691 'ga-IE',
10692 'gd-GB',
10693 'gl-ES',
10694 'gu-IN',
10695 'he-IL',
10696 'hi-IN',
10697 'hr-BA',
10698 'hr-HR',
10699 'hu-HU',
10700 'hy-AM',
10701 'id-ID',
10702 'ig-NG',
10703 'ii-CN',
10704 'is-IS',
10705 'it-CH',
10706 'it-IT',
10707 'ja-JP',
10708 'ka-GE',
10709 'kk-KZ',
10710 'kl-GL',
10711 'km-KH',
10712 'kn-IN',
10713 'ko-KR',
10714 'ky-KG',
10715 'lb-LU',
10716 'lo-LA',
10717 'lt-LT',
10718 'lv-LV',
10719 'mi-NZ',
10720 'mk-MK',
10721 'ml-IN',
10722 'mn-MN',
10723 'mr-IN',
10724 'ms-BN',
10725 'ms-MY',
10726 'mt-MT',
10727 'nb-NO',
10728 'ne-NP',
10729 'nl-BE',
10730 'nl-NL',
10731 'nn-NO',
10732 'oc-FR',
10733 'or-IN',
10734 'pa-IN',
10735 'pl-PL',
10736 'ps-AF',
10737 'pt-BR',
10738 'pt-PT',
10739 'rm-CH',
10740 'ro-MD',
10741 'ro-RO',
10742 'ru-RU',
10743 'rw-RW',
10744 'sa-IN',
10745 'se-FI',
10746 'se-NO',
10747 'se-SE',
10748 'si-LK',
10749 'sk-SK',
10750 'sl-SI',
10751 'sq-AL',
10752 'sv-FI',
10753 'sv-SE',
10754 'sw-KE',
10755 'ta-IN',
10756 'te-IN',
10757 'th-TH',
10758 'tk-TM',
10759 'tn-ZA',
10760 'tr-TR',
10761 'tt-RU',
10762 'ug-CN',
10763 'uk-UA',
10764 'ur-PK',
10765 'vi-VN',
10766 'wo-SN',
10767 'xh-ZA',
10768 'yo-NG',
10769 'zh-CN',
10770 'zh-HK',
10771 'zh-MO',
10772 'zh-SG',
10773 'zh-TW',
10774 'zu-ZA',
10775 );
10776
10777 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
10778 if (in_array($buildprimarykeytotest, $locales)) {
10779 return strtolower($countrycode).'_'.strtoupper($countrycode);
10780 }
10781
10782 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
10783 foreach ($locales as $locale) {
10784 $locale_language = locale_get_primary_language($locale);
10785 $locale_region = locale_get_region($locale);
10786 if (strtoupper($countrycode) == $locale_region) {
10787 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
10788 return strtolower($locale_language).'_'.strtoupper($locale_region);
10789 }
10790 }
10791 } else {
10792 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
10793 }
10794
10795 return null;
10796}
10797
10828function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
10829{
10830 global $hookmanager, $db;
10831
10832 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
10833 foreach ($conf->modules_parts['tabs'][$type] as $value) {
10834 $values = explode(':', $value);
10835
10836 $reg = array();
10837 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
10838 $newtab = array();
10839 $postab = $h;
10840 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
10841 $str = $values[1];
10842 $posstart = strpos($str, '(');
10843 if ($posstart > 0) {
10844 $posend = strpos($str, ')');
10845 if ($posstart > 0) {
10846 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
10847 if (is_numeric($res1)) {
10848 $postab = (int) $res1;
10849 $values[1] = '+' . substr($str, $posend + 1);
10850 }
10851 }
10852 }
10853 if (count($values) == 6) {
10854 // new declaration with permissions:
10855 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10856 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10857 if ($values[0] != $type) {
10858 continue;
10859 }
10860
10861 if (verifCond($values[4], '2')) {
10862 if ($values[3]) {
10863 if ($filterorigmodule) { // If a filter of module origin has been requested
10864 if (strpos($values[3], '@')) { // This is an external module
10865 if ($filterorigmodule != 'external') {
10866 continue;
10867 }
10868 } else { // This looks a core module
10869 if ($filterorigmodule != 'core') {
10870 continue;
10871 }
10872 }
10873 }
10874 $langs->load($values[3]);
10875 }
10876 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10877 // If label is "SUBSTITUION_..."
10878 $substitutionarray = array();
10879 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10880 $label = make_substitutions($reg[1], $substitutionarray);
10881 } else {
10882 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
10883 $labeltemp = explode(',', $values[2]);
10884 $label = $langs->trans($labeltemp[0]);
10885
10886 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
10887 dol_include_once($labeltemp[2]);
10888 $classtoload = $labeltemp[1];
10889 if (class_exists($classtoload)) {
10890 $obj = new $classtoload($db);
10891 $function = $labeltemp[3];
10892 if ($obj && $function && method_exists($obj, $function)) {
10893 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10894 $nbrec = $obj->$function($object->id, $obj);
10895 if (!empty($nbrec)) {
10896 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
10897 }
10898 }
10899 }
10900 }
10901 }
10902
10903 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
10904 $newtab[1] = $label;
10905 $newtab[2] = str_replace('+', '', $values[1]);
10906 $h++;
10907 } else {
10908 continue;
10909 }
10910 } elseif (count($values) == 5) { // case deprecated
10911 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
10912
10913 if ($values[0] != $type) {
10914 continue;
10915 }
10916 if ($values[3]) {
10917 if ($filterorigmodule) { // If a filter of module origin has been requested
10918 if (strpos($values[3], '@')) { // This is an external module
10919 if ($filterorigmodule != 'external') {
10920 continue;
10921 }
10922 } else { // This looks a core module
10923 if ($filterorigmodule != 'core') {
10924 continue;
10925 }
10926 }
10927 }
10928 $langs->load($values[3]);
10929 }
10930 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10931 $substitutionarray = array();
10932 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10933 $label = make_substitutions($reg[1], $substitutionarray);
10934 } else {
10935 $label = $langs->trans($values[2]);
10936 }
10937
10938 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
10939 $newtab[1] = $label;
10940 $newtab[2] = str_replace('+', '', $values[1]);
10941 $h++;
10942 }
10943 // set tab at its position
10944 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
10945 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
10946 if ($values[0] != $type) {
10947 continue;
10948 }
10949 $tabname = str_replace('-', '', $values[1]);
10950 foreach ($head as $key => $val) {
10951 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
10952 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
10953 if ($head[$key][2] == $tabname && $condition) {
10954 unset($head[$key]);
10955 break;
10956 }
10957 }
10958 }
10959 }
10960 }
10961
10962 // No need to make a return $head. Var is modified as a reference
10963 if (!empty($hookmanager)) {
10964 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
10965 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
10966 if ($reshook > 0) { // Hook ask to replace completely the array
10967 $head = $hookmanager->resArray;
10968 } else { // Hook
10969 $head = array_merge($head, $hookmanager->resArray);
10970 }
10971 $h = count($head);
10972 }
10973}
10974
10986function printCommonFooter($zone = 'private')
10987{
10988 global $conf, $hookmanager, $user, $langs;
10989 global $debugbar;
10990 global $action;
10991 global $micro_start_time;
10992
10993 if ($zone == 'private') {
10994 print "\n".'<!-- Common footer for private page -->'."\n";
10995 } else {
10996 print "\n".'<!-- Common footer for public page -->'."\n";
10997 }
10998
10999 // A div to store page_y POST parameter so we can read it using javascript
11000 print "\n<!-- A div to store page_y POST parameter -->\n";
11001 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11002
11003 $parameters = array();
11004 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11005 if (empty($reshook)) {
11006 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11007 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11008 }
11009
11010 print "\n";
11011 if (!empty($conf->use_javascript_ajax)) {
11012 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11013 print '<script>'."\n";
11014 print 'jQuery(document).ready(function() {'."\n";
11015
11016 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11017 print "\n";
11018 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11019 print 'jQuery("li.menuhider").click(function(event) {';
11020 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11021 print ' console.log("We click on .menuhider");'."\n";
11022 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11023 print '});'."\n";
11024 }
11025
11026 // Management of focus and mandatory for fields
11027 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"])))) {
11028 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11029 $relativepathstring = $_SERVER["PHP_SELF"];
11030 // Clean $relativepathstring
11031 if (constant('DOL_URL_ROOT')) {
11032 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11033 }
11034 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11035 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11036 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11037
11038 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11039 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11040 $qualified = 0;
11041 if ($defkey != '_noquery_') {
11042 $tmpqueryarraytohave = explode('&', $defkey);
11043 $foundintru = 0;
11044 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11045 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11046 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11047 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11048 $foundintru = 1;
11049 }
11050 }
11051 if (!$foundintru) {
11052 $qualified = 1;
11053 }
11054 //var_dump($defkey.'-'.$qualified);
11055 } else {
11056 $qualified = 1;
11057 }
11058
11059 if ($qualified) {
11060 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11061 foreach ($defval as $paramkey => $paramval) {
11062 // Set focus on field
11063 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11064 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11065 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11066 }
11067 }
11068 }
11069 }
11070 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11071 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11072 $qualified = 0;
11073 if ($defkey != '_noquery_') {
11074 $tmpqueryarraytohave = explode('&', $defkey);
11075 $foundintru = 0;
11076 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11077 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11078 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11079 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11080 $foundintru = 1;
11081 }
11082 }
11083 if (!$foundintru) {
11084 $qualified = 1;
11085 }
11086 //var_dump($defkey.'-'.$qualified);
11087 } else {
11088 $qualified = 1;
11089 }
11090
11091 if ($qualified) {
11092 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11093
11094 foreach ($defval as $paramkey => $paramval) {
11095 // Solution 1: Add handler on submit to check if mandatory fields are empty
11096 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11097 print "form.on('submit', function(event) {
11098 var submitter = event.originalEvent.submitter;
11099 if (submitter) {
11100 var buttonName = $(submitter).attr('name');
11101 if (buttonName == 'cancel') {
11102 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11103 return true;
11104 }
11105 }
11106
11107 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11108
11109 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11110 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11111
11112 if (tmptypefield == 'textarea') {
11113 // We must instead check the content of ckeditor
11114 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11115 if (tmpeditor) {
11116 tmpvalue = tmpeditor.getData();
11117 console.log('For textarea tmpvalue is '+tmpvalue);
11118 }
11119 }
11120
11121 let tmpvalueisempty = false;
11122 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '') {
11123 tmpvalueisempty = true;
11124 }
11125 if (tmpvalue === '0' && tmptypefield == 'select') {
11126 tmpvalueisempty = true;
11127 }
11128 if (tmpvalueisempty) {
11129 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11130 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11131 event.stopPropagation(); // Stop other handlers.
11132 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11133 return false;
11134 }
11135 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11136 return true;
11137 });
11138 \n";
11139
11140 // Solution 2: Add property 'required' on input
11141 // so browser will check value and try to focus on it when submitting the form.
11142 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11143 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11144 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11145 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11146 //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";
11147 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11148 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11149 // Add 'field required' class on closest td for all input elements : input, textarea and select
11150 //print '}, 500);'; // 500 milliseconds delay
11151
11152 // Now set the class "fieldrequired"
11153 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11154 }
11155
11156
11157 // If we submit using the cancel button, we remove the required attributes
11158 print 'jQuery("input[name=\'cancel\']").click(function() {
11159 console.log("We click on cancel button so removed all required attribute");
11160 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11161 });'."\n";
11162 }
11163 }
11164 }
11165 }
11166
11167 print '});'."\n";
11168
11169 // End of tuning
11170 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11171 print "\n";
11172 print "/* JS CODE TO ENABLE to add memory info */\n";
11173 print 'window.console && console.log("';
11174 if (getDolGlobalString('MEMCACHED_SERVER')) {
11175 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11176 }
11177 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11178 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11179 $micro_end_time = microtime(true);
11180 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11181 }
11182
11183 if (function_exists("memory_get_usage")) {
11184 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11185 }
11186 if (function_exists("memory_get_peak_usage")) {
11187 print ' - Real mem peak: '.memory_get_peak_usage(true);
11188 }
11189 if (function_exists("zend_loader_file_encoded")) {
11190 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11191 }
11192 print '");'."\n";
11193 }
11194
11195 print "\n".'</script>'."\n";
11196
11197 // Google Analytics
11198 // TODO Add a hook here
11199 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11200 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11201 foreach ($tmptagarray as $tmptag) {
11202 print "\n";
11203 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11204 print '
11205 <!-- Global site tag (gtag.js) - Google Analytics -->
11206 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11207 <script>
11208 window.dataLayer = window.dataLayer || [];
11209 function gtag(){dataLayer.push(arguments);}
11210 gtag(\'js\', new Date());
11211
11212 gtag(\'config\', \''.trim($tmptag).'\');
11213 </script>';
11214 print "\n";
11215 }
11216 }
11217 }
11218
11219 // Add Xdebug coverage of code
11220 if (defined('XDEBUGCOVERAGE')) {
11221 print_r(xdebug_get_code_coverage());
11222 }
11223
11224 // Add DebugBar data
11225 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11226 if (isset($debugbar['time'])) {
11227 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11228 $debugbar['time']->stopMeasure('pageaftermaster');
11229 }
11230 print '<!-- Output debugbar data -->'."\n";
11231 $renderer = $debugbar->getJavascriptRenderer();
11232 print $renderer->render();
11233 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11234 print "\n";
11235 print "<!-- Start of log output\n";
11236 //print '<div class="hidden">'."\n";
11237 foreach ($conf->logbuffer as $logline) {
11238 print $logline."<br>\n";
11239 }
11240 //print '</div>'."\n";
11241 print "End of log output -->\n";
11242 }
11243 }
11244}
11245
11255function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11256{
11257 if (is_null($string)) {
11258 return array();
11259 }
11260
11261 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11262 // This is a regex string
11263 $newdelimiter = $delimiter;
11264 } else {
11265 // This is a simple string
11266 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11267 $newdelimiter = preg_quote($delimiter, '/');
11268 }
11269
11270 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11271 $ka = array();
11272 foreach ($a as $s) { // each part
11273 if ($s) {
11274 if ($pos = strpos($s, $kv)) { // key/value delimiter
11275 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11276 } else { // key delimiter not found
11277 $ka[] = trim($s);
11278 }
11279 }
11280 }
11281 return $ka;
11282 }
11283
11284 return array();
11285}
11286
11287
11294function dol_set_focus($selector)
11295{
11296 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11297 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11298}
11299
11300
11308function dol_getmypid()
11309{
11310 if (!function_exists('getmypid')) {
11311 return mt_rand(99900000, 99965535);
11312 } else {
11313 return getmypid(); // May be a number on 64 bits (depending on OS)
11314 }
11315}
11316
11317
11339function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11340{
11341 global $db, $langs;
11342
11343 $value = trim($value);
11344
11345 if ($mode == 0) {
11346 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11347 }
11348 if ($mode == 1) {
11349 $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
11350 }
11351
11352 $value = preg_replace('/\s*\|\s*/', '|', $value);
11353
11354 $crits = explode(' ', $value);
11355 $res = '';
11356 if (!is_array($fields)) {
11357 $fields = array($fields);
11358 }
11359
11360 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11361 foreach ($crits as $crit) { // Loop on each AND criteria
11362 $crit = trim($crit);
11363 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11364 $newres = '';
11365 foreach ($fields as $field) {
11366 if ($mode == 1) {
11367 $tmpcrits = explode('|', $crit);
11368 $i3 = 0; // count the nb of valid criteria added for this current field
11369 foreach ($tmpcrits as $tmpcrit) {
11370 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11371 continue;
11372 }
11373 $tmpcrit = trim($tmpcrit);
11374
11375 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11376
11377 $operator = '=';
11378 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11379
11380 $reg = array();
11381 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11382 if (!empty($reg[1])) {
11383 $operator = $reg[1];
11384 }
11385 if ($newcrit != '') {
11386 $numnewcrit = price2num($newcrit);
11387 if (is_numeric($numnewcrit)) {
11388 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11389 } else {
11390 $newres .= '1 = 2'; // force false, we received a corrupted data
11391 }
11392 $i3++; // a criteria was added to string
11393 }
11394 }
11395 $i2++; // a criteria for 1 more field was added to string
11396 } elseif ($mode == 2 || $mode == -2) {
11397 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11398 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11399 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11400 if ($mode == -2) {
11401 $newres .= ' OR '.$field.' IS NULL';
11402 }
11403 $i2++; // a criteria for 1 more field was added to string
11404 } elseif ($mode == 3 || $mode == -3) {
11405 $tmparray = explode(',', $crit);
11406 if (count($tmparray)) {
11407 $listofcodes = '';
11408 foreach ($tmparray as $val) {
11409 $val = trim($val);
11410 if ($val) {
11411 $listofcodes .= ($listofcodes ? ',' : '');
11412 $listofcodes .= "'".$db->escape($val)."'";
11413 }
11414 }
11415 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
11416 $i2++; // a criteria for 1 more field was added to string
11417 }
11418 if ($mode == -3) {
11419 $newres .= ' OR '.$field.' IS NULL';
11420 }
11421 } elseif ($mode == 4) {
11422 $tmparray = explode(',', $crit);
11423 if (count($tmparray)) {
11424 $listofcodes = '';
11425 foreach ($tmparray as $val) {
11426 $val = trim($val);
11427 if ($val) {
11428 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11429 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11430 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11431 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11432 $newres .= ')';
11433 $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)
11434 }
11435 }
11436 }
11437 } else { // $mode=0
11438 $tmpcrits = explode('|', $crit);
11439 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11440 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11441 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11442 continue;
11443 }
11444 $tmpcrit = trim($tmpcrit);
11445
11446 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11447 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11448 } else {
11449 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11450 }
11451
11452 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11453 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11454 } else {
11455 $tmpcrit2 = $tmpcrit;
11456 $tmpbefore = '%';
11457 $tmpafter = '%';
11458 $tmps = '';
11459
11460 if (preg_match('/^!/', $tmpcrit)) {
11461 $tmps .= $field." NOT LIKE "; // ! as exclude character
11462 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11463 } else {
11464 $tmps .= $field." LIKE ";
11465 }
11466 $tmps .= "'";
11467
11468 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11469 $tmpbefore = '';
11470 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11471 }
11472 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11473 $tmpafter = '';
11474 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11475 }
11476
11477 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11478 $tmps = "(".$tmps;
11479 }
11480 $newres .= $tmps;
11481 $newres .= $tmpbefore;
11482 $newres .= $db->escape($tmpcrit2);
11483 $newres .= $tmpafter;
11484 $newres .= "'";
11485 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11486 $newres .= " OR ".$field." IS NULL)";
11487 }
11488 }
11489
11490 $i3++;
11491 }
11492
11493 $i2++; // a criteria for 1 more field was added to string
11494 }
11495 }
11496
11497 if ($newres) {
11498 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11499 }
11500 $i1++;
11501 }
11502 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11503
11504 return $res;
11505}
11506
11513function showDirectDownloadLink($object)
11514{
11515 global $conf, $langs;
11516
11517 $out = '';
11518 $url = $object->getLastMainDocLink($object->element);
11519
11520 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11521 if ($url) {
11522 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11523 $out .= ajax_autoselect("directdownloadlink", 0);
11524 } else {
11525 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11526 }
11527
11528 return $out;
11529}
11530
11539function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11540{
11541 $dirName = dirname($file);
11542 if ($dirName == '.') {
11543 $dirName = '';
11544 }
11545
11546 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
11547 $fileName = basename($fileName);
11548
11549 if (empty($extImgTarget)) {
11550 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11551 }
11552 if (empty($extImgTarget)) {
11553 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11554 }
11555 if (empty($extImgTarget)) {
11556 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11557 }
11558 if (empty($extImgTarget)) {
11559 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11560 }
11561 if (empty($extImgTarget)) {
11562 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11563 }
11564 if (empty($extImgTarget)) {
11565 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11566 }
11567
11568 if (!$extImgTarget) {
11569 return $file;
11570 }
11571
11572 $subdir = '';
11573 if ($extName) {
11574 $subdir = 'thumbs/';
11575 }
11576
11577 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11578}
11579
11580
11590function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11591{
11592 global $conf, $langs;
11593
11594 if (empty($conf->use_javascript_ajax)) {
11595 return '';
11596 }
11597
11598 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11599
11600 if ($alldata == 1) {
11601 if ($isAllowedForPreview) {
11602 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));
11603 } else {
11604 return array();
11605 }
11606 }
11607
11608 // old behavior, return a string
11609 if ($isAllowedForPreview) {
11610 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11611 $title = $langs->transnoentities("Preview");
11612 //$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().
11613 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
11614
11615 // 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,
11616 // 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.
11617 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
11618 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
11619 } else {
11620 return '';
11621 }
11622}
11623
11624
11633function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11634{
11635 global $langs;
11636 $out = '<script nonce="'.getNonce().'">
11637 jQuery(document).ready(function () {
11638 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11639 });
11640 </script>';
11641 if ($addlink) {
11642 if ($textonlink === 'image') {
11643 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11644 } else {
11645 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11646 }
11647 }
11648 return $out;
11649}
11650
11658function dolIsAllowedForPreview($file)
11659{
11660 // Check .noexe extension in filename
11661 if (preg_match('/\.noexe$/i', $file)) {
11662 return 0;
11663 }
11664
11665 // Check mime types
11666 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11667 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11668 $mime_preview[] = 'svg+xml';
11669 }
11670 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11671 //$mime_preview[]='archive';
11672 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11673 if ($num_mime !== false) {
11674 return 1;
11675 }
11676
11677 // By default, not allowed for preview
11678 return 0;
11679}
11680
11681
11691function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11692{
11693 $mime = $default;
11694 $imgmime = 'other.png';
11695 $famime = 'file-o';
11696 $srclang = '';
11697
11698 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11699
11700 // Plain text files
11701 if (preg_match('/\.txt$/i', $tmpfile)) {
11702 $mime = 'text/plain';
11703 $imgmime = 'text.png';
11704 $famime = 'file-alt';
11705 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
11706 $mime = 'text/richtext';
11707 $imgmime = 'text.png';
11708 $famime = 'file-alt';
11709 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
11710 $mime = 'text/csv';
11711 $imgmime = 'text.png';
11712 $famime = 'file-csv';
11713 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
11714 $mime = 'text/tab-separated-values';
11715 $imgmime = 'text.png';
11716 $famime = 'file-alt';
11717 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
11718 $mime = 'text/plain';
11719 $imgmime = 'text.png';
11720 $famime = 'file-alt';
11721 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
11722 $mime = 'text/plain';
11723 $imgmime = 'text.png';
11724 $srclang = 'ini';
11725 $famime = 'file-alt';
11726 } elseif (preg_match('/\.md$/i', $tmpfile)) {
11727 $mime = 'text/plain';
11728 $imgmime = 'text.png';
11729 $srclang = 'md';
11730 $famime = 'file-alt';
11731 } elseif (preg_match('/\.css$/i', $tmpfile)) {
11732 $mime = 'text/css';
11733 $imgmime = 'css.png';
11734 $srclang = 'css';
11735 $famime = 'file-alt';
11736 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
11737 $mime = 'text/plain';
11738 $imgmime = 'text.png';
11739 $srclang = 'lang';
11740 $famime = 'file-alt';
11741 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
11742 $mime = 'text/plain';
11743 $imgmime = 'text.png';
11744 $famime = 'file-alt';
11745 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
11746 $mime = 'text/html';
11747 $imgmime = 'html.png';
11748 $srclang = 'html';
11749 $famime = 'file-alt';
11750 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
11751 $mime = 'text/xml';
11752 $imgmime = 'other.png';
11753 $srclang = 'xml';
11754 $famime = 'file-alt';
11755 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
11756 $mime = 'text/xml';
11757 $imgmime = 'other.png';
11758 $srclang = 'xaml';
11759 $famime = 'file-alt';
11760 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
11761 $mime = 'text/plain';
11762 $imgmime = 'text.png';
11763 $srclang = 'bas';
11764 $famime = 'file-code';
11765 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
11766 $mime = 'text/plain';
11767 $imgmime = 'text.png';
11768 $srclang = 'c';
11769 $famime = 'file-code';
11770 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
11771 $mime = 'text/plain';
11772 $imgmime = 'text.png';
11773 $srclang = 'cpp';
11774 $famime = 'file-code';
11775 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
11776 $mime = 'text/plain';
11777 $imgmime = 'text.png';
11778 $srclang = 'cs';
11779 $famime = 'file-code';
11780 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
11781 $mime = 'text/plain';
11782 $imgmime = 'text.png';
11783 $srclang = 'h';
11784 $famime = 'file-code';
11785 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
11786 $mime = 'text/plain';
11787 $imgmime = 'text.png';
11788 $srclang = 'java';
11789 $famime = 'file-code';
11790 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
11791 $mime = 'text/plain';
11792 $imgmime = 'php.png';
11793 $srclang = 'php';
11794 $famime = 'file-code';
11795 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
11796 $mime = 'text/plain';
11797 $imgmime = 'php.png';
11798 $srclang = 'php';
11799 $famime = 'file-code';
11800 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
11801 $mime = 'text/plain';
11802 $imgmime = 'pl.png';
11803 $srclang = 'perl';
11804 $famime = 'file-code';
11805 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
11806 $mime = 'text/plain';
11807 $imgmime = 'text.png';
11808 $srclang = 'sql';
11809 $famime = 'file-code';
11810 } elseif (preg_match('/\.js$/i', $tmpfile)) {
11811 $mime = 'text/x-javascript';
11812 $imgmime = 'jscript.png';
11813 $srclang = 'js';
11814 $famime = 'file-code';
11815 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
11816 $mime = 'application/vnd.oasis.opendocument.presentation';
11817 $imgmime = 'ooffice.png';
11818 $famime = 'file-powerpoint';
11819 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
11820 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
11821 $imgmime = 'ooffice.png';
11822 $famime = 'file-excel';
11823 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
11824 $mime = 'application/vnd.oasis.opendocument.text';
11825 $imgmime = 'ooffice.png';
11826 $famime = 'file-word';
11827 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
11828 $mime = 'application/msaccess';
11829 $imgmime = 'mdb.png';
11830 $famime = 'file';
11831 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
11832 $mime = 'application/msword';
11833 $imgmime = 'doc.png';
11834 $famime = 'file-word';
11835 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
11836 $mime = 'application/msword';
11837 $imgmime = 'doc.png';
11838 $famime = 'file-word';
11839 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
11840 $mime = 'application/vnd.ms-excel';
11841 $imgmime = 'xls.png';
11842 $famime = 'file-excel';
11843 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
11844 $mime = 'application/vnd.ms-excel';
11845 $imgmime = 'xls.png';
11846 $famime = 'file-excel';
11847 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
11848 $mime = 'application/vnd.ms-excel';
11849 $imgmime = 'xls.png';
11850 $famime = 'file-excel';
11851 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
11852 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
11853 $imgmime = 'xls.png';
11854 $famime = 'file-excel';
11855 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
11856 $mime = 'application/vnd.ms-powerpoint';
11857 $imgmime = 'ppt.png';
11858 $famime = 'file-powerpoint';
11859 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
11860 $mime = 'application/x-mspowerpoint';
11861 $imgmime = 'ppt.png';
11862 $famime = 'file-powerpoint';
11863 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
11864 $mime = 'application/pdf';
11865 $imgmime = 'pdf.png';
11866 $famime = 'file-pdf';
11867 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
11868 $mime = 'text/x-bat';
11869 $imgmime = 'script.png';
11870 $srclang = 'dos';
11871 $famime = 'file-code';
11872 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
11873 $mime = 'text/x-sh';
11874 $imgmime = 'script.png';
11875 $srclang = 'bash';
11876 $famime = 'file-code';
11877 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
11878 $mime = 'text/x-ksh';
11879 $imgmime = 'script.png';
11880 $srclang = 'bash';
11881 $famime = 'file-code';
11882 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
11883 $mime = 'text/x-bash';
11884 $imgmime = 'script.png';
11885 $srclang = 'bash';
11886 $famime = 'file-code';
11887 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
11888 $mime = 'image/x-icon';
11889 $imgmime = 'image.png';
11890 $famime = 'file-image';
11891 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
11892 $mime = 'image/jpeg';
11893 $imgmime = 'image.png';
11894 $famime = 'file-image';
11895 } elseif (preg_match('/\.png$/i', $tmpfile)) {
11896 $mime = 'image/png';
11897 $imgmime = 'image.png';
11898 $famime = 'file-image';
11899 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
11900 $mime = 'image/gif';
11901 $imgmime = 'image.png';
11902 $famime = 'file-image';
11903 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
11904 $mime = 'image/bmp';
11905 $imgmime = 'image.png';
11906 $famime = 'file-image';
11907 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
11908 $mime = 'image/tiff';
11909 $imgmime = 'image.png';
11910 $famime = 'file-image';
11911 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
11912 $mime = 'image/svg+xml';
11913 $imgmime = 'image.png';
11914 $famime = 'file-image';
11915 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
11916 $mime = 'image/webp';
11917 $imgmime = 'image.png';
11918 $famime = 'file-image';
11919 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
11920 $mime = 'text/calendar';
11921 $imgmime = 'other.png';
11922 $famime = 'file-alt';
11923 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
11924 $mime = 'text/calendar';
11925 $imgmime = 'other.png';
11926 $famime = 'file-alt';
11927 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
11928 $mime = 'application/x-bittorrent';
11929 $imgmime = 'other.png';
11930 $famime = 'file-o';
11931 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
11932 $mime = 'audio';
11933 $imgmime = 'audio.png';
11934 $famime = 'file-audio';
11935 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
11936 $mime = 'video/mp4';
11937 $imgmime = 'video.png';
11938 $famime = 'file-video';
11939 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
11940 $mime = 'video/ogg';
11941 $imgmime = 'video.png';
11942 $famime = 'file-video';
11943 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
11944 $mime = 'video/webm';
11945 $imgmime = 'video.png';
11946 $famime = 'file-video';
11947 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
11948 $mime = 'video/x-msvideo';
11949 $imgmime = 'video.png';
11950 $famime = 'file-video';
11951 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
11952 $mime = 'video/divx';
11953 $imgmime = 'video.png';
11954 $famime = 'file-video';
11955 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
11956 $mime = 'video/xvid';
11957 $imgmime = 'video.png';
11958 $famime = 'file-video';
11959 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
11960 $mime = 'video';
11961 $imgmime = 'video.png';
11962 $famime = 'file-video';
11963 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
11964 // application/xxx where zzz is zip, ...
11965 $mime = 'archive';
11966 $imgmime = 'archive.png';
11967 $famime = 'file-archive';
11968 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
11969 $mime = 'application/octet-stream';
11970 $imgmime = 'other.png';
11971 $famime = 'file-o';
11972 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
11973 $mime = 'library';
11974 $imgmime = 'library.png';
11975 $famime = 'file-o';
11976 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
11977 $mime = 'error';
11978 $imgmime = 'error.png';
11979 $famime = 'file-alt';
11980 }
11981
11982 // Return mimetype string
11983 switch ((int) $mode) {
11984 case 1:
11985 $tmp = explode('/', $mime);
11986 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
11987 case 2:
11988 return $imgmime;
11989 case 3:
11990 return $srclang;
11991 case 4:
11992 return $famime;
11993 }
11994 return $mime;
11995}
11996
12008function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12009{
12010 global $conf, $db;
12011
12012 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12013
12014 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12015
12016 if (is_null($dictvalues)) {
12017 $dictvalues = array();
12018
12019 $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
12020 if ($checkentity) {
12021 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12022 }
12023
12024 $resql = $db->query($sql);
12025 if ($resql) {
12026 while ($obj = $db->fetch_object($resql)) {
12027 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12028 }
12029 } else {
12030 dol_print_error($db);
12031 }
12032
12033 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12034 }
12035
12036 if (!empty($dictvalues[$id])) {
12037 // Found
12038 $tmp = $dictvalues[$id];
12039 return (property_exists($tmp, $field) ? $tmp->$field : '');
12040 } else {
12041 // Not found
12042 return '';
12043 }
12044}
12045
12052function colorIsLight($stringcolor)
12053{
12054 $stringcolor = str_replace('#', '', $stringcolor);
12055 $res = -1;
12056 if (!empty($stringcolor)) {
12057 $res = 0;
12058 $tmp = explode(',', $stringcolor);
12059 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12060 $r = $tmp[0];
12061 $g = $tmp[1];
12062 $b = $tmp[2];
12063 } else {
12064 $hexr = $stringcolor[0].$stringcolor[1];
12065 $hexg = $stringcolor[2].$stringcolor[3];
12066 $hexb = $stringcolor[4].$stringcolor[5];
12067 $r = hexdec($hexr);
12068 $g = hexdec($hexg);
12069 $b = hexdec($hexb);
12070 }
12071 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12072 if ($bright > 0.6) {
12073 $res = 1;
12074 }
12075 }
12076 return $res;
12077}
12078
12087function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12088{
12089 global $conf;
12090
12091 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12092 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12093 if (empty($menuentry['enabled'])) {
12094 return 0; // Entry disabled by condition
12095 }
12096 if ($type_user && $menuentry['module']) {
12097 $tmploops = explode('|', $menuentry['module']);
12098 $found = 0;
12099 foreach ($tmploops as $tmploop) {
12100 if (in_array($tmploop, $listofmodulesforexternal)) {
12101 $found++;
12102 break;
12103 }
12104 }
12105 if (!$found) {
12106 return 0; // Entry is for menus all excluded to external users
12107 }
12108 }
12109 if (!$menuentry['perms'] && $type_user) {
12110 return 0; // No permissions and user is external
12111 }
12112 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12113 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12114 }
12115 if (!$menuentry['perms']) {
12116 return 2; // No permissions and user is external
12117 }
12118 return 1;
12119}
12120
12128function roundUpToNextMultiple($n, $x = 5)
12129{
12130 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12131 return (int) $result;
12132}
12133
12145function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12146{
12147 $csstouse = 'badge';
12148 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12149 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12150 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12151
12152 $attr = array(
12153 'class' => $csstouse
12154 );
12155
12156 if (empty($html)) {
12157 $html = $label;
12158 }
12159
12160 if (!empty($url)) {
12161 $attr['href'] = $url;
12162 }
12163
12164 if ($mode === 'dot') {
12165 $attr['class'] .= ' classfortooltip';
12166 $attr['title'] = $html;
12167 $attr['aria-label'] = $label;
12168 $html = '';
12169 }
12170
12171 // Override attr
12172 if (!empty($params['attr']) && is_array($params['attr'])) {
12173 foreach ($params['attr'] as $key => $value) {
12174 if ($key == 'class') {
12175 $attr['class'] .= ' '.$value;
12176 } elseif ($key == 'classOverride') {
12177 $attr['class'] = $value;
12178 } else {
12179 $attr[$key] = $value;
12180 }
12181 }
12182 }
12183
12184 // TODO: add hook
12185
12186 // escape all attribute
12187 $attr = array_map('dol_escape_htmltag', $attr);
12188
12189 $TCompiledAttr = array();
12190 foreach ($attr as $key => $value) {
12191 $TCompiledAttr[] = $key.'="'.$value.'"';
12192 }
12193
12194 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12195
12196 $tag = !empty($url) ? 'a' : 'span';
12197
12198 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12199}
12200
12201
12214function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12215{
12216 global $conf;
12217
12218 $return = '';
12219 $dolGetBadgeParams = array();
12220
12221 if (!empty($params['badgeParams'])) {
12222 $dolGetBadgeParams = $params['badgeParams'];
12223 }
12224
12225 // TODO : add a hook
12226 if ($displayMode == 0) {
12227 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12228 } elseif ($displayMode == 1) {
12229 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12230 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12231 // Use status with images (for backward compatibility)
12232 $return = '';
12233 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12234 $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>' : '');
12235
12236 // For small screen, we always use the short label instead of long label.
12237 if (!empty($conf->dol_optimize_smallscreen)) {
12238 if ($displayMode == 0) {
12239 $displayMode = 1;
12240 } elseif ($displayMode == 4) {
12241 $displayMode = 2;
12242 } elseif ($displayMode == 6) {
12243 $displayMode = 5;
12244 }
12245 }
12246
12247 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12248 $statusImg = array(
12249 'status0' => 'statut0',
12250 'status1' => 'statut1',
12251 'status2' => 'statut2',
12252 'status3' => 'statut3',
12253 'status4' => 'statut4',
12254 'status5' => 'statut5',
12255 'status6' => 'statut6',
12256 'status7' => 'statut7',
12257 'status8' => 'statut8',
12258 'status9' => 'statut9'
12259 );
12260
12261 if (!empty($statusImg[$statusType])) {
12262 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12263 } else {
12264 $htmlImg = img_picto($statusLabel, $statusType);
12265 }
12266
12267 if ($displayMode === 2) {
12268 $return = $htmlImg.' '.$htmlLabelShort;
12269 } elseif ($displayMode === 3) {
12270 $return = $htmlImg;
12271 } elseif ($displayMode === 4) {
12272 $return = $htmlImg.' '.$htmlLabel;
12273 } elseif ($displayMode === 5) {
12274 $return = $htmlLabelShort.' '.$htmlImg;
12275 } else { // $displayMode >= 6
12276 $return = $htmlLabel.' '.$htmlImg;
12277 }
12278 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12279 // Use new badge
12280 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12281
12282 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12283 if (empty($dolGetBadgeParams['attr']['title'])) {
12284 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12285 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12286 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12287 // And if we use tooltip, we can output title in HTML
12288 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12289 }
12290
12291 if ($displayMode == 3) {
12292 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12293 } elseif ($displayMode === 5) {
12294 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12295 } else {
12296 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12297 }
12298 }
12299
12300 return $return;
12301}
12302
12303
12341function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12342{
12343 global $hookmanager, $action, $object, $langs;
12344
12345 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12346 if (is_array($url)) {
12347 // Loop on $url array to remove entries of disabled modules
12348 foreach ($url as $key => $subbutton) {
12349 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12350 unset($url[$key]);
12351 }
12352 }
12353
12354 $out = '';
12355
12356 if (isset($params["areDropdownButtons"]) && $params["areDropdownButtons"] === false) {
12357 foreach ($url as $button) {
12358 if (!empty($button['lang'])) {
12359 $langs->load($button['lang']);
12360 }
12361 $label = $langs->trans($button['label']);
12362 $text = $button['text'] ?? '';
12363 $actionType = $button['actionType'] ?? '';
12364 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12365 $id = $button['id'] ?? '';
12366 $userRight = $button['perm'] ?? 1;
12367 $button['params'] = $button['params'] ?? [];
12368
12369 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12370 }
12371 return $out;
12372 }
12373
12374 if (count($url) > 1) {
12375 $out .= '<div class="dropdown inline-block dropdown-holder">';
12376 $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>';
12377 $out .= '<div class="dropdown-content">';
12378 foreach ($url as $subbutton) {
12379 if (!empty($subbutton['lang'])) {
12380 $langs->load($subbutton['lang']);
12381 }
12382
12383 if (!empty($subbutton['urlraw'])) {
12384 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12385 } else {
12386 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12387 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12388 }
12389
12390 $subbuttonparam = array();
12391 if (!empty($subbutton['attr'])) {
12392 $subbuttonparam['attr'] = $subbutton['attr'];
12393 }
12394 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown']??false) : $params['isDropDown']);
12395
12396 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12397 }
12398 $out .= "</div>";
12399 $out .= "</div>";
12400 } else {
12401 foreach ($url as $subbutton) { // Should loop on 1 record only
12402 if (!empty($subbutton['lang'])) {
12403 $langs->load($subbutton['lang']);
12404 }
12405
12406 if (!empty($subbutton['urlraw'])) {
12407 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12408 } else {
12409 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12410 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12411 }
12412
12413 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12414 }
12415 }
12416
12417 return $out;
12418 }
12419
12420 // Here, $url is a simple link
12421
12422 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12423 $class = "dropdown-item";
12424 } else {
12425 $class = 'butAction';
12426 if ($actionType == 'danger' || $actionType == 'delete') {
12427 $class = 'butActionDelete';
12428 if (!empty($url) && strpos($url, 'token=') === false) {
12429 $url .= '&token='.newToken();
12430 }
12431 }
12432 }
12433 $attr = array(
12434 'class' => $class,
12435 'href' => empty($url) ? '' : $url,
12436 'title' => $label
12437 );
12438
12439 if (empty($text)) {
12440 $text = $label;
12441 $attr['title'] = ''; // if html not set, leave label on title is redundant
12442 } else {
12443 $attr['title'] = $label;
12444 $attr['aria-label'] = $label;
12445 }
12446
12447 if (empty($userRight)) {
12448 $attr['class'] = 'butActionRefused';
12449 $attr['href'] = '';
12450 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12451 $attr['title'] = ($attr['title'] ? $attr['title'].'<br>' : '').$langs->trans('NotEnoughPermissions');
12452 }
12453
12454 if (!empty($id)) {
12455 $attr['id'] = $id;
12456 }
12457
12458 // Override attr
12459 if (!empty($params['attr']) && is_array($params['attr'])) {
12460 foreach ($params['attr'] as $key => $value) {
12461 if ($key == 'class') {
12462 $attr['class'] .= ' '.$value;
12463 } elseif ($key == 'classOverride') {
12464 $attr['class'] = $value;
12465 } else {
12466 $attr[$key] = $value;
12467 }
12468 }
12469 }
12470
12471 // automatic add tooltip when title is detected
12472 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12473 $attr['class'] .= ' classfortooltip';
12474 }
12475
12476 // Js Confirm button
12477 if ($userRight && !empty($params['confirm'])) {
12478 if (!is_array($params['confirm'])) {
12479 $params['confirm'] = array();
12480 }
12481
12482 if (empty($params['confirm']['url'])) {
12483 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12484 }
12485
12486 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12487 $attr['data-confirm-url'] = $params['confirm']['url'];
12488 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12489 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12490 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12491 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12492 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12493 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12494
12495 $attr['class'] .= ' butActionConfirm';
12496 }
12497
12498 if (isset($attr['href']) && empty($attr['href'])) {
12499 unset($attr['href']);
12500 }
12501
12502 // escape all attribute
12503 $attr = array_map('dol_escape_htmltag', $attr);
12504
12505 $TCompiledAttr = array();
12506 foreach ($attr as $key => $value) {
12507 $TCompiledAttr[] = $key.'= "'.$value.'"';
12508 }
12509
12510 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12511
12512 $tag = !empty($attr['href']) ? 'a' : 'span';
12513
12514
12515 $parameters = array(
12516 'TCompiledAttr' => $TCompiledAttr, // array
12517 'compiledAttributes' => $compiledAttributes, // string
12518 'attr' => $attr,
12519 'tag' => $tag,
12520 'label' => $label,
12521 'html' => $text,
12522 'actionType' => $actionType,
12523 'url' => $url,
12524 'id' => $id,
12525 'userRight' => $userRight,
12526 'params' => $params
12527 );
12528
12529 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12530 if ($reshook < 0) {
12531 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12532 }
12533
12534 if (empty($reshook)) {
12535 if (dol_textishtml($text)) { // If content already HTML encoded
12536 return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
12537 } else {
12538 return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
12539 }
12540 } else {
12541 return $hookmanager->resPrint;
12542 }
12543}
12544
12545
12554function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
12555{
12556 if (empty($url)) {
12557 return '';
12558 }
12559
12560 $parsedUrl = parse_url($url);
12561 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
12562 return $url;
12563 }
12564
12565 if (!empty($parsedUrl['query'])) {
12566 // Use parse_str() function to parse the string passed via URL
12567 parse_str($parsedUrl['query'], $urlQuery);
12568 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
12569 $url.= '&amp;backtopage='.urlencode($params['backtopage']);
12570 }
12571 }
12572
12573 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
12574 $url = DOL_URL_ROOT.$url;
12575 }
12576
12577 return $url;
12578}
12579
12580
12587function dolGetButtonTitleSeparator($moreClass = "")
12588{
12589 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12590}
12591
12598function getFieldErrorIcon($fieldValidationErrorMsg)
12599{
12600 $out = '';
12601 if (!empty($fieldValidationErrorMsg)) {
12602 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12603 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12604 $out .= '</span>';
12605 }
12606
12607 return $out;
12608}
12609
12622function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12623{
12624 global $langs, $conf, $user;
12625
12626 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12627 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12628 return '';
12629 }
12630
12631 $class = 'btnTitle';
12632 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12633 $class .= ' btnTitlePlus';
12634 }
12635 $useclassfortooltip = 1;
12636
12637 if (!empty($params['morecss'])) {
12638 $class .= ' '.$params['morecss'];
12639 }
12640
12641 $attr = array(
12642 'class' => $class,
12643 'href' => empty($url) ? '' : $url
12644 );
12645
12646 if (!empty($helpText)) {
12647 $attr['title'] = dol_escape_htmltag($helpText);
12648 } elseif (empty($attr['title']) && $label) {
12649 $attr['title'] = $label;
12650 $useclassfortooltip = 0;
12651 }
12652
12653 if ($status == 2) {
12654 $attr['class'] .= ' btnTitleSelected';
12655 } elseif ($status <= 0) {
12656 $attr['class'] .= ' refused';
12657
12658 $attr['href'] = '';
12659
12660 if ($status == -1) { // disable
12661 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12662 } elseif ($status == 0) { // Not enough permissions
12663 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12664 }
12665 }
12666
12667 if (!empty($attr['title']) && $useclassfortooltip) {
12668 $attr['class'] .= ' classfortooltip';
12669 }
12670
12671 if (!empty($id)) {
12672 $attr['id'] = $id;
12673 }
12674
12675 // Override attr
12676 if (!empty($params['attr']) && is_array($params['attr'])) {
12677 foreach ($params['attr'] as $key => $value) {
12678 if ($key == 'class') {
12679 $attr['class'] .= ' '.$value;
12680 } elseif ($key == 'classOverride') {
12681 $attr['class'] = $value;
12682 } else {
12683 $attr[$key] = $value;
12684 }
12685 }
12686 }
12687
12688 if (isset($attr['href']) && empty($attr['href'])) {
12689 unset($attr['href']);
12690 }
12691
12692 // TODO : add a hook
12693
12694 // escape all attribute
12695 $attr = array_map('dol_escape_htmltag', $attr);
12696
12697 $TCompiledAttr = array();
12698 foreach ($attr as $key => $value) {
12699 $TCompiledAttr[] = $key.'="'.$value.'"';
12700 }
12701
12702 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12703
12704 $tag = (empty($attr['href']) ? 'span' : 'a');
12705
12706 $button = '<'.$tag.' '.$compiledAttributes.'>';
12707 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
12708 if (!empty($params['forcenohideoftext'])) {
12709 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
12710 }
12711 $button .= '</'.$tag.'>';
12712
12713 return $button;
12714}
12715
12725function getElementProperties($elementType)
12726{
12727 global $conf, $db, $hookmanager;
12728
12729 $regs = array();
12730
12731 //$element_type='facture';
12732
12733 $classfile = $classname = $classpath = $subdir = $dir_output = '';
12734
12735 // Parse element/subelement
12736 $module = $elementType;
12737 $element = $elementType;
12738 $subelement = $elementType;
12739 $table_element = $elementType;
12740
12741 // If we ask a resource form external module (instead of default path)
12742 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
12743 $element = $subelement = $regs[1];
12744 $module = $regs[2];
12745 }
12746
12747 // If we ask a resource for a string with an element and a subelement
12748 // Example 'project_task'
12749 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
12750 $module = $element = $regs[1];
12751 $subelement = $regs[2];
12752 }
12753
12754 // Object lines will use parent classpath and module ref
12755 if (substr($elementType, -3) == 'det') {
12756 $module = preg_replace('/det$/', '', $element);
12757 $subelement = preg_replace('/det$/', '', $subelement);
12758 $classpath = $module.'/class';
12759 $classfile = $module;
12760 $classname = preg_replace('/det$/', 'Line', $element);
12761 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
12762 $classname = preg_replace('/det$/', 'Ligne', $element);
12763 }
12764 }
12765 // For compatibility and to work with non standard path
12766 if ($elementType == "action" || $elementType == "actioncomm") {
12767 $classpath = 'comm/action/class';
12768 $subelement = 'Actioncomm';
12769 $module = 'agenda';
12770 $table_element = 'actioncomm';
12771 } elseif ($elementType == 'cronjob') {
12772 $classpath = 'cron/class';
12773 $module = 'cron';
12774 $table_element = 'cron';
12775 } elseif ($elementType == 'adherent_type') {
12776 $classpath = 'adherents/class';
12777 $classfile = 'adherent_type';
12778 $module = 'adherent';
12779 $subelement = 'adherent_type';
12780 $classname = 'AdherentType';
12781 $table_element = 'adherent_type';
12782 } elseif ($elementType == 'bank_account') {
12783 $classpath = 'compta/bank/class';
12784 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
12785 $classfile = 'account';
12786 $classname = 'Account';
12787 } elseif ($elementType == 'category') {
12788 $classpath = 'categories/class';
12789 $module = 'categorie';
12790 $subelement = 'categorie';
12791 $table_element = 'categorie';
12792 } elseif ($elementType == 'contact') {
12793 $classpath = 'contact/class';
12794 $classfile = 'contact';
12795 $module = 'societe';
12796 $subelement = 'contact';
12797 $table_element = 'socpeople';
12798 } elseif ($elementType == 'inventory') {
12799 $module = 'product';
12800 $classpath = 'product/inventory/class';
12801 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
12802 $module = 'stock';
12803 $classpath = 'product/stock/class';
12804 $classfile = 'entrepot';
12805 $classname = 'Entrepot';
12806 $table_element = 'entrepot';
12807 } elseif ($elementType == 'project') {
12808 $classpath = 'projet/class';
12809 $module = 'projet';
12810 $table_element = 'projet';
12811 } elseif ($elementType == 'project_task') {
12812 $classpath = 'projet/class';
12813 $module = 'projet';
12814 $subelement = 'task';
12815 $table_element = 'projet_task';
12816 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
12817 $classpath = 'compta/facture/class';
12818 $module = 'facture';
12819 $subelement = 'facture';
12820 $table_element = 'facture';
12821 } elseif ($elementType == 'facturerec') {
12822 $classpath = 'compta/facture/class';
12823 $module = 'facture';
12824 $classname = 'FactureRec';
12825 } elseif ($elementType == 'commande' || $elementType == 'order') {
12826 $classpath = 'commande/class';
12827 $module = 'commande';
12828 $subelement = 'commande';
12829 $table_element = 'commande';
12830 } elseif ($elementType == 'propal') {
12831 $classpath = 'comm/propal/class';
12832 $table_element = 'propal';
12833 } elseif ($elementType == 'shipping') {
12834 $classpath = 'expedition/class';
12835 $classfile = 'expedition';
12836 $classname = 'Expedition';
12837 $module = 'expedition';
12838 $table_element = 'expedition';
12839 } elseif ($elementType == 'delivery_note') {
12840 $classpath = 'delivery/class';
12841 $subelement = 'delivery';
12842 $module = 'expedition';
12843 } elseif ($elementType == 'delivery') {
12844 $classpath = 'delivery/class';
12845 $subelement = 'delivery';
12846 $module = 'expedition';
12847 } elseif ($elementType == 'supplier_proposal') {
12848 $classpath = 'supplier_proposal/class';
12849 $module = 'supplier_proposal';
12850 $element = 'supplierproposal';
12851 $classfile = 'supplier_proposal';
12852 $subelement = 'supplierproposal';
12853 } elseif ($elementType == 'contract') {
12854 $classpath = 'contrat/class';
12855 $module = 'contrat';
12856 $subelement = 'contrat';
12857 $table_element = 'contract';
12858 } elseif ($elementType == 'mailing') {
12859 $classpath = 'comm/mailing/class';
12860 $module = 'mailing';
12861 $classfile = 'mailing';
12862 $classname = 'Mailing';
12863 $subelement = '';
12864 } elseif ($elementType == 'member') {
12865 $classpath = 'adherents/class';
12866 $module = 'adherent';
12867 $subelement = 'adherent';
12868 $table_element = 'adherent';
12869 } elseif ($elementType == 'usergroup') {
12870 $classpath = 'user/class';
12871 $module = 'user';
12872 } elseif ($elementType == 'mo') {
12873 $classpath = 'mrp/class';
12874 $classfile = 'mo';
12875 $classname = 'Mo';
12876 $module = 'mrp';
12877 $subelement = '';
12878 $table_element = 'mrp_mo';
12879 } elseif ($elementType == 'cabinetmed_cons') {
12880 $classpath = 'cabinetmed/class';
12881 $module = 'cabinetmed';
12882 $subelement = 'cabinetmedcons';
12883 $table_element = 'cabinetmedcons';
12884 } elseif ($elementType == 'fichinter') {
12885 $classpath = 'fichinter/class';
12886 $module = 'ficheinter';
12887 $subelement = 'fichinter';
12888 $table_element = 'fichinter';
12889 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
12890 $classpath = 'resource/class';
12891 $module = 'resource';
12892 $subelement = 'dolresource';
12893 $table_element = 'resource';
12894 } elseif ($elementType == 'propaldet') {
12895 $classpath = 'comm/propal/class';
12896 $module = 'propal';
12897 $subelement = 'propaleligne';
12898 } elseif ($elementType == 'opensurvey_sondage') {
12899 $classpath = 'opensurvey/class';
12900 $module = 'opensurvey';
12901 $subelement = 'opensurveysondage';
12902 } elseif ($elementType == 'order_supplier') {
12903 $classpath = 'fourn/class';
12904 $module = 'fournisseur';
12905 $classfile = 'fournisseur.commande';
12906 $element = 'order_supplier';
12907 $subelement = '';
12908 $classname = 'CommandeFournisseur';
12909 $table_element = 'commande_fournisseur';
12910 } elseif ($elementType == 'commande_fournisseurdet') {
12911 $classpath = 'fourn/class';
12912 $module = 'fournisseur';
12913 $classfile = 'fournisseur.commande';
12914 $element = 'commande_fournisseurdet';
12915 $subelement = '';
12916 $classname = 'CommandeFournisseurLigne';
12917 $table_element = 'commande_fournisseurdet';
12918 } elseif ($elementType == 'invoice_supplier') {
12919 $classpath = 'fourn/class';
12920 $module = 'fournisseur';
12921 $classfile = 'fournisseur.facture';
12922 $element = 'invoice_supplier';
12923 $subelement = '';
12924 $classname = 'FactureFournisseur';
12925 $table_element = 'facture_fourn';
12926 } elseif ($elementType == "service") {
12927 $classpath = 'product/class';
12928 $subelement = 'product';
12929 $table_element = 'product';
12930 } elseif ($elementType == 'salary') {
12931 $classpath = 'salaries/class';
12932 $module = 'salaries';
12933 } elseif ($elementType == 'payment_salary') {
12934 $classpath = 'salaries/class';
12935 $classfile = 'paymentsalary';
12936 $classname = 'PaymentSalary';
12937 $module = 'salaries';
12938 } elseif ($elementType == 'productlot') {
12939 $module = 'productbatch';
12940 $classpath = 'product/stock/class';
12941 $classfile = 'productlot';
12942 $classname = 'Productlot';
12943 $element = 'productlot';
12944 $subelement = '';
12945 $table_element = 'product_lot';
12946 } elseif ($elementType == 'societeaccount') {
12947 $classpath = 'societe/class';
12948 $classfile = 'societeaccount';
12949 $classname = 'SocieteAccount';
12950 $module = 'societe';
12951 } elseif ($elementType == 'websitepage') {
12952 $classpath = 'website/class';
12953 $classfile = 'websitepage';
12954 $classname = 'Websitepage';
12955 $module = 'website';
12956 $subelement = 'websitepage';
12957 $table_element = 'website_page';
12958 } elseif ($elementType == 'fiscalyear') {
12959 $classpath = 'core/class';
12960 $module = 'accounting';
12961 $subelement = 'fiscalyear';
12962 } elseif ($elementType == 'chargesociales') {
12963 $classpath = 'compta/sociales/class';
12964 $module = 'tax';
12965 $table_element = 'chargesociales';
12966 } elseif ($elementType == 'tva') {
12967 $classpath = 'compta/tva/class';
12968 $module = 'tax';
12969 $subdir = '/vat';
12970 $table_element = 'tva';
12971 } elseif ($elementType == 'emailsenderprofile') {
12972 $module = '';
12973 $classpath = 'core/class';
12974 $classfile = 'emailsenderprofile';
12975 $classname = 'EmailSenderProfile';
12976 $table_element = 'c_email_senderprofile';
12977 $subelement = '';
12978 } elseif ($elementType == 'conferenceorboothattendee') {
12979 $classpath = 'eventorganization/class';
12980 $classfile = 'conferenceorboothattendee';
12981 $classname = 'ConferenceOrBoothAttendee';
12982 $module = 'eventorganization';
12983 } elseif ($elementType == 'conferenceorbooth') {
12984 $classpath = 'eventorganization/class';
12985 $classfile = 'conferenceorbooth';
12986 $classname = 'ConferenceOrBooth';
12987 $module = 'eventorganization';
12988 } elseif ($elementType == 'ccountry') {
12989 $module = '';
12990 $classpath = 'core/class';
12991 $classfile = 'ccountry';
12992 $classname = 'Ccountry';
12993 $table_element = 'c_country';
12994 $subelement = '';
12995 }
12996
12997 if (empty($classfile)) {
12998 $classfile = strtolower($subelement);
12999 }
13000 if (empty($classname)) {
13001 $classname = ucfirst($subelement);
13002 }
13003 if (empty($classpath)) {
13004 $classpath = $module.'/class';
13005 }
13006
13007 //print 'getElementProperties subdir='.$subdir;
13008
13009 // Set dir_output
13010 if ($module && isset($conf->$module)) { // The generic case
13011 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13012 $dir_output = $conf->$module->multidir_output[$conf->entity];
13013 } elseif (!empty($conf->$module->output[$conf->entity])) {
13014 $dir_output = $conf->$module->output[$conf->entity];
13015 } elseif (!empty($conf->$module->dir_output)) {
13016 $dir_output = $conf->$module->dir_output;
13017 }
13018 }
13019
13020 // Overwrite value for special cases
13021 if ($element == 'order_supplier') {
13022 $dir_output = $conf->fournisseur->commande->dir_output;
13023 } elseif ($element == 'invoice_supplier') {
13024 $dir_output = $conf->fournisseur->facture->dir_output;
13025 }
13026 $dir_output .= $subdir;
13027
13028 $elementProperties = array(
13029 'module' => $module,
13030 'element' => $element,
13031 'table_element' => $table_element,
13032 'subelement' => $subelement,
13033 'classpath' => $classpath,
13034 'classfile' => $classfile,
13035 'classname' => $classname,
13036 'dir_output' => $dir_output
13037 );
13038
13039
13040 // Add hook
13041 if (!is_object($hookmanager)) {
13042 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13043 $hookmanager = new HookManager($db);
13044 }
13045 $hookmanager->initHooks(array('elementproperties'));
13046
13047
13048 // Hook params
13049 $parameters = array(
13050 'elementType' => $elementType,
13051 'elementProperties' => $elementProperties
13052 );
13053
13054 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13055
13056 if ($reshook) {
13057 $elementProperties = $hookmanager->resArray;
13058 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13059 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13060 }
13061
13062 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13063 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13064 unset($hookmanager->contextarray[$key]);
13065 }
13066
13067 return $elementProperties;
13068}
13069
13082function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13083{
13084 global $db, $conf;
13085
13086 $ret = 0;
13087
13088 $element_prop = getElementProperties($element_type);
13089
13090 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13091 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13092 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13093 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13094 // of service and we will return properties of a product.
13095 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13096 } elseif ($element_prop['module'] == 'societeaccount') {
13097 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13098 } else {
13099 $ismodenabled = isModEnabled($element_prop['module']);
13100 }
13101 //var_dump('element_type='.$element_type);
13102 //var_dump($element_prop);
13103 //var_dump($element_prop['module'].' '.$ismodenabled);
13104 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13105 if ($useCache === 1
13106 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13107 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13108 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13109 ) {
13110 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13111 }
13112
13113 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13114
13115 if (class_exists($element_prop['classname'])) {
13116 $className = $element_prop['classname'];
13117 $objecttmp = new $className($db);
13118 '@phan-var-force CommonObject $objecttmp';
13119
13120 if ($element_id > 0 || !empty($element_ref)) {
13121 $ret = $objecttmp->fetch($element_id, $element_ref);
13122 if ($ret >= 0) {
13123 if (empty($objecttmp->module)) {
13124 $objecttmp->module = $element_prop['module'];
13125 }
13126
13127 if ($useCache > 0) {
13128 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13129 $conf->cache['fetchObjectByElement'][$element_type] = [];
13130 }
13131
13132 // Manage cache limit
13133 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13134 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13135 }
13136
13137 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13138 }
13139
13140 return $objecttmp;
13141 }
13142 } else {
13143 return $objecttmp; // returned an object without fetch
13144 }
13145 } else {
13146 return -1;
13147 }
13148 }
13149
13150 return $ret;
13151}
13152
13159function isAFileWithExecutableContent($filename)
13160{
13161 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)) {
13162 return true;
13163 }
13164
13165 return false;
13166}
13167
13175function newToken()
13176{
13177 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13178}
13179
13187function currentToken()
13188{
13189 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13190}
13191
13197function getNonce()
13198{
13199 global $conf;
13200
13201 if (empty($conf->cache['nonce'])) {
13202 $conf->cache['nonce'] = dolGetRandomBytes(8);
13203 }
13204
13205 return $conf->cache['nonce'];
13206}
13207
13208
13222function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13223{
13224 global $langs;
13225
13226 print '<div class="div-table-responsive-no-min">';
13227 print '<table class="noborder centpercent">';
13228 print '<tr class="liste_titre">';
13229
13230 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13231
13232 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13233
13234 if (!empty($link)) {
13235 if (!empty($arguments)) {
13236 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13237 } else {
13238 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13239 }
13240 }
13241
13242 if ($number > -1) {
13243 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13244 } elseif (!empty($link)) {
13245 print '<span class="badge marginleftonlyshort">...</span>';
13246 }
13247
13248 if (!empty($link)) {
13249 print '</a>';
13250 }
13251
13252 print '</th>';
13253
13254 if ($number < 0 && !empty($link)) {
13255 print '<th class="right">';
13256 print '</th>';
13257 }
13258
13259 print '</tr>';
13260}
13261
13270function finishSimpleTable($addLineBreak = false)
13271{
13272 print '</table>';
13273 print '</div>';
13274
13275 if ($addLineBreak) {
13276 print '<br>';
13277 }
13278}
13279
13291function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13292{
13293 global $langs;
13294
13295 if ($num === 0) {
13296 print '<tr class="oddeven">';
13297 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13298 print '</tr>';
13299 return;
13300 }
13301
13302 if ($nbofloop === 0) {
13303 // don't show a summary line
13304 return;
13305 }
13306
13307 if ($num === 0) {
13308 $colspan = $tableColumnCount;
13309 } elseif ($num > $nbofloop) {
13310 $colspan = $tableColumnCount;
13311 } else {
13312 $colspan = $tableColumnCount - 1;
13313 }
13314
13315 if ($extraRightColumn) {
13316 $colspan--;
13317 }
13318
13319 print '<tr class="liste_total">';
13320
13321 if ($nbofloop > 0 && $num > $nbofloop) {
13322 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13323 } else {
13324 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13325 print '<td class="right centpercent">'.price($total).'</td>';
13326 }
13327
13328 if ($extraRightColumn) {
13329 print '<td></td>';
13330 }
13331
13332 print '</tr>';
13333}
13334
13343function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13344{
13345 if ($method == -1) {
13346 $method = 0;
13347 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13348 $method = 1;
13349 }
13350 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13351 $method = 2;
13352 }
13353 }
13354
13355 // Be sure we don't have output buffering enabled to have readfile working correctly
13356 while (ob_get_level()) {
13357 ob_end_flush();
13358 }
13359
13360 // Solution 0
13361 if ($method == 0) {
13362 readfile($fullpath_original_file_osencoded);
13363 } elseif ($method == 1) {
13364 // Solution 1
13365 $handle = fopen($fullpath_original_file_osencoded, "rb");
13366 while (!feof($handle)) {
13367 print fread($handle, 8192);
13368 }
13369 fclose($handle);
13370 } elseif ($method == 2) {
13371 // Solution 2
13372 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13373 $handle2 = fopen("php://output", "wb");
13374 stream_copy_to_stream($handle1, $handle2);
13375 fclose($handle1);
13376 fclose($handle2);
13377 }
13378}
13379
13389function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13390{
13391 /*
13392 global $conf;
13393
13394 if (!empty($conf->dol_no_mouse_hover)) {
13395 $showonlyonhover = 0;
13396 }*/
13397
13398 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13399 if ($texttoshow === 'none') {
13400 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'><span class="clipboardCPValueToPrint"></span><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText"></span></span>';
13401 } elseif ($texttoshow) {
13402 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'><span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText"></span></span>';
13403 } else {
13404 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText"></span></span>';
13405 }
13406
13407 return $result;
13408}
13409
13410
13417function jsonOrUnserialize($stringtodecode)
13418{
13419 $result = json_decode($stringtodecode);
13420 if ($result === null) {
13421 $result = unserialize($stringtodecode);
13422 }
13423
13424 return $result;
13425}
13426
13427
13444function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13445{
13446 global $db, $user;
13447
13448 if (is_null($filter) || !is_string($filter) || $filter === '') {
13449 return '';
13450 }
13451 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13452 $filter = '(' . $filter . ')';
13453 }
13454
13455 $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'
13456 $firstandlastparenthesis = 0;
13457
13458 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13459 if ($noerror) {
13460 return '1 = 2';
13461 } else {
13462 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13463 }
13464 }
13465
13466 // Test the filter syntax
13467 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13468 $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
13469 // If the string result contains something else than '()', the syntax was wrong
13470
13471 if (preg_match('/[^\‍(\‍)]/', $t)) {
13472 $tmperrorstr = 'Bad syntax of the search string';
13473 $errorstr = 'Bad syntax of the search string: '.$filter;
13474 if ($noerror) {
13475 return '1 = 2';
13476 } else {
13477 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
13478 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13479 }
13480 }
13481
13482 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
13483
13484 if (is_object($db)) {
13485 $ret = str_replace('__NOW__', $db->idate(dol_now()), $ret);
13486 }
13487 if (is_object($user)) {
13488 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13489 }
13490
13491 return $ret;
13492}
13493
13501function dolForgeExplodeAnd($sqlfilters)
13502{
13503 $arrayofandtags = array();
13504 $nbofchars = dol_strlen($sqlfilters);
13505
13506 $error = '';
13507 $parenthesislevel = 0;
13508 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13509 if (!$result) {
13510 return array();
13511 }
13512 if ($parenthesislevel >= 1) {
13513 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13514 }
13515
13516 $i = 0;
13517 $s = '';
13518 $countparenthesis = 0;
13519 while ($i < $nbofchars) {
13520 $char = dol_substr($sqlfilters, $i, 1);
13521
13522 if ($char == '(') {
13523 $countparenthesis++;
13524 } elseif ($char == ')') {
13525 $countparenthesis--;
13526 }
13527
13528 if ($countparenthesis == 0) {
13529 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13530 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13531 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13532 // We found a AND
13533 $s = trim($s);
13534 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13535 $s = '('.$s.')';
13536 }
13537 $arrayofandtags[] = $s;
13538 $s = '';
13539 $i += 2;
13540 } else {
13541 $s .= $char;
13542 }
13543 } else {
13544 $s .= $char;
13545 }
13546 $i++;
13547 }
13548 if ($s) {
13549 $s = trim($s);
13550 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13551 $s = '('.$s.')';
13552 }
13553 $arrayofandtags[] = $s;
13554 }
13555
13556 return $arrayofandtags;
13557}
13558
13568function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13569{
13570 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13571 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13572 $tmp = $sqlfilters;
13573
13574 $nb = dol_strlen($tmp);
13575 $counter = 0;
13576 $parenthesislevel = 0;
13577
13578 $error = '';
13579
13580 $i = 0;
13581 while ($i < $nb) {
13582 $char = dol_substr($tmp, $i, 1);
13583
13584 if ($char == '(') {
13585 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13586 // We open a parenthesis and it is the first char
13587 $parenthesislevel++;
13588 }
13589 $counter++;
13590 } elseif ($char == ')') {
13591 $nbcharremaining = ($nb - $i - 1);
13592 if ($nbcharremaining >= $counter) {
13593 $parenthesislevel = min($parenthesislevel, $counter - 1);
13594 }
13595 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13596 $parenthesislevel = $counter;
13597 }
13598 $counter--;
13599 }
13600
13601 if ($counter < 0) {
13602 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13603 $parenthesislevel = 0;
13604 dol_syslog($error, LOG_WARNING);
13605 return false;
13606 }
13607
13608 $i++;
13609 }
13610
13611 if ($counter > 0) {
13612 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13613 $parenthesislevel = 0;
13614 dol_syslog($error, LOG_WARNING);
13615 return false;
13616 }
13617
13618 return true;
13619}
13620
13628function dolForgeDummyCriteriaCallback($matches)
13629{
13630 //dol_syslog("Convert matches ".$matches[1]);
13631 if (empty($matches[1])) {
13632 return '';
13633 }
13634 $tmp = explode(':', $matches[1]);
13635 if (count($tmp) < 3) {
13636 return '';
13637 }
13638
13639 return '()'; // An empty criteria
13640}
13641
13650function dolForgeCriteriaCallback($matches)
13651{
13652 global $db;
13653
13654 //dol_syslog("Convert matches ".$matches[1]);
13655 if (empty($matches[1])) {
13656 return '';
13657 }
13658 $tmp = explode(':', $matches[1], 3);
13659 if (count($tmp) < 3) {
13660 return '';
13661 }
13662
13663 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
13664
13665 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
13666
13667 $realOperator = [
13668 'NOTLIKE' => 'NOT LIKE',
13669 'ISNOT' => 'IS NOT',
13670 'NOTIN' => 'NOT IN',
13671 '!=' => '<>',
13672 ];
13673
13674 if (array_key_exists($operator, $realOperator)) {
13675 $operator = $realOperator[$operator];
13676 }
13677
13678 $tmpescaped = $tmp[2];
13679
13680 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
13681
13682 $regbis = array();
13683
13684 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
13685 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
13686 $tmpescaped2 = '(';
13687 // Explode and sanitize each element in list
13688 $tmpelemarray = explode(',', $tmpescaped);
13689 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
13690 $reg = array();
13691 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
13692 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
13693 } else {
13694 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
13695 }
13696 }
13697 $tmpescaped2 .= implode(',', $tmpelemarray);
13698 $tmpescaped2 .= ')';
13699
13700 $tmpescaped = $tmpescaped2;
13701 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
13702 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
13703 $tmpescaped = $regbis[1];
13704 }
13705 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
13706 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
13707 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
13708 // TODO Retrieve type of field for $operand field name.
13709 // So we can complete format. For example we could complete a year with month and day.
13710 $tmpescaped = "'".$db->escape($regbis[1])."'";
13711 } else {
13712 if (strtoupper($tmpescaped) == 'NULL') {
13713 $tmpescaped = 'NULL';
13714 } elseif (is_int($tmpescaped)) {
13715 $tmpescaped = (int) $tmpescaped;
13716 } else {
13717 $tmpescaped = (float) $tmpescaped;
13718 }
13719 }
13720
13721 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
13722}
13723
13724
13734function getTimelineIcon($actionstatic, &$histo, $key)
13735{
13736 global $langs;
13737
13738 $out = '<!-- timeline icon -->'."\n";
13739 $iconClass = 'fa fa-comments';
13740 $img_picto = '';
13741 $colorClass = '';
13742 $pictoTitle = '';
13743
13744 if ($histo[$key]['percent'] == -1) {
13745 $colorClass = 'timeline-icon-not-applicble';
13746 $pictoTitle = $langs->trans('StatusNotApplicable');
13747 } elseif ($histo[$key]['percent'] == 0) {
13748 $colorClass = 'timeline-icon-todo';
13749 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
13750 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
13751 $colorClass = 'timeline-icon-in-progress';
13752 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
13753 } elseif ($histo[$key]['percent'] >= 100) {
13754 $colorClass = 'timeline-icon-done';
13755 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
13756 }
13757
13758 if ($actionstatic->code == 'AC_TICKET_CREATE') {
13759 $iconClass = 'fa fa-ticket';
13760 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
13761 $iconClass = 'fa fa-pencilxxx';
13762 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
13763 $iconClass = 'fa fa-comments';
13764 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
13765 $iconClass = 'fa fa-mask';
13766 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
13767 if ($actionstatic->type_picto) {
13768 $img_picto = img_picto('', $actionstatic->type_picto);
13769 } else {
13770 if ($actionstatic->type_code == 'AC_RDV') {
13771 $iconClass = 'fa fa-handshake';
13772 } elseif ($actionstatic->type_code == 'AC_TEL') {
13773 $iconClass = 'fa fa-phone';
13774 } elseif ($actionstatic->type_code == 'AC_FAX') {
13775 $iconClass = 'fa fa-fax';
13776 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
13777 $iconClass = 'fa fa-envelope';
13778 } elseif ($actionstatic->type_code == 'AC_INT') {
13779 $iconClass = 'fa fa-shipping-fast';
13780 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
13781 $iconClass = 'fa fa-robot';
13782 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
13783 $iconClass = 'fa fa-robot';
13784 }
13785 }
13786 }
13787
13788 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
13789 return $out;
13790}
13791
13799{
13800 global $conf, $db;
13801
13802 $documents = array();
13803
13804 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
13805 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
13806 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
13807 //$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
13808 $sql .= ' ORDER BY ecm.position ASC';
13809
13810 $resql = $db->query($sql);
13811 if ($resql) {
13812 if ($db->num_rows($resql)) {
13813 while ($obj = $db->fetch_object($resql)) {
13814 $documents[$obj->id] = $obj;
13815 }
13816 }
13817 }
13818
13819 return $documents;
13820}
13821
13822
13840function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
13841{
13842 global $user, $conf;
13843 global $form;
13844
13845 global $param, $massactionbutton;
13846
13847 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
13848
13849 // Check parameters
13850 if (!is_object($filterobj) && !is_object($objcon)) {
13851 dol_print_error(null, 'BadParameter');
13852 }
13853
13854 $histo = array();
13855 '@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';
13856
13857 $numaction = 0;
13858 $now = dol_now();
13859
13860 $sortfield_list = explode(',', $sortfield);
13861 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
13862 $sortfield_new_list = array();
13863 foreach ($sortfield_list as $sortfield_value) {
13864 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
13865 }
13866 $sortfield_new = implode(',', $sortfield_new_list);
13867
13868 $sql = null;
13869 $sql2 = null;
13870
13871 if (isModEnabled('agenda')) {
13872 // Search histo on actioncomm
13873 if (is_object($objcon) && $objcon->id > 0) {
13874 $sql = "SELECT DISTINCT a.id, a.label as label,";
13875 } else {
13876 $sql = "SELECT a.id, a.label as label,";
13877 }
13878 $sql .= " a.datep as dp,";
13879 $sql .= " a.note as message,";
13880 $sql .= " a.datep2 as dp2,";
13881 $sql .= " a.percent as percent, 'action' as type,";
13882 $sql .= " a.fk_element, a.elementtype,";
13883 $sql .= " a.fk_contact,";
13884 $sql .= " a.email_from as msg_from,";
13885 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
13886 $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";
13887 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13888 $sql .= ", sp.lastname, sp.firstname";
13889 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13890 $sql .= ", m.lastname, m.firstname";
13891 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13892 $sql .= ", o.ref";
13893 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13894 $sql .= ", o.ref";
13895 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13896 $sql .= ", o.ref";
13897 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13898 $sql .= ", o.ref";
13899 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13900 $sql .= ", o.ref";
13901 }
13902 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
13903 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
13904 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
13905
13906 $force_filter_contact = $filterobj instanceof User;
13907
13908 if (is_object($objcon) && $objcon->id > 0) {
13909 $force_filter_contact = true;
13910 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
13911 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
13912 }
13913
13914 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13915 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
13916 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
13917 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
13918 $sql .= " ON er.resource_type = 'dolresource'";
13919 $sql .= " AND er.element_id = a.id";
13920 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
13921 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13922 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
13923 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13924 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
13925 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13926 $sql .= ", ".MAIN_DB_PREFIX."product as o";
13927 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13928 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
13929 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13930 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
13931 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13932 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
13933 }
13934
13935 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
13936 if (!$force_filter_contact) {
13937 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
13938 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
13939 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
13940 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
13941 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13942 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
13943 if ($filterobj->id) {
13944 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13945 }
13946 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13947 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
13948 if ($filterobj->id) {
13949 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13950 }
13951 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13952 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
13953 if ($filterobj->id) {
13954 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13955 }
13956 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13957 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
13958 if ($filterobj->id) {
13959 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13960 }
13961 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13962 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
13963 if ($filterobj->id) {
13964 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13965 }
13966 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13967 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
13968 if ($filterobj->id) {
13969 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13970 }
13971 }
13972 } else {
13973 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
13974 }
13975
13976 // Condition on actioncode
13977 if (!empty($actioncode)) {
13978 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
13979 if ($actioncode == 'AC_NON_AUTO') {
13980 $sql .= " AND c.type != 'systemauto'";
13981 } elseif ($actioncode == 'AC_ALL_AUTO') {
13982 $sql .= " AND c.type = 'systemauto'";
13983 } else {
13984 if ($actioncode == 'AC_OTH') {
13985 $sql .= " AND c.type != 'systemauto'";
13986 } elseif ($actioncode == 'AC_OTH_AUTO') {
13987 $sql .= " AND c.type = 'systemauto'";
13988 }
13989 }
13990 } else {
13991 if ($actioncode == 'AC_NON_AUTO') {
13992 $sql .= " AND c.type != 'systemauto'";
13993 } elseif ($actioncode == 'AC_ALL_AUTO') {
13994 $sql .= " AND c.type = 'systemauto'";
13995 } else {
13996 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
13997 }
13998 }
13999 }
14000 if ($donetodo == 'todo') {
14001 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14002 } elseif ($donetodo == 'done') {
14003 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14004 }
14005 if (is_array($filters) && $filters['search_agenda_label']) {
14006 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14007 }
14008 }
14009
14010 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14011 if (isModEnabled('mailing') && !empty($objcon->email)
14012 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14013 $langs->load("mails");
14014
14015 $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";
14016 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14017 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14018 $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
14019 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14020 $sql2 .= ", '' as lastname, '' as firstname";
14021 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14022 $sql2 .= ", '' as lastname, '' as firstname";
14023 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14024 $sql2 .= ", '' as ref";
14025 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14026 $sql2 .= ", '' as ref";
14027 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14028 $sql2 .= ", '' as ref";
14029 }
14030 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14031 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14032 $sql2 .= " AND mc.statut = 1";
14033 $sql2 .= " AND u.rowid = m.fk_user_valid";
14034 $sql2 .= " AND mc.fk_mailing=m.rowid";
14035 }
14036
14037 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14038 if (!empty($sql) && !empty($sql2)) {
14039 $sql = $sql." UNION ".$sql2;
14040 } elseif (empty($sql) && !empty($sql2)) {
14041 $sql = $sql2;
14042 }
14043
14044 //TODO Add navigation with this limits...
14045 $offset = 0;
14046 $limit = 1000;
14047
14048 // Complete request and execute it with limit
14049 $sql .= $db->order($sortfield_new, $sortorder);
14050 if ($limit) {
14051 $sql .= $db->plimit($limit + 1, $offset);
14052 }
14053
14054 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14055 $resql = $db->query($sql);
14056 if ($resql) {
14057 $i = 0;
14058 $num = $db->num_rows($resql);
14059
14060 $imaxinloop = ($limit ? min($num, $limit) : $num);
14061 while ($i < $imaxinloop) {
14062 $obj = $db->fetch_object($resql);
14063
14064 if ($obj->type == 'action') {
14065 $contactaction = new ActionComm($db);
14066 $contactaction->id = $obj->id;
14067 $result = $contactaction->fetchResources();
14068 if ($result < 0) {
14069 dol_print_error($db);
14070 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14071 }
14072
14073 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14074 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14075 $tododone = '';
14076 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14077 $tododone = 'todo';
14078 }
14079
14080 $histo[$numaction] = array(
14081 'type' => $obj->type,
14082 'tododone' => $tododone,
14083 'id' => $obj->id,
14084 'datestart' => $db->jdate($obj->dp),
14085 'dateend' => $db->jdate($obj->dp2),
14086 'note' => $obj->label,
14087 'message' => dol_htmlentitiesbr($obj->message),
14088 'percent' => $obj->percent,
14089
14090 'userid' => $obj->user_id,
14091 'login' => $obj->user_login,
14092 'userfirstname' => $obj->user_firstname,
14093 'userlastname' => $obj->user_lastname,
14094 'userphoto' => $obj->user_photo,
14095 'msg_from' => $obj->msg_from,
14096
14097 'contact_id' => $obj->fk_contact,
14098 'socpeopleassigned' => $contactaction->socpeopleassigned,
14099 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14100 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14101 'fk_element' => $obj->fk_element,
14102 'elementtype' => $obj->elementtype,
14103 // Type of event
14104 'acode' => $obj->acode,
14105 'alabel' => $obj->alabel,
14106 'libelle' => $obj->alabel, // deprecated
14107 'apicto' => $obj->apicto
14108 );
14109 } else {
14110 $histo[$numaction] = array(
14111 'type' => $obj->type,
14112 'tododone' => 'done',
14113 'id' => $obj->id,
14114 'datestart' => $db->jdate($obj->dp),
14115 'dateend' => $db->jdate($obj->dp2),
14116 'note' => $obj->label,
14117 'message' => dol_htmlentitiesbr($obj->message),
14118 'percent' => $obj->percent,
14119 'acode' => $obj->acode,
14120
14121 'userid' => $obj->user_id,
14122 'login' => $obj->user_login,
14123 'userfirstname' => $obj->user_firstname,
14124 'userlastname' => $obj->user_lastname,
14125 'userphoto' => $obj->user_photo
14126 );
14127 }
14128
14129 $numaction++;
14130 $i++;
14131 }
14132 } else {
14133 dol_print_error($db);
14134 }
14135 }
14136
14137 // Set $out to show events
14138 $out = '';
14139
14140 if (!isModEnabled('agenda')) {
14141 $langs->loadLangs(array("admin", "errors"));
14142 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14143 }
14144
14145 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14146 $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
14147
14148 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14149 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14150 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14151 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14152
14153 $formactions = new FormActions($db);
14154
14155 $actionstatic = new ActionComm($db);
14156 $userstatic = new User($db);
14157 $contactstatic = new Contact($db);
14158 $userGetNomUrlCache = array();
14159 $contactGetNomUrlCache = array();
14160
14161 $out .= '<div class="filters-container" >';
14162 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14163 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14164
14165 if ($objcon && get_class($objcon) == 'Contact' &&
14166 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14167 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14168 } else {
14169 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14170 }
14171 if (($filterobj && get_class($filterobj) == 'Societe')) {
14172 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14173 } else {
14174 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14175 }
14176
14177 $out .= "\n";
14178
14179 $out .= '<div class="div-table-responsive-no-min">';
14180 $out .= '<table class="noborder borderbottom centpercent">';
14181
14182 $out .= '<tr class="liste_titre">';
14183
14184 // Action column
14185 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14186 $out .= '<th class="liste_titre width50 middle">';
14187 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14188 $out .= $searchpicto;
14189 $out .= '</th>';
14190 }
14191
14192 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
14193
14194 $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14195 if ($donetodo) {
14196 $out .= '<th class="liste_titre"></th>';
14197 }
14198 $out .= '<th class="liste_titre">';
14199 $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14200 //$out .= img_picto($langs->trans("Type"), 'type');
14201 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', !getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : -1, 0, 0, 1, 'minwidth200imp');
14202 $out .= '</th>';
14203 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14204 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14205 $out .= '</th>';
14206
14207 // Action column
14208 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14209 $out .= '<th class="liste_titre width50 middle">';
14210 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14211 $out .= $searchpicto;
14212 $out .= '</th>';
14213 }
14214
14215 $out .= '</tr>';
14216
14217
14218 $out .= '</table>';
14219
14220 $out .= '</form>';
14221 $out .= '</div>';
14222
14223 $out .= "\n";
14224
14225 $out .= '<ul class="timeline">';
14226
14227 if ($donetodo) {
14228 $tmp = '';
14229 if ($filterobj instanceof Societe) {
14230 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14231 }
14232 if ($filterobj instanceof User) {
14233 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14234 }
14235 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14236 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14237 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14238 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14239 if ($filterobj instanceof Societe) {
14240 $tmp .= '</a>';
14241 }
14242 if ($filterobj instanceof User) {
14243 $tmp .= '</a>';
14244 }
14245 $out .= getTitleFieldOfList($tmp);
14246 }
14247
14248 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14249 $caction = new CActionComm($db);
14250 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14251
14252 $actualCycleDate = false;
14253
14254 // Loop on each event to show it
14255 foreach ($histo as $key => $value) {
14256 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14257
14258 $actionstatic->type_picto = $histo[$key]['apicto'];
14259 $actionstatic->type_code = $histo[$key]['acode'];
14260
14261 $labeltype = $actionstatic->type_code;
14262 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14263 $labeltype = 'AC_OTH';
14264 }
14265 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14266 $labeltype = $langs->trans("Message");
14267 } else {
14268 if (!empty($arraylist[$labeltype])) {
14269 $labeltype = $arraylist[$labeltype];
14270 }
14271 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14272 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14273 }
14274 }
14275
14276 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14277
14278 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14279
14280 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14281 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14282 $out .= '<!-- timeline time label -->';
14283 $out .= '<li class="time-label">';
14284 $out .= '<span class="timeline-badge-date">';
14285 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14286 $out .= '</span>';
14287 $out .= '</li>';
14288 $out .= '<!-- /.timeline-label -->';
14289 }
14290
14291
14292 $out .= '<!-- timeline item -->'."\n";
14293 $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
14294
14295 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14296 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14297 //$out .= $timelineicon;
14298 //var_dump($timelineicon);
14299 $out .= $typeicon;
14300
14301 $out .= '<div class="timeline-item">'."\n";
14302
14303 $out .= '<span class="time timeline-header-action2">';
14304
14305 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14306 $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").' ';
14307 $out .= $histo[$key]['id'];
14308 $out .= '</a> ';
14309 } else {
14310 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14311 }
14312
14313 if ($user->hasRight('agenda', 'allactions', 'create') ||
14314 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14315 $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).'">';
14316 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14317 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14318 $out .= '</a>';
14319 }
14320
14321 $out .= '</span>';
14322
14323 // Date
14324 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14325 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14326 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14327 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14328 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14329 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14330 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14331 } else {
14332 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14333 }
14334 }
14335 $late = 0;
14336 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14337 $late = 1;
14338 }
14339 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14340 $late = 1;
14341 }
14342 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14343 $late = 1;
14344 }
14345 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14346 $late = 1;
14347 }
14348 if ($late) {
14349 $out .= img_warning($langs->trans("Late")).' ';
14350 }
14351 $out .= "</span></span>\n";
14352
14353 // Ref
14354 $out .= '<h3 class="timeline-header">';
14355
14356 // Author of event
14357 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14358 if ($histo[$key]['userid'] > 0) {
14359 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14360 $userstatic->fetch($histo[$key]['userid']);
14361 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14362 }
14363 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14364 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14365 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14366 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14367 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14368 } else {
14369 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14370 }
14371 }
14372 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14373 }
14374 $out .= '</div>';
14375
14376 // Title
14377 $out .= ' <div class="messaging-title inline-block">';
14378 //$out .= $actionstatic->getTypePicto();
14379 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14380 $out .= $labeltype.' - ';
14381 }
14382
14383 $libelle = '';
14384 if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14385 $out .= $langs->trans('TicketNewMessage');
14386 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14387 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14388 } elseif (isset($histo[$key]['type'])) {
14389 if ($histo[$key]['type'] == 'action') {
14390 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14391 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14392 $libelle = $histo[$key]['note'];
14393 $actionstatic->id = $histo[$key]['id'];
14394 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14395 } elseif ($histo[$key]['type'] == 'mailing') {
14396 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14397 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14398 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14399 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14400 } else {
14401 $libelle .= $histo[$key]['note'];
14402 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14403 }
14404 }
14405
14406 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14407 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14408 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14409 } else {
14410 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14411 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14412 }
14413 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14414 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14415 }
14416 if ($link) {
14417 $out .= ' - '.$link;
14418 }
14419 }
14420
14421 $out .= '</div>';
14422
14423 $out .= '</h3>';
14424
14425 // Message
14426 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14427 && $actionstatic->code != 'AC_TICKET_CREATE'
14428 && $actionstatic->code != 'AC_TICKET_MODIFY'
14429 ) {
14430 $out .= '<div class="timeline-body wordbreak small">';
14431 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14432 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14433 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14434 $out .= '<div class="readmore-block --closed" >';
14435 $out .= ' <div class="readmore-block__excerpt">';
14436 $out .= dolPrintHTML($truncatedText);
14437 $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>';
14438 $out .= ' </div>';
14439 $out .= ' <div class="readmore-block__full-text" >';
14440 $out .= dolPrintHTML($histo[$key]['message']);
14441 $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>';
14442 $out .= ' </div>';
14443 $out .= '</div>';
14444 } else {
14445 $out .= dolPrintHTML($histo[$key]['message']);
14446 }
14447
14448 $out .= '</div>';
14449 }
14450
14451 // Timeline footer
14452 $footer = '';
14453
14454 // Contact for this action
14455 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14456 $contactList = '';
14457 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14458 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14459 $contact = new Contact($db);
14460 $contact->fetch($cid);
14461 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14462 } else {
14463 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14464 }
14465
14466 if ($contact) {
14467 $contactList .= !empty($contactList) ? ', ' : '';
14468 $contactList .= $contact->getNomUrl(1);
14469 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14470 if (!empty($contact->phone_pro)) {
14471 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14472 }
14473 }
14474 }
14475 }
14476
14477 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14478 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14479 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14480 $contact = new Contact($db);
14481 $result = $contact->fetch($histo[$key]['contact_id']);
14482 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14483 } else {
14484 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14485 $result = ($contact instanceof Contact) ? $contact->id : 0;
14486 }
14487
14488 if ($result > 0) {
14489 $footer .= $contact->getNomUrl(1);
14490 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14491 if (!empty($contact->phone_pro)) {
14492 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14493 }
14494 }
14495 }
14496 }
14497
14498 $documents = getActionCommEcmList($actionstatic);
14499 if (!empty($documents)) {
14500 $footer .= '<div class="timeline-documents-container">';
14501 foreach ($documents as $doc) {
14502 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14503 $footer .= ' data-id="'.$doc->id.'" ';
14504 $footer .= ' data-path="'.$doc->filepath.'"';
14505 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14506 $footer .= '>';
14507
14508 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14509 $mime = dol_mimetype($filePath);
14510 $file = $actionstatic->id.'/'.$doc->filename;
14511 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14512 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14513 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14514
14515 $mimeAttr = ' mime="'.$mime.'" ';
14516 $class = '';
14517 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14518 $class .= ' documentpreview';
14519 }
14520
14521 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14522 $footer .= img_mime($filePath).' '.$doc->filename;
14523 $footer .= '</a>';
14524
14525 $footer .= '</span>';
14526 }
14527 $footer .= '</div>';
14528 }
14529
14530 if (!empty($footer)) {
14531 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14532 }
14533
14534 $out .= '</div>'."\n"; // end timeline-item
14535
14536 $out .= '</li>';
14537 $out .= '<!-- END timeline item -->';
14538 }
14539
14540 $out .= "</ul>\n";
14541
14542 $out .= '<script>
14543 jQuery(document).ready(function () {
14544 $(document).on("click", "[data-read-more-action]", function(e){
14545 let readMoreBloc = $(this).closest(".readmore-block");
14546 if(readMoreBloc.length > 0){
14547 e.preventDefault();
14548 if($(this).attr("data-read-more-action") == "close"){
14549 readMoreBloc.addClass("--closed").removeClass("--open");
14550 $("html, body").animate({
14551 scrollTop: readMoreBloc.offset().top - 200
14552 }, 100);
14553 }else{
14554 readMoreBloc.addClass("--open").removeClass("--closed");
14555 }
14556 }
14557 });
14558 });
14559 </script>';
14560
14561
14562 if (empty($histo)) {
14563 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14564 }
14565 }
14566
14567 if ($noprint) {
14568 return $out;
14569 } else {
14570 print $out;
14571 }
14572}
14573
14584function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
14585{
14586 $m = array();
14587 if ($hourTime === 'getpost') {
14588 $hour = GETPOSTINT($prefix . 'hour');
14589 $minute = GETPOSTINT($prefix . 'minute');
14590 $second = GETPOSTINT($prefix . 'second');
14591 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
14592 $hour = intval($m[1]);
14593 $minute = intval($m[2]);
14594 $second = intval($m[3]);
14595 } else {
14596 $hour = $minute = $second = 0;
14597 }
14598 // normalize out of range values
14599 $hour = min($hour, 23);
14600 $minute = min($minute, 59);
14601 $second = min($second, 59);
14602 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
14603}
14604
14616function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14617{
14618 if ($timestamp === null) {
14619 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14620 }
14621 $TParam = array(
14622 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14623 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14624 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14625 );
14626 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
14627 $TParam = array_merge($TParam, array(
14628 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
14629 $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
14630 $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
14631 ));
14632 }
14633
14634 return '&' . http_build_query($TParam);
14635}
14636
14656function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
14657{
14658 global $conf, $db, $langs, $hookmanager;
14659 global $action, $object;
14660
14661 if (!is_object($langs)) {
14662 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
14663 $langs = new Translate('', $conf);
14664 $langs->setDefaultLang();
14665 }
14666
14667 $langs->load("errors");
14668
14669 if ($printheader) {
14670 if (function_exists("llxHeader")) {
14671 llxHeader('');
14672 } elseif (function_exists("llxHeaderVierge")) {
14673 llxHeaderVierge('');
14674 }
14675 }
14676
14677 print '<div class="error">';
14678 if (empty($message)) {
14679 print $langs->trans("ErrorRecordNotFound");
14680 } else {
14681 print $langs->trans($message);
14682 }
14683 print '</div>';
14684 print '<br>';
14685
14686 if (empty($showonlymessage)) {
14687 if (empty($hookmanager)) {
14688 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14689 $hookmanager = new HookManager($db);
14690 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
14691 $hookmanager->initHooks(array('main'));
14692 }
14693
14694 $parameters = array('message' => $message, 'params' => $params);
14695 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14696 print $hookmanager->resPrint;
14697 }
14698
14699 if ($printfooter && function_exists("llxFooter")) {
14700 llxFooter();
14701 }
14702 exit(0);
14703}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
if(!defined( 'NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined( 'NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) if(!defined( 'NOLOGIN')) if(!defined('NOCSRFCHECK')) if(!defined( 'NOIPCHECK')) llxHeaderVierge()
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:456
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:725
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader()
Empty header.
Definition wrapper.php:55
llxFooter()
Empty footer.
Definition wrapper.php:69
$object ref
Definition info.php:79
Class to manage agenda events (actions)
Class to manage different types of events.
Class to manage contact/addresses.
Class to manage GeoIP conversion Usage: $geoip=new GeoIP('country',$datfile); $geoip->getCountryCodeF...
Class to manage standard extra fields.
Class to manage invoices.
Class to manage building of HTML components.
Class to manage generation of HTML components Only common components must be here.
Class to manage hooks.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
dol_get_prev_month($month, $year)
Return previous month.
Definition date.lib.php:514
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:499
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:483
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:533
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.
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)
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_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='')
Format phone numbers according to country.
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.
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.
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
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_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.
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0)
Set event message in dol_events session object.
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.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
checkVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
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
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
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.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0)
Clean a string to keep only desirable HTML tags.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
dolPrintHTMLForAttribute($s)
Return a string ready to be output on an HTML attribute (alt, title, data-html, .....
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot=true)
An function to complete dropdown url in dolGetButtonAction.
dol_get_object_properties($obj, $properties=[])
Get properties for an object - including magic properties when requested.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
img_search($titlealt='default', $other='')
Show search logo.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
isValidPhone($phone)
Return true if phone number syntax is ok TODO Decide what to do with this.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
img_previous($titlealt='default', $moreatt='')
Show previous logo.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
getBrowserInfo($user_agent)
Return information about user browser.
dolGetFirstLetters($s, $nbofchar=1)
Return first letters of a strings.
dol_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)
img_printer($titlealt="default", $other='')
Show printer logo.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
ascii_check($str)
Check if a string is in ASCII.
get_date_range($date_start, $date_end, $format='', $outputlangs=null, $withparenthesis=1)
Format output for start and end date.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
print_date_range($date_start, $date_end, $format='', $outputlangs=null)
Format output for start and end date.
getArrayOfSocialNetworks()
Get array of social network dictionary.
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
num2Alpha($n)
Return a numeric value into an Excel like column number.
dol_size($size, $type='')
Optimize a size for some browsers (phone, smarphone, ...)
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
img_pdf($titlealt='default', $size=3)
Show pdf logo.
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $hideselectlimit=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolPrintPassword($s)
Return a string ready to be output on an HTML attribute (alt, title, ...)
colorIsLight($stringcolor)
Return true if the color is light.
dol_escape_all($stringtoescape)
Returns text escaped for all protocols (so only alpha chars and numbers)
GETPOSTFLOAT($paramname, $rounding='')
Return the value of a $_GET or $_POST supervariable, converted into float.
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files,...
dol_shutdown()
Function called at end of web php process.
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl='')
Format address string.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
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.
dol_bc($var, $moreclass='')
Return string to add class property on html element with pair/impair.
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).
img_error($titlealt='default')
Show error logo.
getTimelineIcon($actionstatic, &$histo, $key)
Get timeline icon.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formatted messages to output (Used to show messages on html output).
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
buildParamDate($prefix, $timestamp=null, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_next($titlealt='default', $moreatt='')
Show next logo.
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_string_is_good_iso($s, $clean=0)
Check if a string is a correct iso string If not, it will not be considered as HTML encoded even if i...
getNonce()
Return a random string to be used as a nonce value for js.
isStringVarMatching($var, $regextext, $matchrule=1)
Check if a variable with name $var startx with $text.
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode='add', $filterorigmodule='')
Complete or removed entries into a head array (used to build tabs).
get_htmloutput_mesg($mesgstring='', $mesgarray=[], $style='ok', $keepembedded=0)
Get formatted messages to output (Used to show messages on html output).
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
Return localtax vat rate of a product in a particular country or default country vat if product is un...
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null, $include=null)
Return array of possible common substitutions.
dol_nboflines($s, $maxchar=0)
Return nb of lines of a clear text.
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js, dangerous content and external link.
jsonOrUnserialize($stringtodecode)
Decode an encode string.
addSummaryTableLine($tableColumnCount, $num, $nbofloop=0, $total=0, $noneWord="None", $extraRightColumn=false)
Add a summary line to the current open table ("None", "XMoreLines" or "Total xxx")
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
dol_escape_xml($stringtoescape)
Returns text escaped for inclusion into a XML string.
getActionCommEcmList($object)
getActionCommEcmList
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
img_edit_add($titlealt='default', $other='')
Show logo +.
print_fiche_titre($title, $mesg='', $picto='generic', $pictoisfullpath=0, $id='')
Show a title with picto.
get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice=0)
Return vat rate of a product in a particular country, or default country vat if product is unknown.
dolForgeDummyCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax string.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
dolForgeCriteriaCallback($matches)
Function to forge a SQL criteria from a Dolibarr filter syntax string.
getDolGlobalString($key, $default='')
Return 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.
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 ...
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
table table fiche title col title div col center btnTitle icon
Automatically calls the icon named with the corresponding "object_" prefix.
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition inc.php:420
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition main.inc.php:86
dol_setcache($memoryid, $data, $expire=0)
Save data into a memory area shared by all users, all sessions on server.
dol_getcache($memoryid)
Read a memory area shared by all users, all sessions on server.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:137
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:140
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length for cryptographic purposes.
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e rowid
Definition invoice.php:2010