dolibarr 20.0.5
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-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-2024 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 $regreplace = array();
966 $maxloop = 20;
967 $loopnb = 0; // Protection against infinite loop
968 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.
969 $loopnb++;
970 $newout = '';
971
972 if ($reg[1] == 'DAY') {
973 $tmp = dol_getdate(dol_now(), true);
974 $newout = $tmp['mday'];
975 } elseif ($reg[1] == 'MONTH') {
976 $tmp = dol_getdate(dol_now(), true);
977 $newout = $tmp['mon'];
978 } elseif ($reg[1] == 'YEAR') {
979 $tmp = dol_getdate(dol_now(), true);
980 $newout = $tmp['year'];
981 } elseif ($reg[1] == 'PREVIOUS_DAY') {
982 $tmp = dol_getdate(dol_now(), true);
983 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
984 $newout = $tmp2['day'];
985 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
986 $tmp = dol_getdate(dol_now(), true);
987 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
988 $newout = $tmp2['month'];
989 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
990 $tmp = dol_getdate(dol_now(), true);
991 $newout = ($tmp['year'] - 1);
992 } elseif ($reg[1] == 'NEXT_DAY') {
993 $tmp = dol_getdate(dol_now(), true);
994 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
995 $newout = $tmp2['day'];
996 } elseif ($reg[1] == 'NEXT_MONTH') {
997 $tmp = dol_getdate(dol_now(), true);
998 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
999 $newout = $tmp2['month'];
1000 } elseif ($reg[1] == 'NEXT_YEAR') {
1001 $tmp = dol_getdate(dol_now(), true);
1002 $newout = ($tmp['year'] + 1);
1003 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1004 $newout = $mysoc->country_id;
1005 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1006 $newout = $user->id;
1007 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1008 $newout = $user->fk_user;
1009 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1010 $newout = $conf->entity;
1011 } elseif ($reg[1] == 'ID') {
1012 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1013 } else {
1014 $newout = 'REGREPLACE_'.$loopnb; // Key not found, we replace with temporary string to reload later
1015 $regreplace[$loopnb] = $reg[0];
1016 }
1017 //var_dump('__'.$reg[1].'__ -> '.$newout);
1018 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1019 }
1020 if (!empty($regreplace)) {
1021 foreach ($regreplace as $key => $value) {
1022 $out = preg_replace('/REGREPLACE_'.$key.'/', $value, $out);
1023 }
1024 }
1025 }
1026
1027 // Check type of variable and make sanitization according to this
1028 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1029 if (!is_array($out) || empty($out)) {
1030 $out = array();
1031 } else {
1032 $tmparray = explode(':', $check);
1033 if (!empty($tmparray[1])) {
1034 $tmpcheck = $tmparray[1];
1035 } else {
1036 $tmpcheck = 'alphanohtml';
1037 }
1038 foreach ($out as $outkey => $outval) {
1039 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1040 }
1041 }
1042 } else {
1043 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1044 // 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
1045 if (strpos($paramname, 'search_') === 0) {
1046 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1047 }
1048
1049 // @phan-suppress-next-line UnknownSanitizeType
1050 $out = sanitizeVal($out, $check, $filter, $options);
1051 }
1052
1053 // Sanitizing for special parameters.
1054 // 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.
1055 if (preg_match('/backtopage/', $paramname) || $paramname == 'backtolist' || $paramname == 'backtourl') {
1056 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1057 $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.
1058 do {
1059 $oldstringtoclean = $out;
1060 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1061 $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'
1062 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1063 } while ($oldstringtoclean != $out);
1064 }
1065
1066 // Code for search criteria persistence.
1067 // Save data into session if key start with 'search_'
1068 if (empty($method) || $method == 3 || $method == 4) {
1069 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1070 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1071
1072 // We save search key only if $out not empty that means:
1073 // - posted value not empty, or
1074 // - 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).
1075
1076 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1077 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1078 }
1079 }
1080 }
1081
1082 return $out;
1083}
1084
1094function GETPOSTINT($paramname, $method = 0)
1095{
1096 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1097}
1098
1099
1108function GETPOSTFLOAT($paramname, $rounding = '')
1109{
1110 // 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.)
1111 return (float) price2num(GETPOST($paramname), $rounding, 2);
1112}
1113
1114
1125function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1126{
1127 return sanitizeVal($out, $check, $filter, $options);
1128}
1129
1139function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1140{
1141 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1142 // Check is done after replacement
1143 switch ($check) {
1144 case 'none':
1145 break;
1146 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1147 if (!is_numeric($out)) {
1148 $out = '';
1149 }
1150 break;
1151 case 'intcomma':
1152 if (is_array($out)) {
1153 $out = implode(',', $out);
1154 }
1155 if (preg_match('/[^0-9,-]+/i', $out)) {
1156 $out = '';
1157 }
1158 break;
1159 case 'san_alpha':
1160 $out = filter_var($out, FILTER_SANITIZE_STRING);
1161 break;
1162 case 'email':
1163 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1164 break;
1165 case 'aZ':
1166 if (!is_array($out)) {
1167 $out = trim($out);
1168 if (preg_match('/[^a-z]+/i', $out)) {
1169 $out = '';
1170 }
1171 }
1172 break;
1173 case 'aZ09':
1174 if (!is_array($out)) {
1175 $out = trim($out);
1176 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1177 $out = '';
1178 }
1179 }
1180 break;
1181 case 'aZ09arobase': // great to sanitize $objecttype parameter
1182 if (!is_array($out)) {
1183 $out = trim($out);
1184 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1185 $out = '';
1186 }
1187 }
1188 break;
1189 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1190 if (!is_array($out)) {
1191 $out = trim($out);
1192 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1193 $out = '';
1194 }
1195 }
1196 break;
1197 case 'alpha': // No html and no ../ and "
1198 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1199 if (!is_array($out)) {
1200 $out = trim($out);
1201 do {
1202 $oldstringtoclean = $out;
1203 // Remove html tags
1204 $out = dol_string_nohtmltag($out, 0);
1205 // 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).
1206 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1207 // Remove also other dangerous string sequences
1208 // '../' or '..\' is dangerous because it allows dir transversals
1209 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1210 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1211 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1212 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1213 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1214 } while ($oldstringtoclean != $out);
1215 // keep lines feed
1216 }
1217 break;
1218 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'
1219 if (!is_array($out)) {
1220 $out = trim($out);
1221 do {
1222 $oldstringtoclean = $out;
1223 // Decode html entities
1224 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1225 // 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).
1226 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1227 // Remove also other dangerous string sequences
1228 // '../' or '..\' is dangerous because it allows dir transversals
1229 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1230 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1231 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1232 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1233 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1234 } while ($oldstringtoclean != $out);
1235 }
1236 break;
1237 case 'nohtml': // No html
1238 $out = dol_string_nohtmltag($out, 0);
1239 break;
1240 case 'restricthtmlnolink':
1241 case 'restricthtml': // Recommended for most html textarea
1242 case 'restricthtmlallowclass':
1243 case 'restricthtmlallowunvalid':
1244 $out = dol_htmlwithnojs($out, 1, $check);
1245 break;
1246 case 'custom':
1247 if (!empty($out)) {
1248 if (empty($filter)) {
1249 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1250 }
1251 if (is_null($options)) {
1252 $options = 0;
1253 }
1254 $out = filter_var($out, $filter, $options);
1255 }
1256 break;
1257 default:
1258 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1259 $out = GETPOST($out, 'alphanohtml');
1260 break;
1261 }
1262
1263 return $out;
1264}
1265
1266
1267if (!function_exists('dol_getprefix')) {
1278 function dol_getprefix($mode = '')
1279 {
1280 // If prefix is for email (we need to have $conf already loaded for this case)
1281 if ($mode == 'email') {
1282 global $conf;
1283
1284 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1285 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1286 return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1287 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1288 return $_SERVER["SERVER_NAME"];
1289 }
1290 }
1291
1292 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1293 if (!empty($conf->file->instance_unique_id)) {
1294 return sha1('dolibarr'.$conf->file->instance_unique_id);
1295 }
1296
1297 // For backward compatibility when instance_unique_id is not set
1298 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1299 }
1300
1301 // If prefix is for session (no need to have $conf loaded)
1302 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1303 $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
1304
1305 // The recommended value (may be not defined for old versions)
1306 if (!empty($tmp_instance_unique_id)) {
1307 return sha1('dolibarr'.$tmp_instance_unique_id);
1308 }
1309
1310 // For backward compatibility when instance_unique_id is not set
1311 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1312 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1313 } else {
1314 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1315 }
1316 }
1317}
1318
1329function dol_include_once($relpath, $classname = '')
1330{
1331 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']
1332
1333 $fullpath = dol_buildpath($relpath);
1334
1335 if (!file_exists($fullpath)) {
1336 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1337 return false;
1338 }
1339
1340 if (!empty($classname) && !class_exists($classname)) {
1341 return include $fullpath;
1342 } else {
1343 return include_once $fullpath;
1344 }
1345}
1346
1347
1361function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1362{
1363 global $conf;
1364
1365 $path = preg_replace('/^\//', '', $path);
1366
1367 if (empty($type)) { // For a filesystem path
1368 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1369 if (is_array($conf->file->dol_document_root)) {
1370 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1371 if ($key == 'main') {
1372 continue;
1373 }
1374 // if (@file_exists($dirroot.'/'.$path)) {
1375 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1376 $res = $dirroot.'/'.$path;
1377 return $res;
1378 }
1379 }
1380 }
1381 if ($returnemptyifnotfound) {
1382 // Not found into alternate dir
1383 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1384 return '';
1385 }
1386 }
1387 } else {
1388 // For an url path
1389 // We try to get local path of file on filesystem from url
1390 // Note that trying to know if a file on disk exist by forging path on disk from url
1391 // works only for some web server and some setup. This is bugged when
1392 // using proxy, rewriting, virtual path, etc...
1393 $res = '';
1394 if ($type == 1) {
1395 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1396 }
1397 if ($type == 2) {
1398 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1399 }
1400 if ($type == 3) {
1401 $res = DOL_URL_ROOT.'/'.$path;
1402 }
1403
1404 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1405 if ($key == 'main') {
1406 if ($type == 3) {
1407 /*global $dolibarr_main_url_root;*/
1408
1409 // Define $urlwithroot
1410 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1411 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1412 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1413
1414 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1415 }
1416 continue;
1417 }
1418 $regs = array();
1419 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1420 if (!empty($regs[1])) {
1421 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1422 //if (file_exists($dirroot.'/'.$regs[1])) {
1423 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1424 if ($type == 1) {
1425 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1426 } elseif ($type == 2) {
1427 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1428 } elseif ($type == 3) {
1429 /*global $dolibarr_main_url_root;*/
1430
1431 // Define $urlwithroot
1432 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1433 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1434 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1435
1436 $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
1437 }
1438 break;
1439 }
1440 }
1441 }
1442 }
1443
1444 return $res;
1445}
1446
1457function dol_get_object_properties($obj, $properties = [])
1458{
1459 // Get real properties using get_object_vars() if $properties is empty
1460 if (empty($properties)) {
1461 return get_object_vars($obj);
1462 }
1463
1464 $existingProperties = [];
1465 $realProperties = get_object_vars($obj);
1466
1467 // Get the real or magic property values
1468 foreach ($properties as $property) {
1469 if (array_key_exists($property, $realProperties)) {
1470 // Real property, add the value
1471 $existingProperties[$property] = $obj->{$property};
1472 } elseif (property_exists($obj, $property)) {
1473 // Magic property
1474 $existingProperties[$property] = $obj->{$property};
1475 }
1476 }
1477
1478 return $existingProperties;
1479}
1480
1481
1496function dol_clone($object, $native = 0)
1497{
1498 if ($native == 0) {
1499 // deprecated method, use the method with native = 2 instead
1500 $tmpsavdb = null;
1501 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1502 $tmpsavdb = $object->db;
1503 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1504 }
1505
1506 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1507
1508 if (!empty($tmpsavdb)) {
1509 $object->db = $tmpsavdb;
1510 }
1511 } elseif ($native == 2) {
1512 // recommended method to have a full isolated cloned object
1513 $myclone = new stdClass();
1514 $tmparray = get_object_vars($object); // return only public properties
1515
1516 if (is_array($tmparray)) {
1517 foreach ($tmparray as $propertykey => $propertyval) {
1518 if (is_scalar($propertyval) || is_array($propertyval)) {
1519 $myclone->$propertykey = $propertyval;
1520 }
1521 }
1522 }
1523 } else {
1524 $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)
1525 }
1526
1527 return $myclone;
1528}
1529
1539function dol_size($size, $type = '')
1540{
1541 global $conf;
1542 if (empty($conf->dol_optimize_smallscreen)) {
1543 return $size;
1544 }
1545 if ($type == 'width' && $size > 250) {
1546 return 250;
1547 } else {
1548 return 10;
1549 }
1550}
1551
1552
1564function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1565{
1566 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1567 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1568 // Char '/' and '\' are file delimiters.
1569 // 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
1570 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1571 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1572 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1573 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1574 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1575 $tmp = str_replace('..', '', $tmp);
1576 return $tmp;
1577}
1578
1579
1591function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1592{
1593 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1594 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1595 // 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
1596 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1597 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1598 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1599 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1600 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1601 $tmp = str_replace('..', '', $tmp);
1602 return $tmp;
1603}
1604
1612function dol_sanitizeUrl($stringtoclean, $type = 1)
1613{
1614 // 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)
1615 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1616 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1617 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1618 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1619
1620 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1621 if ($type == 1) {
1622 // removing : should disable links to external url like http:aaa)
1623 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1624 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1625 }
1626
1627 do {
1628 $oldstringtoclean = $stringtoclean;
1629 // removing '&colon' should disable links to external url like http:aaa)
1630 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1631 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1632 } while ($oldstringtoclean != $stringtoclean);
1633
1634 if ($type == 1) {
1635 // removing '//' should disable links to external url like //aaa or http//)
1636 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1637 }
1638
1639 return $stringtoclean;
1640}
1641
1648function dol_sanitizeEmail($stringtoclean)
1649{
1650 do {
1651 $oldstringtoclean = $stringtoclean;
1652 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1653 } while ($oldstringtoclean != $stringtoclean);
1654
1655 return $stringtoclean;
1656}
1657
1666function dol_string_unaccent($str)
1667{
1668 global $conf;
1669
1670 if (is_null($str)) {
1671 return '';
1672 }
1673
1674 if (utf8_check($str)) {
1675 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1676 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1677 return $transliterator->transliterate($str);
1678 }
1679 // See http://www.utf8-chartable.de/
1680 $string = rawurlencode($str);
1681 $replacements = array(
1682 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1683 '%C3%87' => 'C',
1684 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1685 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1686 '%C3%91' => 'N',
1687 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1688 '%C5%A0' => 'S',
1689 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1690 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1691 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1692 '%C3%A7' => 'c',
1693 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1694 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1695 '%C3%B1' => 'n',
1696 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1697 '%C5%A1' => 's',
1698 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1699 '%C3%BD' => 'y', '%C3%BF' => 'y'
1700 );
1701 $string = strtr($string, $replacements);
1702 return rawurldecode($string);
1703 } else {
1704 // See http://www.ascii-code.com/
1705 $string = strtr(
1706 $str,
1707 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1708 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1709 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1710 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1711 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1712 \xF9\xFA\xFB\xFC\xFD\xFF",
1713 "AAAAAAC
1714 EEEEIIIIDN
1715 OOOOOUUUY
1716 aaaaaaceeee
1717 iiiidnooooo
1718 uuuuyy"
1719 );
1720 $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"));
1721 return $string;
1722 }
1723}
1724
1738function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1739{
1740 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1741 if (empty($keepspaces)) {
1742 $forbidden_chars_to_replace[] = " ";
1743 }
1744 $forbidden_chars_to_remove = array();
1745 //$forbidden_chars_to_remove=array("(",")");
1746
1747 if (is_array($badcharstoreplace)) {
1748 $forbidden_chars_to_replace = $badcharstoreplace;
1749 }
1750 if (is_array($badcharstoremove)) {
1751 $forbidden_chars_to_remove = $badcharstoremove;
1752 }
1753
1754 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1755 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1756}
1757
1758
1772function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1773{
1774 if ($removetabcrlf) {
1775 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1776 } else {
1777 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
1778 }
1779}
1780
1789function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1790{
1791 if (is_null($stringtoescape)) {
1792 return '';
1793 }
1794
1795 // escape quotes and backslashes, newlines, etc.
1796 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1797 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1798 if (empty($noescapebackslashn)) {
1799 $substitjs["\n"] = '\\n';
1800 $substitjs['\\'] = '\\\\';
1801 }
1802 if (empty($mode)) {
1803 $substitjs["'"] = "\\'";
1804 $substitjs['"'] = "\\'";
1805 } elseif ($mode == 1) {
1806 $substitjs["'"] = "\\'";
1807 } elseif ($mode == 2) {
1808 $substitjs['"'] = '\\"';
1809 } elseif ($mode == 3) {
1810 $substitjs["'"] = "\\'";
1811 $substitjs['"'] = "\\\"";
1812 }
1813 return strtr($stringtoescape, $substitjs);
1814}
1815
1825function dol_escape_uri($stringtoescape)
1826{
1827 return rawurlencode($stringtoescape);
1828}
1829
1836function dol_escape_json($stringtoescape)
1837{
1838 return str_replace('"', '\"', $stringtoescape);
1839}
1840
1848function dol_escape_php($stringtoescape, $stringforquotes = 2)
1849{
1850 if (is_null($stringtoescape)) {
1851 return '';
1852 }
1853
1854 if ($stringforquotes == 2) {
1855 return str_replace('"', "'", $stringtoescape);
1856 } elseif ($stringforquotes == 1) {
1857 // We remove the \ char.
1858 // If we allow the \ char, we can have $stringtoescape =
1859 // abc\';phpcodedanger; so the escapement will become
1860 // abc\\';phpcodedanger; and injecting this into
1861 // $a='...' will give $ac='abc\\';phpcodedanger;
1862 $stringtoescape = str_replace('\\', '', $stringtoescape);
1863 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1864 }
1865
1866 return 'Bad parameter for stringforquotes in dol_escape_php';
1867}
1868
1875function dol_escape_all($stringtoescape)
1876{
1877 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1878}
1879
1886function dol_escape_xml($stringtoescape)
1887{
1888 return $stringtoescape;
1889}
1890
1898function dolPrintLabel($s)
1899{
1900 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1901}
1902
1912function dolPrintHTML($s, $allowiframe = 0)
1913{
1914 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1915}
1916
1924function dolPrintHTMLForAttribute($s)
1925{
1926 // The dol_htmlentitiesbr will convert simple text into html
1927 // The dol_escape_htmltag will escape html chars.
1928 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
1929}
1930
1939function dolPrintHTMLForTextArea($s, $allowiframe = 0)
1940{
1941 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
1942}
1943
1950function dolPrintPassword($s)
1951{
1952 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
1953}
1954
1955
1972function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1973{
1974 if ($noescapetags == 'common') {
1975 $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';
1976 // Add also html5 tags
1977 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
1978 }
1979 if ($cleanalsojavascript) {
1980 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1981 }
1982
1983 // escape quotes and backslashes, newlines, etc.
1984 if ($escapeonlyhtmltags) {
1985 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1986 } else {
1987 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
1988 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
1989 }
1990 if (!$keepb) {
1991 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
1992 }
1993 if (!$keepn) {
1994 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
1995 } elseif ($keepn == -1) {
1996 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
1997 }
1998
1999 if ($escapeonlyhtmltags) {
2000 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2001 } else {
2002 // Escape tags to keep
2003 $tmparrayoftags = array();
2004 if ($noescapetags) {
2005 $tmparrayoftags = explode(',', $noescapetags);
2006 }
2007 if (count($tmparrayoftags)) {
2008 $reg = array();
2009 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2010
2011 foreach ($tmparrayoftags as $tagtoreplace) {
2012 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2013 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2014 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2015
2016 // For case of tag with attribute
2017 do {
2018 $tmpold = $tmp;
2019
2020 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2021 // We want to pprotect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() lafter
2022 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2023 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes); // TODO Try to remove this
2024 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2025 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2026 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2027 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2028 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2029 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2030
2031 // TODO Test the replacement by using a memory array for attributes to restore them
2032 // TODO Test a tag like '<a href="https://mydomain.com/aaa%20bbb">abc</a>'
2033 //$tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2034 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2035 }
2036 // TODO This may be already in previous case ? Try to remove this.
2037 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)(\s+)\/>/', $tmp, $reg)) {
2038 // We want to protect the attribute part ... in '<xxx ... />' to avoid transformation by htmlentities() lafter
2039 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must not have [ ] inside the attribute string
2040 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2041 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2042 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2043 //$tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2044 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].$reg[3].'/>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2045 }
2046
2047 $diff = strcmp($tmpold, $tmp);
2048 } while ($diff);
2049 }
2050 }
2051
2052 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2053
2054 //print $result;
2055
2056 if (count($tmparrayoftags)) {
2057 foreach ($tmparrayoftags as $tagtoreplace) {
2058 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2059 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2060 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2061 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2062 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2063 }
2064
2065 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2066 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2067 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2068 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2069 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2070 }
2071
2072 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2073
2074 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2075
2076 return $result;
2077 }
2078}
2079
2087function dol_strtolower($string, $encoding = "UTF-8")
2088{
2089 if (function_exists('mb_strtolower')) {
2090 return mb_strtolower($string, $encoding);
2091 } else {
2092 return strtolower($string);
2093 }
2094}
2095
2104function dol_strtoupper($string, $encoding = "UTF-8")
2105{
2106 if (function_exists('mb_strtoupper')) {
2107 return mb_strtoupper($string, $encoding);
2108 } else {
2109 return strtoupper($string);
2110 }
2111}
2112
2121function dol_ucfirst($string, $encoding = "UTF-8")
2122{
2123 if (function_exists('mb_substr')) {
2124 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2125 } else {
2126 return ucfirst($string);
2127 }
2128}
2129
2138function dol_ucwords($string, $encoding = "UTF-8")
2139{
2140 if (function_exists('mb_convert_case')) {
2141 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2142 } else {
2143 return ucwords($string);
2144 }
2145}
2146
2169function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2170{
2171 global $conf, $user, $debugbar;
2172
2173 // If syslog module enabled
2174 if (!isModEnabled('syslog')) {
2175 return;
2176 }
2177
2178 // Check if we are into execution of code of a website
2179 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2180 global $website, $websitekey;
2181 if (is_object($website) && !empty($website->ref)) {
2182 $suffixinfilename .= '_website_'.$website->ref;
2183 } elseif (!empty($websitekey)) {
2184 $suffixinfilename .= '_website_'.$websitekey;
2185 }
2186 }
2187
2188 // Check if we have a forced suffix
2189 if (defined('USESUFFIXINLOG')) {
2190 $suffixinfilename .= constant('USESUFFIXINLOG');
2191 }
2192
2193 if ($ident < 0) {
2194 foreach ($conf->loghandlers as $loghandlerinstance) {
2195 $loghandlerinstance->setIdent($ident);
2196 }
2197 }
2198
2199 if (!empty($message)) {
2200 // Test log level
2201 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2202 $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');
2203
2204 if (!array_key_exists($level, $logLevels)) {
2205 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2206 $level = $logLevels[LOG_ERR];
2207 }
2208 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2209 return;
2210 }
2211
2212 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2213 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2214 }
2215
2216 // If adding log inside HTML page is required
2217 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2218 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2219 $ospid = sprintf("%7s", dol_trunc(getmypid(), 7, 'right', 'UTF-8', 1));
2220 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2221
2222 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2223 }
2224
2225 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2226 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2227 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2228 print "\n\n<!-- Log start\n";
2229 print dol_escape_htmltag($message)."\n";
2230 print "Log end -->\n";
2231 }
2232
2233 $data = array(
2234 'message' => $message,
2235 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2236 'level' => $level,
2237 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2238 'ip' => false,
2239 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2240 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2241 );
2242
2243 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2244 if (!empty($remoteip)) {
2245 $data['ip'] = $remoteip;
2246 // This is when server run behind a reverse proxy
2247 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2248 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2249 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2250 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2251 }
2252 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2253 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2254 $data['ip'] = $_SERVER['SERVER_ADDR'];
2255 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2256 // 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).
2257 $data['ip'] = $_SERVER['COMPUTERNAME'];
2258 } else {
2259 $data['ip'] = '???';
2260 }
2261
2262 if (!empty($_SERVER['USERNAME'])) {
2263 // 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).
2264 $data['osuser'] = $_SERVER['USERNAME'];
2265 } elseif (!empty($_SERVER['LOGNAME'])) {
2266 // 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).
2267 $data['osuser'] = $_SERVER['LOGNAME'];
2268 }
2269
2270 // Loop on each log handler and send output
2271 foreach ($conf->loghandlers as $loghandlerinstance) {
2272 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2273 continue;
2274 }
2275 $loghandlerinstance->export($data, $suffixinfilename);
2276 }
2277 unset($data);
2278 }
2279
2280 if ($ident > 0) {
2281 foreach ($conf->loghandlers as $loghandlerinstance) {
2282 $loghandlerinstance->setIdent($ident);
2283 }
2284 }
2285}
2286
2298function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2299{
2300 global $langs, $db;
2301
2302 $form = new Form($db);
2303
2304 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2305 if (empty($templatenameforexport)) {
2306 $templatenameforexport = 'website_'.$website->ref;
2307 }
2308
2309 $out = '';
2310 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2311
2312 // for generate popup
2313 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2314 $out .= 'jQuery(document).ready(function () {';
2315 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2316 $out .= ' var dialogHtml = \'';
2317
2318 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2319 $dialogcontent .= ' <div style="margin-top: 20px;">';
2320 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2321 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2322 $dialogcontent .= ' </div>';
2323 $dialogcontent .= ' <br>';
2324 $dialogcontent .= ' <div style="margin-top: 20px;">';
2325 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2326 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2327 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2328 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2329 $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>';
2330 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2331 $dialogcontent .= ' </form>';
2332 $dialogcontent .= ' </div>';
2333 $dialogcontent .= ' </div>';
2334
2335 $out .= dol_escape_js($dialogcontent);
2336
2337 $out .= '\';';
2338
2339
2340 // Add the content of the dialog to the body of the page
2341 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2342 $out .= ' if ($dialog.length > 0) {
2343 $dialog.remove();
2344 }
2345 jQuery("body").append(dialogHtml);';
2346
2347 // Configuration of popup
2348 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2349 $out .= ' autoOpen: false,';
2350 $out .= ' modal: true,';
2351 $out .= ' height: 290,';
2352 $out .= ' width: "40%",';
2353 $out .= ' title: "' . dol_escape_js($label) . '",';
2354 $out .= ' });';
2355
2356 // Simulate a click on the original "submit" input to export the site.
2357 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2358 $out .= ' console.log("Clic on exportsite.");';
2359 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2360 $out .= ' console.log("element founded:", target.length > 0);';
2361 $out .= ' if (target.length > 0) { target.click(); }';
2362 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2363 $out .= ' });';
2364
2365 // open popup
2366 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2367 $out .= ' return false;';
2368 $out .= ' });';
2369 $out .= '});';
2370 $out .= '</script>';
2371
2372 return $out;
2373}
2374
2375
2392function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2393{
2394 global $conf;
2395
2396 if (strpos($url, '?') > 0) {
2397 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2398 } else {
2399 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2400 }
2401
2402 $out = '';
2403
2404 $backtopagejsfieldsid = '';
2405 $backtopagejsfieldslabel = '';
2406 if ($backtopagejsfields) {
2407 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2408 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2409 $backtopagejsfields = $name.":".$backtopagejsfields;
2410 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2411 } else {
2412 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2413 }
2414 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2415 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2416 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2417 }
2418
2419 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2420 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2421 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2422 if (empty($conf->use_javascript_ajax)) {
2423 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2424 } elseif ($jsonopen) {
2425 $out .= ' href="#" onclick="'.$jsonopen.'"';
2426 } else {
2427 $out .= ' href="#"';
2428 }
2429 $out .= '>'.$buttonstring.'</a>';
2430
2431 if (!empty($conf->use_javascript_ajax)) {
2432 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2433 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2434 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2435 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2436 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2437
2438 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2439 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2440 jQuery(document).ready(function () {
2441 jQuery(".button_'.$name.'").click(function () {
2442 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2443 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2444 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2445 $tmpdialog.dialog({
2446 autoOpen: false,
2447 modal: true,
2448 height: (window.innerHeight - 150),
2449 width: \'80%\',
2450 title: \''.dol_escape_js($label).'\',
2451 open: function (event, ui) {
2452 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2453 },
2454 close: function (event, ui) {
2455 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2456 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2457 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2458 if (returnedid != "" && returnedid != "div for returned id") {
2459 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2460 }
2461 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2462 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2463 }
2464 }
2465 });
2466
2467 $tmpdialog.dialog(\'open\');
2468 return false;
2469 });
2470 });
2471 </script>';
2472 }
2473 return $out;
2474}
2475
2492function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2493{
2494 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2495}
2496
2513function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2514{
2515 global $conf, $langs, $hookmanager;
2516
2517 // Show title
2518 $showtitle = 1;
2519 if (!empty($conf->dol_optimize_smallscreen)) {
2520 $showtitle = 0;
2521 }
2522
2523 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2524
2525 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2526 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2527 }
2528
2529 // Show right part
2530 if ($morehtmlright) {
2531 $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.
2532 }
2533
2534 // Show title
2535 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2536 $limittitle = 30;
2537 $out .= '<a class="tabTitle">';
2538 if ($picto) {
2539 $noprefix = $pictoisfullpath;
2540 if (strpos($picto, 'fontawesome_') !== false) {
2541 $noprefix = 1;
2542 }
2543 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2544 }
2545 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2546 $out .= '</a>';
2547 }
2548
2549 // Show tabs
2550
2551 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2552 $maxkey = -1;
2553 if (is_array($links) && !empty($links)) {
2554 $keys = array_keys($links);
2555 if (count($keys)) {
2556 $maxkey = max($keys);
2557 }
2558 }
2559
2560 // Show tabs
2561 // if =0 we don't use the feature
2562 if (empty($limittoshow)) {
2563 $limittoshow = (!getDolGlobalString('MAIN_MAXTABS_IN_CARD') ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2564 }
2565 if (!empty($conf->dol_optimize_smallscreen)) {
2566 $limittoshow = 2;
2567 }
2568
2569 $displaytab = 0;
2570 $nbintab = 0;
2571 $popuptab = 0;
2572 $outmore = '';
2573 for ($i = 0; $i <= $maxkey; $i++) {
2574 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2575 // If active tab is already present
2576 if ($i >= $limittoshow) {
2577 $limittoshow--;
2578 }
2579 }
2580 }
2581
2582 for ($i = 0; $i <= $maxkey; $i++) {
2583 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2584 $isactive = true;
2585 } else {
2586 $isactive = false;
2587 }
2588
2589 if ($i < $limittoshow || $isactive) {
2590 // Output entry with a visible tab
2591 $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])).' -->';
2592
2593 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2594 if (!empty($links[$i][0])) {
2595 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2596 } else {
2597 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2598 }
2599 } elseif (!empty($links[$i][1])) {
2600 //print "x $i $active ".$links[$i][2]." z";
2601 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2602 if (!empty($links[$i][0])) {
2603 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2604 $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).'">';
2605 }
2606 $out .= $links[$i][1];
2607 if (!empty($links[$i][0])) {
2608 $out .= '</a>'."\n";
2609 }
2610 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2611 $out .= '</div>';
2612 }
2613
2614 $out .= '</div>';
2615 } else {
2616 // Add entry into the combo popup with the other tabs
2617 if (!$popuptab) {
2618 $popuptab = 1;
2619 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2620 }
2621 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2622 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2623 if (!empty($links[$i][0])) {
2624 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2625 } else {
2626 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2627 }
2628 } elseif (!empty($links[$i][1])) {
2629 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2630 $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.
2631 $outmore .= '</a>'."\n";
2632 }
2633 $outmore .= '</div>';
2634
2635 $nbintab++;
2636 }
2637 $displaytab = $i;
2638 }
2639 if ($popuptab) {
2640 $outmore .= '</div>';
2641 }
2642
2643 if ($popuptab) { // If there is some tabs not shown
2644 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2645 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2646 $widthofpopup = 200;
2647
2648 $tabsname = $moretabssuffix;
2649 if (empty($tabsname)) {
2650 $tabsname = str_replace("@", "", $picto);
2651 }
2652 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2653 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2654 $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".
2655 }
2656 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2657 $out .= $outmore;
2658 $out .= '</div>';
2659 $out .= '<div></div>';
2660 $out .= "</div>\n";
2661
2662 $out .= '<script nonce="'.getNonce().'">';
2663 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2664 var x = this.offsetLeft, y = this.offsetTop;
2665 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2666 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2667 $('#moretabsList".$tabsname."').css('".$right."','8px');
2668 }
2669 $('#moretabsList".$tabsname."').css('".$left."','auto');
2670 });
2671 ";
2672 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2673 $out .= "</script>";
2674 }
2675
2676 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2677 $out .= "</div>\n";
2678 }
2679
2680 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2681 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom')));
2682 $out .= '">'."\n";
2683 }
2684 if (!empty($dragdropfile)) {
2685 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2686 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2687 }
2688 $parameters = array('tabname' => $active, 'out' => $out);
2689 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2690 if ($reshook > 0) {
2691 $out = $hookmanager->resPrint;
2692 }
2693
2694 return $out;
2695}
2696
2704function dol_fiche_end($notab = 0)
2705{
2706 print dol_get_fiche_end($notab);
2707}
2708
2715function dol_get_fiche_end($notab = 0)
2716{
2717 if (!$notab || $notab == -1) {
2718 return "\n</div>\n";
2719 } else {
2720 return '';
2721 }
2722}
2723
2743function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2744{
2745 global $conf, $form, $user, $langs, $hookmanager, $action;
2746
2747 $error = 0;
2748
2749 $maxvisiblephotos = 1;
2750 $showimage = 1;
2751 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2752 // @phan-suppress-next-line PhanUndeclaredMethod
2753 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2754 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2755 $showbarcode = 0;
2756 }
2757 $modulepart = 'unknown';
2758
2759 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2760 $modulepart = $object->element;
2761 } elseif ($object->element == 'member') {
2762 $modulepart = 'memberphoto';
2763 } elseif ($object->element == 'user') {
2764 $modulepart = 'userphoto';
2765 }
2766
2767 if (class_exists("Imagick")) {
2768 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2769 $modulepart = $object->element;
2770 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2771 $modulepart = 'ficheinter';
2772 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2773 $modulepart = 'contract';
2774 } elseif ($object->element == 'order_supplier') {
2775 $modulepart = 'supplier_order';
2776 } elseif ($object->element == 'invoice_supplier') {
2777 $modulepart = 'supplier_invoice';
2778 }
2779 }
2780
2781 if ($object->element == 'product') {
2783 '@phan-var-force Product $object';
2784 $width = 80;
2785 $cssclass = 'photowithmargin photoref';
2786 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2787 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2788 if ($conf->browser->layout == 'phone') {
2789 $maxvisiblephotos = 1;
2790 }
2791 if ($showimage) {
2792 $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>';
2793 } else {
2794 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2795 $nophoto = '';
2796 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2797 } else { // Show no photo link
2798 $nophoto = '/public/theme/common/nophoto.png';
2799 $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>';
2800 }
2801 }
2802 } elseif ($object->element == 'category') {
2804 '@phan-var-force Categorie $object';
2805 $width = 80;
2806 $cssclass = 'photowithmargin photoref';
2807 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2808 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2809 if ($conf->browser->layout == 'phone') {
2810 $maxvisiblephotos = 1;
2811 }
2812 if ($showimage) {
2813 $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>';
2814 } else {
2815 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2816 $nophoto = '';
2817 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2818 } else { // Show no photo link
2819 $nophoto = '/public/theme/common/nophoto.png';
2820 $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>';
2821 }
2822 }
2823 } elseif ($object->element == 'bom') {
2825 '@phan-var-force Bom $object';
2826 $width = 80;
2827 $cssclass = 'photowithmargin photoref';
2828 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2829 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2830 if ($conf->browser->layout == 'phone') {
2831 $maxvisiblephotos = 1;
2832 }
2833 if ($showimage) {
2834 $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>';
2835 } else {
2836 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2837 $nophoto = '';
2838 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2839 } else { // Show no photo link
2840 $nophoto = '/public/theme/common/nophoto.png';
2841 $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>';
2842 }
2843 }
2844 } elseif ($object->element == 'ticket') {
2845 $width = 80;
2846 $cssclass = 'photoref';
2848 '@phan-var-force Ticket $object';
2849 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2850 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2851 if ($conf->browser->layout == 'phone') {
2852 $maxvisiblephotos = 1;
2853 }
2854
2855 if ($showimage) {
2856 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2857 if ($object->nbphoto > 0) {
2858 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2859 } else {
2860 $showimage = 0;
2861 }
2862 }
2863 if (!$showimage) {
2864 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2865 $nophoto = '';
2866 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2867 } else { // Show no photo link
2868 $nophoto = img_picto('No photo', 'object_ticket');
2869 $morehtmlleft .= '<!-- No photo to show -->';
2870 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2871 $morehtmlleft .= $nophoto;
2872 $morehtmlleft .= '</div></div>';
2873 }
2874 }
2875 } else {
2876 if ($showimage) {
2877 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2878 $phototoshow = '';
2879 // Check if a preview file is available
2880 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2881 $objectref = dol_sanitizeFileName($object->ref);
2882 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2883 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2884 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2885 $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
2886 } else {
2887 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2888 }
2889 if (empty($subdir)) {
2890 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2891 }
2892
2893 $filepath = $dir_output.$subdir."/";
2894
2895 $filepdf = $filepath.$objectref.".pdf";
2896 $relativepath = $subdir.'/'.$objectref.'.pdf';
2897
2898 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2899 $fileimage = $filepdf.'_preview.png';
2900 $relativepathimage = $relativepath.'_preview.png';
2901
2902 $pdfexists = file_exists($filepdf);
2903
2904 // If PDF file exists
2905 if ($pdfexists) {
2906 // Conversion du PDF en image png si fichier png non existent
2907 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2908 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2909 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2910 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2911 if ($ret < 0) {
2912 $error++;
2913 }
2914 }
2915 }
2916 }
2917
2918 if ($pdfexists && !$error) {
2919 $heightforphotref = 80;
2920 if (!empty($conf->dol_optimize_smallscreen)) {
2921 $heightforphotref = 60;
2922 }
2923 // If the preview file is found
2924 if (file_exists($fileimage)) {
2925 $phototoshow = '<div class="photoref">';
2926 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2927 $phototoshow .= '</div>';
2928 }
2929 }
2930 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
2931 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2932 }
2933
2934 if ($phototoshow) {
2935 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2936 $morehtmlleft .= $phototoshow;
2937 $morehtmlleft .= '</div>';
2938 }
2939 }
2940
2941 if (empty($phototoshow)) { // Show No photo link (picto of object)
2942 if ($object->element == 'action') {
2943 $width = 80;
2944 $cssclass = 'photorefcenter';
2945 $nophoto = img_picto('No photo', 'title_agenda');
2946 } else {
2947 $width = 14;
2948 $cssclass = 'photorefcenter';
2949 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
2950 $prefix = 'object_';
2951 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
2952 $picto = 'project'; // instead of projectpub
2953 }
2954 if (strpos($picto, 'fontawesome_') !== false) {
2955 $prefix = '';
2956 }
2957 $nophoto = img_picto('No photo', $prefix.$picto);
2958 }
2959 $morehtmlleft .= '<!-- No photo to show -->';
2960 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2961 $morehtmlleft .= $nophoto;
2962 $morehtmlleft .= '</div></div>';
2963 }
2964 }
2965 }
2966
2967 // Show barcode
2968 if ($showbarcode) {
2969 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2970 }
2971
2972 if ($object->element == 'societe') {
2973 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2974 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2975 } else {
2976 $morehtmlstatus .= $object->getLibStatut(6);
2977 }
2978 } elseif ($object->element == 'product') {
2979 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2980 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2981 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2982 } else {
2983 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2984 }
2985 $morehtmlstatus .= ' &nbsp; ';
2986 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2987 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2988 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2989 } else {
2990 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2991 }
2992 } elseif (in_array($object->element, array('salary'))) {
2993 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
2994 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2995 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
2996 }
2997 $morehtmlstatus .= $tmptxt;
2998 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
2999 $totalallpayments = $object->getSommePaiement(0);
3000 $totalallpayments += $object->getSumCreditNotesUsed(0);
3001 $totalallpayments += $object->getSumDepositsUsed(0);
3002 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3003 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3004 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3005 }
3006 $morehtmlstatus .= $tmptxt;
3007 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3008 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3009 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3010 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3011 }
3012 $morehtmlstatus .= $tmptxt;
3013 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3014 if ($object->statut == 0) {
3015 $morehtmlstatus .= $object->getLibStatut(5);
3016 } else {
3017 $morehtmlstatus .= $object->getLibStatut(4);
3018 }
3019 } elseif ($object->element == 'facturerec') {
3020 '@phan-var-force FactureRec $object';
3021 if ($object->frequency == 0) {
3022 $morehtmlstatus .= $object->getLibStatut(2);
3023 } else {
3024 $morehtmlstatus .= $object->getLibStatut(5);
3025 }
3026 } elseif ($object->element == 'project_task') {
3027 $object->fk_statut = 1;
3028 if ($object->progress > 0) {
3029 $object->fk_statut = 2;
3030 }
3031 if ($object->progress >= 100) {
3032 $object->fk_statut = 3;
3033 }
3034 $tmptxt = $object->getLibStatut(5);
3035 $morehtmlstatus .= $tmptxt; // No status on task
3036 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3037 $tmptxt = $object->getLibStatut(6);
3038 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3039 $tmptxt = $object->getLibStatut(5);
3040 }
3041 $morehtmlstatus .= $tmptxt;
3042 }
3043
3044 // Add if object was dispatched "into accountancy"
3045 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3046 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3047 if (method_exists($object, 'getVentilExportCompta')) {
3048 $accounted = $object->getVentilExportCompta();
3049 $langs->load("accountancy");
3050 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3051 }
3052 }
3053
3054 // Add alias for thirdparty
3055 if (!empty($object->name_alias)) {
3056 '@phan-var-force Societe $object';
3057 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3058 }
3059
3060 // Add label
3061 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3062 if (!empty($object->label)) {
3063 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3064 }
3065 }
3066 // Show address and email
3067 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3068 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3069 if ($moreaddress) {
3070 $morehtmlref .= '<div class="refidno refaddress">';
3071 $morehtmlref .= $moreaddress;
3072 $morehtmlref .= '</div>';
3073 }
3074 }
3075 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)) {
3076 $morehtmlref .= '<div style="clear: both;"></div>';
3077 $morehtmlref .= '<div class="refidno opacitymedium">';
3078 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3079 $morehtmlref .= '</div>';
3080 }
3081
3082 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3083 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3084 if ($reshook < 0) {
3085 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3086 } elseif (empty($reshook)) {
3087 $morehtmlref .= $hookmanager->resPrint;
3088 } elseif ($reshook > 0) {
3089 $morehtmlref = $hookmanager->resPrint;
3090 }
3091
3092 // $morehtml is the right part (link "Back to list")
3093 // $morehtmlleft is the picto or photo of banner
3094 // $morehtmlstatus is part under the status
3095 // $morehtmlright is part of htmlright
3096
3097 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3098 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3099 print '</div>';
3100 print '<div class="underrefbanner clearboth"></div>';
3101}
3102
3112function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3113{
3114 global $langs;
3115 $ret = '';
3116 if ($fieldrequired) {
3117 $ret .= '<span class="fieldrequired">';
3118 }
3119 $ret .= '<label for="'.$fieldkey.'">';
3120 $ret .= $langs->trans($langkey);
3121 $ret .= '</label>';
3122 if ($fieldrequired) {
3123 $ret .= '</span>';
3124 }
3125 return $ret;
3126}
3127
3135function dol_bc($var, $moreclass = '')
3136{
3137 global $bc;
3138 $ret = ' '.$bc[$var];
3139 if ($moreclass) {
3140 $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
3141 }
3142 return $ret;
3143}
3144
3158function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3159{
3160 global $langs, $hookmanager;
3161
3162 $ret = '';
3163 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3164
3165 // See format of addresses on https://en.wikipedia.org/wiki/Address
3166 // Address
3167 if (empty($mode)) {
3168 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3169 }
3170 // Zip/Town/State
3171 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3172 // US: title firstname name \n address lines \n town, state, zip \n country
3173 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3174 $ret .= (($ret && $town) ? $sep : '').$town;
3175
3176 if (!empty($object->state)) {
3177 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3178 }
3179 if (!empty($object->zip)) {
3180 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3181 }
3182 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3183 // UK: title firstname name \n address lines \n town state \n zip \n country
3184 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3185 $ret .= ($ret ? $sep : '').$town;
3186 if (!empty($object->state)) {
3187 $ret .= ($ret ? ", " : '').$object->state;
3188 }
3189 if (!empty($object->zip)) {
3190 $ret .= ($ret ? $sep : '').$object->zip;
3191 }
3192 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3193 // ES: title firstname name \n address lines \n zip town \n state \n country
3194 $ret .= ($ret ? $sep : '').$object->zip;
3195 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3196 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3197 if (!empty($object->state)) {
3198 $ret .= $sep.$object->state;
3199 }
3200 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3201 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3202 // See https://www.sljfaq.org/afaq/addresses.html
3203 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3204 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3205 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3206 // IT: title firstname name\n address lines \n zip town state_code \n country
3207 $ret .= ($ret ? $sep : '').$object->zip;
3208 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3209 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3210 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3211 } else {
3212 // Other: title firstname name \n address lines \n zip town[, state] \n country
3213 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3214 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3215 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3216 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3217 $ret .= ($ret ? ", " : '').$object->state;
3218 }
3219 }
3220
3221 if (!is_object($outputlangs)) {
3222 $outputlangs = $langs;
3223 }
3224 if ($withcountry) {
3225 $langs->load("dict");
3226 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3227 }
3228 if ($hookmanager) {
3229 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3230 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3231 if ($reshook > 0) {
3232 $ret = '';
3233 }
3234 $ret .= $hookmanager->resPrint;
3235 }
3236
3237 return $ret;
3238}
3239
3240
3241
3251function dol_strftime($fmt, $ts = false, $is_gmt = false)
3252{
3253 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3254 return dol_print_date($ts, $fmt, $is_gmt);
3255 } else {
3256 return 'Error date outside supported range';
3257 }
3258}
3259
3281function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3282{
3283 global $conf, $langs;
3284
3285 // If date undefined or "", we return ""
3286 if (dol_strlen($time) == 0) {
3287 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3288 }
3289
3290 if ($tzoutput === 'auto') {
3291 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3292 }
3293
3294 // Clean parameters
3295 $to_gmt = false;
3296 $offsettz = $offsetdst = 0;
3297 if ($tzoutput) {
3298 $to_gmt = true; // For backward compatibility
3299 if (is_string($tzoutput)) {
3300 if ($tzoutput == 'tzserver') {
3301 $to_gmt = false;
3302 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3303 // @phan-suppress-next-line PhanPluginRedundantAssignment
3304 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3305 // @phan-suppress-next-line PhanPluginRedundantAssignment
3306 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3307 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3308 $to_gmt = true;
3309 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3310
3311 if (class_exists('DateTimeZone')) {
3312 $user_date_tz = new DateTimeZone($offsettzstring);
3313 $user_dt = new DateTime();
3314 $user_dt->setTimezone($user_date_tz);
3315 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3316 $offsettz = $user_dt->getOffset(); // should include dst ?
3317 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3318 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3319 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3320 }
3321 }
3322 }
3323 }
3324 if (!is_object($outputlangs)) {
3325 $outputlangs = $langs;
3326 }
3327 if (!$format) {
3328 $format = 'daytextshort';
3329 }
3330
3331 // Do we have to reduce the length of date (year on 2 chars) to save space.
3332 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3333 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3334 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3335 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3336 if ($formatwithoutreduce != $format) {
3337 $format = $formatwithoutreduce;
3338 $reduceformat = 1;
3339 } // so format 'dayreduceformat' is processed like day
3340
3341 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3342 // TODO Add format daysmallyear and dayhoursmallyear
3343 if ($format == 'day') {
3344 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3345 } elseif ($format == 'hour') {
3346 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3347 } elseif ($format == 'hourduration') {
3348 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3349 } elseif ($format == 'daytext') {
3350 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3351 } elseif ($format == 'daytextshort') {
3352 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3353 } elseif ($format == 'dayhour') {
3354 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3355 } elseif ($format == 'dayhoursec') {
3356 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3357 } elseif ($format == 'dayhourtext') {
3358 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3359 } elseif ($format == 'dayhourtextshort') {
3360 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3361 } elseif ($format == 'dayhourlog') {
3362 // Format not sensitive to language
3363 $format = '%Y%m%d%H%M%S';
3364 } elseif ($format == 'dayhourlogsmall') {
3365 // Format not sensitive to language
3366 $format = '%y%m%d%H%M';
3367 } elseif ($format == 'dayhourldap') {
3368 $format = '%Y%m%d%H%M%SZ';
3369 } elseif ($format == 'dayhourxcard') {
3370 $format = '%Y%m%dT%H%M%SZ';
3371 } elseif ($format == 'dayxcard') {
3372 $format = '%Y%m%d';
3373 } elseif ($format == 'dayrfc') {
3374 $format = '%Y-%m-%d'; // DATE_RFC3339
3375 } elseif ($format == 'dayhourrfc') {
3376 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3377 } elseif ($format == 'standard') {
3378 $format = '%Y-%m-%d %H:%M:%S';
3379 }
3380
3381 if ($reduceformat) {
3382 $format = str_replace('%Y', '%y', $format);
3383 $format = str_replace('yyyy', 'yy', $format);
3384 }
3385
3386 // Clean format
3387 if (preg_match('/%b/i', $format)) { // There is some text to translate
3388 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3389 $format = str_replace('%b', '__b__', $format);
3390 $format = str_replace('%B', '__B__', $format);
3391 }
3392 if (preg_match('/%a/i', $format)) { // There is some text to translate
3393 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3394 $format = str_replace('%a', '__a__', $format);
3395 $format = str_replace('%A', '__A__', $format);
3396 }
3397
3398 // Analyze date
3399 $reg = array();
3400 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
3401 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"]));
3402 return '';
3403 } 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
3404 // This part of code should not be used anymore.
3405 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);
3406 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3407 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3408 $syear = (!empty($reg[1]) ? $reg[1] : '');
3409 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3410 $sday = (!empty($reg[3]) ? $reg[3] : '');
3411 $shour = (!empty($reg[4]) ? $reg[4] : '');
3412 $smin = (!empty($reg[5]) ? $reg[5] : '');
3413 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3414
3415 $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
3416
3417 if ($to_gmt) {
3418 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3419 } else {
3420 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3421 }
3422 $dtts = new DateTime();
3423 $dtts->setTimestamp($time);
3424 $dtts->setTimezone($tzo);
3425 $newformat = str_replace(
3426 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3427 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3428 $format
3429 );
3430 $ret = $dtts->format($newformat);
3431 $ret = str_replace(
3432 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3433 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3434 $ret
3435 );
3436 } else {
3437 // Date is a timestamps
3438 if ($time < 100000000000) { // Protection against bad date values
3439 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3440
3441 if ($to_gmt) {
3442 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3443 } else {
3444 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3445 }
3446 $dtts = new DateTime();
3447 $dtts->setTimestamp($timetouse);
3448 $dtts->setTimezone($tzo);
3449 $newformat = str_replace(
3450 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3451 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3452 $format
3453 );
3454 $ret = $dtts->format($newformat);
3455 $ret = str_replace(
3456 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3457 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3458 $ret
3459 );
3460 //var_dump($ret);exit;
3461 } else {
3462 $ret = 'Bad value '.$time.' for date';
3463 }
3464 }
3465
3466 if (preg_match('/__b__/i', $format)) {
3467 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3468
3469 if ($to_gmt) {
3470 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3471 } else {
3472 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3473 }
3474 $dtts = new DateTime();
3475 $dtts->setTimestamp($timetouse);
3476 $dtts->setTimezone($tzo);
3477 $month = (int) $dtts->format("m");
3478 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3479 if ($encodetooutput) {
3480 $monthtext = $outputlangs->transnoentities('Month'.$month);
3481 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3482 } else {
3483 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3484 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3485 }
3486 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3487 $ret = str_replace('__b__', $monthtextshort, $ret);
3488 $ret = str_replace('__B__', $monthtext, $ret);
3489 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3490 //return $ret;
3491 }
3492 if (preg_match('/__a__/i', $format)) {
3493 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3494 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3495
3496 if ($to_gmt) {
3497 $tzo = new DateTimeZone('UTC');
3498 } else {
3499 $tzo = new DateTimeZone(date_default_timezone_get());
3500 }
3501 $dtts = new DateTime();
3502 $dtts->setTimestamp($timetouse);
3503 $dtts->setTimezone($tzo);
3504 $w = $dtts->format("w");
3505 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3506
3507 $ret = str_replace('__A__', $dayweek, $ret);
3508 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3509 }
3510
3511 return $ret;
3512}
3513
3514
3535function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3536{
3537 if ($timestamp === '') {
3538 return array();
3539 }
3540
3541 $datetimeobj = new DateTime();
3542 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3543 if ($forcetimezone) {
3544 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3545 }
3546 $arrayinfo = array(
3547 'year' => ((int) date_format($datetimeobj, 'Y')),
3548 'mon' => ((int) date_format($datetimeobj, 'm')),
3549 'mday' => ((int) date_format($datetimeobj, 'd')),
3550 'wday' => ((int) date_format($datetimeobj, 'w')),
3551 'yday' => ((int) date_format($datetimeobj, 'z')),
3552 'hours' => ((int) date_format($datetimeobj, 'H')),
3553 'minutes' => ((int) date_format($datetimeobj, 'i')),
3554 'seconds' => ((int) date_format($datetimeobj, 's')),
3555 '0' => $timestamp
3556 );
3557
3558 return $arrayinfo;
3559}
3560
3582function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3583{
3584 global $conf;
3585 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3586
3587 if ($gm === 'auto') {
3588 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3589 }
3590 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3591
3592 // Clean parameters
3593 if ($hour == -1 || empty($hour)) {
3594 $hour = 0;
3595 }
3596 if ($minute == -1 || empty($minute)) {
3597 $minute = 0;
3598 }
3599 if ($second == -1 || empty($second)) {
3600 $second = 0;
3601 }
3602
3603 // Check parameters
3604 if ($check) {
3605 if (!$month || !$day) {
3606 return '';
3607 }
3608 if ($day > 31) {
3609 return '';
3610 }
3611 if ($month > 12) {
3612 return '';
3613 }
3614 if ($hour < 0 || $hour > 24) {
3615 return '';
3616 }
3617 if ($minute < 0 || $minute > 60) {
3618 return '';
3619 }
3620 if ($second < 0 || $second > 60) {
3621 return '';
3622 }
3623 }
3624
3625 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3626 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3627 $localtz = new DateTimeZone($default_timezone);
3628 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3629 // We use dol_tz_string first because it is more reliable.
3630 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3631 try {
3632 $localtz = new DateTimeZone($default_timezone);
3633 } catch (Exception $e) {
3634 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3635 $default_timezone = @date_default_timezone_get();
3636 }
3637 } elseif (strrpos($gm, "tz,") !== false) {
3638 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3639 try {
3640 $localtz = new DateTimeZone($timezone);
3641 } catch (Exception $e) {
3642 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3643 }
3644 }
3645
3646 if (empty($localtz)) {
3647 $localtz = new DateTimeZone('UTC');
3648 }
3649 //var_dump($localtz);
3650 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3651 $dt = new DateTime('now', $localtz);
3652 $dt->setDate((int) $year, (int) $month, (int) $day);
3653 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3654 $date = $dt->getTimestamp(); // should include daylight saving time
3655 //var_dump($date);
3656 return $date;
3657}
3658
3659
3670function dol_now($mode = 'auto')
3671{
3672 $ret = 0;
3673
3674 if ($mode === 'auto') {
3675 $mode = 'gmt';
3676 }
3677
3678 if ($mode == 'gmt') {
3679 $ret = time(); // Time for now at greenwich.
3680 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3681 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3682 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3683 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3684 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3685 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3686 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3687 // $ret=dol_now('gmt')+($tzsecond*3600);
3688 //}
3689 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3690 // Time for now with user timezone added
3691 //print 'time: '.time();
3692 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3693 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3694 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3695 }
3696
3697 return $ret;
3698}
3699
3700
3709function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3710{
3711 global $conf, $langs;
3712 $level = 1024;
3713
3714 if (!empty($conf->dol_optimize_smallscreen)) {
3715 $shortunit = 1;
3716 }
3717
3718 // Set value text
3719 if (empty($shortvalue) || $size < ($level * 10)) {
3720 $ret = $size;
3721 $textunitshort = $langs->trans("b");
3722 $textunitlong = $langs->trans("Bytes");
3723 } else {
3724 $ret = round($size / $level, 0);
3725 $textunitshort = $langs->trans("Kb");
3726 $textunitlong = $langs->trans("KiloBytes");
3727 }
3728 // Use long or short text unit
3729 if (empty($shortunit)) {
3730 $ret .= ' '.$textunitlong;
3731 } else {
3732 $ret .= ' '.$textunitshort;
3733 }
3734
3735 return $ret;
3736}
3737
3748function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3749{
3750 global $langs;
3751
3752 if (empty($url)) {
3753 return '';
3754 }
3755
3756 $linkstart = '<a href="';
3757 if (!preg_match('/^http/i', $url)) {
3758 $linkstart .= 'http://';
3759 }
3760 $linkstart .= $url;
3761 $linkstart .= '"';
3762 if ($target) {
3763 $linkstart .= ' target="'.$target.'"';
3764 }
3765 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3766 $linkstart .= '>';
3767
3768 $link = '';
3769 if (!preg_match('/^http/i', $url)) {
3770 $link .= 'http://';
3771 }
3772 $link .= dol_trunc($url, $max);
3773
3774 $linkend = '</a>';
3775
3776 if ($morecss == 'float') { // deprecated
3777 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3778 } else {
3779 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3780 }
3781}
3782
3795function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3796{
3797 global $user, $langs, $hookmanager;
3798
3799 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3800 //$showinvalid = 1; $email = 'rrrrr';
3801
3802 $newemail = dol_escape_htmltag($email);
3803
3804 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3805 $withpicto = 0;
3806 }
3807
3808 if (empty($email)) {
3809 return '&nbsp;';
3810 }
3811
3812 if (!empty($addlink)) {
3813 $newemail = '<a class="paddingrightonly" style="text-overflow: ellipsis;" href="';
3814 if (!preg_match('/^mailto:/i', $email)) {
3815 $newemail .= 'mailto:';
3816 }
3817 $newemail .= $email;
3818 $newemail .= '">';
3819
3820 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3821
3822 $newemail .= dol_trunc($email, $max);
3823 $newemail .= '</a>';
3824 if ($showinvalid && !isValidEmail($email)) {
3825 $langs->load("errors");
3826 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3827 }
3828
3829 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3830 $type = 'AC_EMAIL';
3831 $linktoaddaction = '';
3832 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3833 $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>';
3834 }
3835 if ($linktoaddaction) {
3836 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3837 }
3838 }
3839 } else {
3840 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3841
3842 if ($showinvalid && !isValidEmail($email)) {
3843 $langs->load("errors");
3844 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3845 }
3846 }
3847
3848 //$rep = '<div class="nospan" style="margin-right: 10px">';
3849 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3850 //$rep .= '</div>';
3851 $rep = $newemail;
3852
3853 if ($hookmanager) {
3854 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3855
3856 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3857 if ($reshook > 0) {
3858 $rep = '';
3859 }
3860 $rep .= $hookmanager->resPrint;
3861 }
3862
3863 return $rep;
3864}
3865
3871function getArrayOfSocialNetworks()
3872{
3873 global $conf, $db;
3874
3875 $socialnetworks = array();
3876 // Enable caching of array
3877 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3878 $cachekey = 'socialnetworks_' . $conf->entity;
3879 $dataretrieved = dol_getcache($cachekey);
3880 if (!is_null($dataretrieved)) {
3881 $socialnetworks = $dataretrieved;
3882 } else {
3883 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3884 $sql .= " WHERE entity=".$conf->entity;
3885 $resql = $db->query($sql);
3886 if ($resql) {
3887 while ($obj = $db->fetch_object($resql)) {
3888 $socialnetworks[$obj->code] = array(
3889 'rowid' => $obj->rowid,
3890 'label' => $obj->label,
3891 'url' => $obj->url,
3892 'icon' => $obj->icon,
3893 'active' => $obj->active,
3894 );
3895 }
3896 }
3897 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3898 }
3899 return $socialnetworks;
3900}
3901
3912function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3913{
3914 global $user, $langs;
3915
3916 $htmllink = $value;
3917
3918 if (empty($value)) {
3919 return '&nbsp;';
3920 }
3921
3922 if (!empty($type)) {
3923 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3924 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3925 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3926 if ($type == 'skype') {
3927 $htmllink .= dol_escape_htmltag($value);
3928 $htmllink .= '&nbsp; <a href="skype:';
3929 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3930 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3931 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3932 $htmllink .= '</a><a href="skype:';
3933 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3934 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3935 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3936 $htmllink .= '</a>';
3937 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3938 $addlink = 'AC_SKYPE';
3939 $link = '';
3940 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
3941 $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>';
3942 }
3943 $htmllink .= ($link ? ' '.$link : '');
3944 }
3945 } else {
3946 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
3947 if (getDolGlobalString($networkconstname)) {
3948 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
3949 if (preg_match('/^https?:\/\//i', $link)) {
3950 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3951 } else {
3952 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3953 }
3954 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
3955 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3956 if ($tmpvirginurl) {
3957 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3958 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3959
3960 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3961 if ($tmpvirginurl3) {
3962 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3963 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3964 }
3965
3966 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3967 if ($tmpvirginurl2) {
3968 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3969 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3970 }
3971 }
3972 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3973 if (preg_match('/^https?:\/\//i', $link)) {
3974 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3975 } else {
3976 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3977 }
3978 } else {
3979 $htmllink .= dol_escape_htmltag($value);
3980 }
3981 }
3982 $htmllink .= '</div>';
3983 } else {
3984 $langs->load("errors");
3985 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3986 }
3987 return $htmllink;
3988}
3989
3999function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4000{
4001 global $mysoc;
4002
4003 if (empty($profID) || empty($profIDtype)) {
4004 return '';
4005 }
4006 if (empty($countrycode)) {
4007 $countrycode = $mysoc->country_code;
4008 }
4009 $newProfID = $profID;
4010 $id = substr($profIDtype, -1);
4011 $ret = '';
4012 if (strtoupper($countrycode) == 'FR') {
4013 // France
4014 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4015
4016 if ($id == 1 && dol_strlen($newProfID) == 9) {
4017 // SIREN (ex: 123 123 123)
4018 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4019 }
4020 if ($id == 2 && dol_strlen($newProfID) == 14) {
4021 // SIRET (ex: 123 123 123 12345)
4022 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4023 }
4024 if ($id == 3 && dol_strlen($newProfID) == 5) {
4025 // NAF/APE (ex: 69.20Z)
4026 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4027 }
4028 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4029 // TVA intracommunautaire (ex: FR12 123 123 123)
4030 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4031 }
4032 }
4033 if (!empty($addcpButton)) {
4034 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4035 } else {
4036 $ret = $newProfID;
4037 }
4038 return $ret;
4039}
4040
4056function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = '')
4057{
4058 global $conf, $user, $langs, $mysoc, $hookmanager;
4059
4060 // Clean phone parameter
4061 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4062 if (empty($phone)) {
4063 return '';
4064 }
4065 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4066 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4067 }
4068 if (empty($countrycode) && is_object($mysoc)) {
4069 $countrycode = $mysoc->country_code;
4070 }
4071
4072 // Short format for small screens
4073 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4074 $separ = '';
4075 }
4076
4077 $newphone = $phone;
4078 $newphonewa = $phone;
4079 if (strtoupper($countrycode) == "FR") {
4080 // France
4081 if (dol_strlen($phone) == 10) {
4082 $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);
4083 } elseif (dol_strlen($phone) == 7) {
4084 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4085 } elseif (dol_strlen($phone) == 9) {
4086 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4087 } elseif (dol_strlen($phone) == 11) {
4088 $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);
4089 } elseif (dol_strlen($phone) == 12) {
4090 $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);
4091 } elseif (dol_strlen($phone) == 13) {
4092 $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);
4093 }
4094 } elseif (strtoupper($countrycode) == "CA") {
4095 if (dol_strlen($phone) == 10) {
4096 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4097 }
4098 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4099 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4100 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4101 }
4102 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4103 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4104 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4105 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4106 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4107 }
4108 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4109 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4110 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4111 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4112 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4113 }
4114 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4115 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4116 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4117 }
4118 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4119 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4120 $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);
4121 }
4122 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4123 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4124 $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);
4125 }
4126 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4127 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4128 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4129 }
4130 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4131 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4132 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4133 }
4134 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4135 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4136 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4137 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4138 $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);
4139 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4140 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4141 }
4142 } elseif (strtoupper($countrycode) == "ML") {//Mali
4143 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4144 $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);
4145 }
4146 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4147 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4148 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4149 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4150 $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);
4151 }
4152 } elseif (strtoupper($countrycode) == "MU") {
4153 //Maurice
4154 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4155 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4156 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4157 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4158 }
4159 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4160 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4161 $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);
4162 }
4163 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4164 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4165 $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);
4166 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4167 $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);
4168 }
4169 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4170 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4171 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4172 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4173 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4174 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4175 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4176 }
4177 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4178 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4179 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4180 }
4181 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4182 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4183 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4184 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4185 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4186 }
4187 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4188 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4189 $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);
4190 }
4191 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4192 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4193 $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);
4194 }
4195 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4196 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4197 $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);
4198 }
4199 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4200 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4201 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4202 }
4203 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4204 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4205 $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);
4206 }
4207 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4208 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4209 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4210 }
4211 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4212 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4213 $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);
4214 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4215 $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);
4216 }
4217 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4218 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4219 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4220 }
4221 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4222 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4223 $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);
4224 }
4225 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4226 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4227 $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);
4228 }
4229 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4230 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4231 $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);
4232 }
4233 } elseif (strtoupper($countrycode) == "IT") {//Italie
4234 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4235 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4236 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4237 $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);
4238 }
4239 } elseif (strtoupper($countrycode) == "AU") {
4240 //Australie
4241 if (dol_strlen($phone) == 12) {
4242 //ex: +61_A_BCDE_FGHI
4243 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4244 }
4245 } elseif (strtoupper($countrycode) == "LU") {
4246 // Luxembourg
4247 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4248 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4249 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4250 $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);
4251 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4252 $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);
4253 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4254 $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);
4255 }
4256 } elseif (strtoupper($countrycode) == "PE") {
4257 // Peru
4258 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4259 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4260 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4261 $newphonewa = '+51'.$newphone;
4262 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4263 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4264 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4265 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4266 $newphonewa = $newphone;
4267 $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);
4268 }
4269 }
4270
4271 $newphoneastart = $newphoneaend = '';
4272 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4273 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
4274 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4275 $newphoneaend .= '</a>';
4276 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4277 if (empty($user->clicktodial_loaded)) {
4278 $user->fetch_clicktodial();
4279 }
4280
4281 // Define urlmask
4282 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4283 if (!empty($user->clicktodial_url)) {
4284 $urlmask = $user->clicktodial_url;
4285 }
4286
4287 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4288 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4289 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4290 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4291 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4292 // Those lines are for substitution
4293 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4294 '__PHONETO__' => urlencode($phone),
4295 '__LOGIN__' => $clicktodial_login,
4296 '__PASS__' => $clicktodial_password);
4297 $url = make_substitutions($url, $substitarray);
4298 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4299 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4300 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4301 $newphoneaend = '</a>';
4302 } else {
4303 // Old method
4304 $newphoneastart = '<a href="'.$url.'"';
4305 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4306 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4307 }
4308 $newphoneastart .= '>';
4309 $newphoneaend .= '</a>';
4310 }
4311 }
4312
4313 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4314 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4315 $type = 'AC_TEL';
4316 $addlinktoagenda = '';
4317 if ($addlink == 'AC_FAX') {
4318 $type = 'AC_FAX';
4319 }
4320 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4321 $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>';
4322 }
4323 if ($addlinktoagenda) {
4324 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4325 }
4326 }
4327 }
4328
4329 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4330 // Link to Whatsapp
4331 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4332 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4333 }
4334
4335 if (empty($titlealt)) {
4336 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4337 }
4338 $rep = '';
4339
4340 if ($hookmanager) {
4341 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4342 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4343 $rep .= $hookmanager->resPrint;
4344 }
4345 if (empty($reshook)) {
4346 $picto = '';
4347 if ($withpicto) {
4348 if ($withpicto == 'fax') {
4349 $picto = 'phoning_fax';
4350 } elseif ($withpicto == 'phone') {
4351 $picto = 'phoning';
4352 } elseif ($withpicto == 'mobile') {
4353 $picto = 'phoning_mobile';
4354 } else {
4355 $picto = '';
4356 }
4357 }
4358 if ($adddivfloat == 1) {
4359 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">';
4360 } elseif (empty($adddivfloat)) {
4361 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').' style="margin-right: 10px;">';
4362 }
4363
4364 $rep .= $newphoneastart;
4365 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4366 if ($separ != 'hidenum') {
4367 $rep .= ($withpicto ? ' ' : '').$newphone;
4368 }
4369 $rep .= $newphoneaend;
4370
4371 if ($adddivfloat == 1) {
4372 $rep .= '</div>';
4373 } elseif (empty($adddivfloat)) {
4374 $rep .= '</span>';
4375 }
4376 }
4377
4378 return $rep;
4379}
4380
4388function dol_print_ip($ip, $mode = 0)
4389{
4390 global $langs;
4391
4392 $ret = '';
4393
4394 if (empty($mode)) {
4395 $ret .= $ip;
4396 }
4397
4398 if ($mode != 2) {
4399 $countrycode = dolGetCountryCodeFromIp($ip);
4400 if ($countrycode) { // If success, countrycode is us, fr, ...
4401 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4402 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4403 } else {
4404 $ret .= ' ('.$countrycode.')';
4405 }
4406 } else {
4407 // Nothing
4408 }
4409 }
4410
4411 return $ret;
4412}
4413
4422function getUserRemoteIP()
4423{
4424 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4425 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4426 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4427 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4428 } else {
4429 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4430 }
4431 } else {
4432 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4433 }
4434 } else {
4435 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4436 }
4437 return $ip;
4438}
4439
4448function isHTTPS()
4449{
4450 $isSecure = false;
4451 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4452 $isSecure = true;
4453 } 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') {
4454 $isSecure = true;
4455 }
4456 return $isSecure;
4457}
4458
4465function dolGetCountryCodeFromIp($ip)
4466{
4467 global $conf;
4468
4469 $countrycode = '';
4470
4471 if (!empty($conf->geoipmaxmind->enabled)) {
4472 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4473 //$ip='24.24.24.24';
4474 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4475 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4476 $geoip = new DolGeoIP('country', $datafile);
4477 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4478 $countrycode = $geoip->getCountryCodeFromIP($ip);
4479 }
4480
4481 return $countrycode;
4482}
4483
4484
4491function dol_user_country()
4492{
4493 global $conf, $langs, $user;
4494
4495 //$ret=$user->xxx;
4496 $ret = '';
4497 if (!empty($conf->geoipmaxmind->enabled)) {
4498 $ip = getUserRemoteIP();
4499 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4500 //$ip='24.24.24.24';
4501 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4502 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4503 $geoip = new DolGeoIP('country', $datafile);
4504 $countrycode = $geoip->getCountryCodeFromIP($ip);
4505 $ret = $countrycode;
4506 }
4507 return $ret;
4508}
4509
4522function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4523{
4524 global $conf, $user, $langs, $hookmanager;
4525
4526 $out = '';
4527
4528 if ($address) {
4529 if ($hookmanager) {
4530 $parameters = array('element' => $element, 'id' => $id);
4531 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4532 $out .= $hookmanager->resPrint;
4533 }
4534 if (empty($reshook)) {
4535 if (empty($charfornl)) {
4536 $out .= nl2br($address);
4537 } else {
4538 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4539 }
4540
4541 // TODO Remove this block, we can add this using the hook now
4542 $showgmap = $showomap = 0;
4543 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4544 $showgmap = 1;
4545 }
4546 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4547 $showgmap = 1;
4548 }
4549 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4550 $showgmap = 1;
4551 }
4552 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4553 $showgmap = 1;
4554 }
4555 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4556 $showomap = 1;
4557 }
4558 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4559 $showomap = 1;
4560 }
4561 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4562 $showomap = 1;
4563 }
4564 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4565 $showomap = 1;
4566 }
4567 if ($showgmap) {
4568 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4569 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4570 }
4571 if ($showomap) {
4572 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4573 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4574 }
4575 }
4576 }
4577 if ($noprint) {
4578 return $out;
4579 } else {
4580 print $out;
4581 }
4582}
4583
4584
4594function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4595{
4596 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4597 return true;
4598 }
4599 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4600 return true;
4601 }
4602 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4603 return true;
4604 }
4605
4606 return false;
4607}
4608
4618function isValidMXRecord($domain)
4619{
4620 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4621 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4622 return 0;
4623 }
4624 if (function_exists('getmxrr')) {
4625 $mxhosts = array();
4626 $weight = array();
4627 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4628 if (count($mxhosts) > 1) {
4629 return 1;
4630 }
4631 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4632 return 1;
4633 }
4634
4635 return 0;
4636 }
4637 }
4638
4639 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4640 return -1;
4641}
4642
4650function isValidPhone($phone)
4651{
4652 return true;
4653}
4654
4655
4665function dolGetFirstLetters($s, $nbofchar = 1)
4666{
4667 $ret = '';
4668 $tmparray = explode(' ', $s);
4669 foreach ($tmparray as $tmps) {
4670 $ret .= dol_substr($tmps, 0, $nbofchar);
4671 }
4672
4673 return $ret;
4674}
4675
4676
4684function dol_strlen($string, $stringencoding = 'UTF-8')
4685{
4686 if (is_null($string)) {
4687 return 0;
4688 }
4689
4690 if (function_exists('mb_strlen')) {
4691 return mb_strlen($string, $stringencoding);
4692 } else {
4693 return strlen($string);
4694 }
4695}
4696
4707function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4708{
4709 global $langs;
4710
4711 if (empty($stringencoding)) {
4712 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4713 }
4714
4715 $ret = '';
4716 if (empty($trunconbytes)) {
4717 if (function_exists('mb_substr')) {
4718 $ret = mb_substr($string, $start, $length, $stringencoding);
4719 } else {
4720 $ret = substr($string, $start, $length);
4721 }
4722 } else {
4723 if (function_exists('mb_strcut')) {
4724 $ret = mb_strcut($string, $start, $length, $stringencoding);
4725 } else {
4726 $ret = substr($string, $start, $length);
4727 }
4728 }
4729 return $ret;
4730}
4731
4732
4746function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4747{
4748 global $conf;
4749
4750 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4751 return $string;
4752 }
4753
4754 if (empty($stringencoding)) {
4755 $stringencoding = 'UTF-8';
4756 }
4757 // reduce for small screen
4758 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4759 $size = round($size / 3);
4760 }
4761
4762 // We go always here
4763 if ($trunc == 'right') {
4764 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4765 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4766 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4767 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4768 } else {
4769 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4770 return $string;
4771 }
4772 } elseif ($trunc == 'middle') {
4773 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4774 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4775 $size1 = round($size / 2);
4776 $size2 = round($size / 2);
4777 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4778 } else {
4779 return $string;
4780 }
4781 } elseif ($trunc == 'left') {
4782 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4783 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4784 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4785 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4786 } else {
4787 return $string;
4788 }
4789 } elseif ($trunc == 'wrap') {
4790 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4791 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4792 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4793 } else {
4794 return $string;
4795 }
4796 } else {
4797 return 'BadParam3CallingDolTrunc';
4798 }
4799}
4800
4808function getPictoForType($key, $morecss = '')
4809{
4810 // Set array with type -> picto
4811 $type2picto = array(
4812 'varchar' => 'font',
4813 'text' => 'font',
4814 'html' => 'code',
4815 'int' => 'sort-numeric-down',
4816 'double' => 'sort-numeric-down',
4817 'price' => 'currency',
4818 'pricecy' => 'multicurrency',
4819 'password' => 'key',
4820 'boolean' => 'check-square',
4821 'date' => 'calendar',
4822 'datetime' => 'calendar',
4823 'phone' => 'phone',
4824 'mail' => 'email',
4825 'url' => 'url',
4826 'ip' => 'country',
4827 'select' => 'list',
4828 'sellist' => 'list',
4829 'radio' => 'check-circle',
4830 'checkbox' => 'list',
4831 'chkbxlst' => 'list',
4832 'link' => 'link',
4833 'icon' => "question",
4834 'point' => "country",
4835 'multipts' => 'country',
4836 'linestrg' => "country",
4837 'polygon' => "country",
4838 'separate' => 'minus'
4839 );
4840
4841 if (!empty($type2picto[$key])) {
4842 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4843 }
4844
4845 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4846}
4847
4848
4870function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4871{
4872 global $conf;
4873
4874 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4875 $url = DOL_URL_ROOT;
4876 $theme = isset($conf->theme) ? $conf->theme : null;
4877 $path = 'theme/'.$theme;
4878 if (empty($picto)) {
4879 $picto = 'generic';
4880 }
4881
4882 // Define fullpathpicto to use into src
4883 if ($pictoisfullpath) {
4884 // Clean parameters
4885 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4886 $picto .= '.png';
4887 }
4888 $fullpathpicto = $picto;
4889 $reg = array();
4890 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4891 $morecss .= ($morecss ? ' ' : '').$reg[1];
4892 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4893 }
4894 } else {
4895 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
4896 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4897 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4898
4899 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
4900 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
4901 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4902 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4903
4904 // Compatibility with old fontawesome versions
4905 if ($pictowithouttext == 'file-o') {
4906 $pictowithouttext = 'file';
4907 }
4908
4909 $pictowithouttextarray = explode('_', $pictowithouttext);
4910 $marginleftonlyshort = 0;
4911
4912 if (!empty($pictowithouttextarray[1])) {
4913 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4914 $fakey = 'fa-'.$pictowithouttextarray[0];
4915 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
4916 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4917 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4918 } else {
4919 $fakey = 'fa-'.$pictowithouttext;
4920 $faprefix = 'fas';
4921 $facolor = '';
4922 $fasize = '';
4923 }
4924
4925 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4926 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4927 $morestyle = '';
4928 $reg = array();
4929 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4930 $morecss .= ($morecss ? ' ' : '').$reg[1];
4931 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4932 }
4933 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4934 $morestyle = $reg[1];
4935 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4936 }
4937 $moreatt = trim($moreatt);
4938
4939 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4940 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4941 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4942 $enabledisablehtml .= $titlealt;
4943 }*/
4944 $enabledisablehtml .= '</span>';
4945
4946 return $enabledisablehtml;
4947 }
4948
4949 if (empty($srconly) && in_array($pictowithouttext, array(
4950 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4951 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
4952 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
4953 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4954 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4955 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
4956 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
4957 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
4958 'commercial', 'companies',
4959 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4960 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4961 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
4962 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4963 'hands-helping', 'help', 'holiday',
4964 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4965 'key', 'knowledgemanagement',
4966 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4967 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4968 'off', 'on', 'order',
4969 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4970 'stock', 'resize', 'service', 'stats',
4971 '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',
4972 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4973 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4974 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4975 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4976 'technic', 'ticket',
4977 'error', 'warning',
4978 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4979 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4980 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
4981 'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4982 'conferenceorbooth', 'eventorganization',
4983 'stamp', 'signature',
4984 'webportal'
4985 ))) {
4986 $fakey = $pictowithouttext;
4987 $facolor = '';
4988 $fasize = '';
4989 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
4990 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'))) {
4991 $fa = 'far';
4992 }
4993 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4994 $fa = 'fab';
4995 }
4996
4997 $arrayconvpictotofa = array(
4998 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
4999 'asset' => 'money-check-alt', 'autofill' => 'fill',
5000 'bank_account' => 'university',
5001 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5002 'bookcal' => 'calendar-check',
5003 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5004 'bom' => 'shapes',
5005 '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',
5006 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5007 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5008 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5009 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5010 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5011 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5012 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5013 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5014 'generic' => 'file', 'holiday' => 'umbrella-beach',
5015 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5016 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5017 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5018 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5019 'sign-out' => 'sign-out-alt',
5020 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
5021 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5022 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5023 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5024 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5025 'other' => 'square',
5026 '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',
5027 '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',
5028 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5029 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5030 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5031 'service' => 'concierge-bell',
5032 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5033 'status' => 'stop-circle',
5034 'stripe' => 'stripe-s', 'supplier' => 'building',
5035 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5036 'title_agenda' => 'calendar-alt',
5037 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5038 'jabber' => 'comment-o',
5039 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5040 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5041 'webportal' => 'door-open'
5042 );
5043 if ($conf->currency == 'EUR') {
5044 $arrayconvpictotofa['currency'] = 'euro-sign';
5045 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5046 } else {
5047 $arrayconvpictotofa['currency'] = 'dollar-sign';
5048 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5049 }
5050 if ($pictowithouttext == 'off') {
5051 $fakey = 'fa-square';
5052 $fasize = '1.3em';
5053 } elseif ($pictowithouttext == 'on') {
5054 $fakey = 'fa-check-square';
5055 $fasize = '1.3em';
5056 } elseif ($pictowithouttext == 'listlight') {
5057 $fakey = 'fa-download';
5058 $marginleftonlyshort = 1;
5059 } elseif ($pictowithouttext == 'printer') {
5060 $fakey = 'fa-print';
5061 $fasize = '1.2em';
5062 } elseif ($pictowithouttext == 'note') {
5063 $fakey = 'fa-sticky-note';
5064 $marginleftonlyshort = 1;
5065 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5066 $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');
5067 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5068 if (preg_match('/selected/', $pictowithouttext)) {
5069 $facolor = '#888';
5070 }
5071 $marginleftonlyshort = 1;
5072 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5073 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5074 } else {
5075 $fakey = 'fa-'.$pictowithouttext;
5076 }
5077
5078 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5079 $morecss .= ' em092';
5080 }
5081 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5082 $morecss .= ' em088';
5083 }
5084 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5085 $morecss .= ' em080';
5086 }
5087
5088 // Define $marginleftonlyshort
5089 $arrayconvpictotomarginleftonly = array(
5090 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5091 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
5092 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5093 );
5094 if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
5095 $marginleftonlyshort = 0;
5096 }
5097
5098 // Add CSS
5099 $arrayconvpictotomorcess = array(
5100 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5101 'bank_account' => 'infobox-bank_account',
5102 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5103 'bookcal' => 'infobox-action',
5104 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5105 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5106 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5107 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5108 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5109 'incoterm' => 'infobox-supplier_proposal',
5110 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5111 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5112 'order' => 'infobox-commande',
5113 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5114 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5115 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5116 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5117 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5118 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5119 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5120 'resource' => 'infobox-action',
5121 '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',
5122 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5123 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5124 'vat' => 'infobox-bank_account',
5125 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5126 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5127 );
5128 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5129 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5130 }
5131
5132 // Define $color
5133 $arrayconvpictotocolor = array(
5134 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5135 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5136 'dynamicprice' => '#a69944',
5137 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5138 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5139 'lock' => '#ddd', 'lot' => '#a69944',
5140 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5141 'other' => '#ddd', 'world' => '#986c6a',
5142 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5143 //'shipment'=>'#a69944',
5144 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5145 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5146 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5147 'website' => '#304', 'workstation' => '#a69944'
5148 );
5149 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5150 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5151 }
5152
5153 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5154 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5155 $morestyle = '';
5156 $reg = array();
5157 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5158 $morecss .= ($morecss ? ' ' : '').$reg[1];
5159 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5160 }
5161 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5162 $morestyle = $reg[1];
5163 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5164 }
5165 $moreatt = trim($moreatt);
5166
5167 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5168 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5169 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5170 $enabledisablehtml .= $titlealt;
5171 }*/
5172 $enabledisablehtml .= '</span>';
5173
5174 return $enabledisablehtml;
5175 }
5176
5177 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5178 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5179 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5180 $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
5181 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5182 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5183 }
5184
5185 // If we ask an image into $url/$mymodule/img (instead of default path)
5186 $regs = array();
5187 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5188 $picto = $regs[1];
5189 $path = $regs[2]; // $path is $mymodule
5190 }
5191
5192 // Clean parameters
5193 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5194 $picto .= '.png';
5195 }
5196 // If alt path are defined, define url where img file is, according to physical path
5197 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5198 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5199 if ($type == 'main') {
5200 continue;
5201 }
5202 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5203 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5204 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5205 break;
5206 }
5207 }
5208
5209 // $url is '' or '/custom', $path is current theme or
5210 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5211 }
5212
5213 if ($srconly) {
5214 return $fullpathpicto;
5215 }
5216
5217 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5218 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
5219}
5220
5234function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5235{
5236 if (strpos($picto, '^') === 0) {
5237 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5238 } else {
5239 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5240 }
5241}
5242
5254function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5255{
5256 global $conf;
5257
5258 if (is_numeric($picto)) {
5259 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5260 //$picto = $leveltopicto[$picto];
5261 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5262 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5263 $picto .= '.png';
5264 }
5265
5266 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5267
5268 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5269}
5270
5282function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5283{
5284 global $conf;
5285
5286 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5287 $picto .= '.png';
5288 }
5289
5290 if ($pictoisfullpath) {
5291 $path = $picto;
5292 } else {
5293 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5294
5295 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5296 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5297
5298 if (file_exists($themepath)) {
5299 $path = $themepath;
5300 }
5301 }
5302 }
5303
5304 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5305}
5306
5320function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5321{
5322 global $langs;
5323
5324 if (empty($titlealt) || $titlealt == 'default') {
5325 if ($numaction == '-1' || $numaction == 'ST_NO') {
5326 $numaction = -1;
5327 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5328 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5329 $numaction = 0;
5330 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5331 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5332 $numaction = 1;
5333 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5334 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5335 $numaction = 2;
5336 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5337 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5338 $numaction = 3;
5339 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5340 } else {
5341 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5342 $numaction = 0;
5343 }
5344 }
5345 if (!is_numeric($numaction)) {
5346 $numaction = 0;
5347 }
5348
5349 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5350}
5351
5359function img_pdf($titlealt = 'default', $size = 3)
5360{
5361 global $langs;
5362
5363 if ($titlealt == 'default') {
5364 $titlealt = $langs->trans('Show');
5365 }
5366
5367 return img_picto($titlealt, 'pdf'.$size.'.png');
5368}
5369
5377function img_edit_add($titlealt = 'default', $other = '')
5378{
5379 global $langs;
5380
5381 if ($titlealt == 'default') {
5382 $titlealt = $langs->trans('Add');
5383 }
5384
5385 return img_picto($titlealt, 'edit_add.png', $other);
5386}
5394function img_edit_remove($titlealt = 'default', $other = '')
5395{
5396 global $langs;
5397
5398 if ($titlealt == 'default') {
5399 $titlealt = $langs->trans('Remove');
5400 }
5401
5402 return img_picto($titlealt, 'edit_remove.png', $other);
5403}
5404
5413function img_edit($titlealt = 'default', $float = 0, $other = '')
5414{
5415 global $langs;
5416
5417 if ($titlealt == 'default') {
5418 $titlealt = $langs->trans('Modify');
5419 }
5420
5421 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5422}
5423
5432function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5433{
5434 global $langs;
5435
5436 if ($titlealt == 'default') {
5437 $titlealt = $langs->trans('View');
5438 }
5439
5440 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5441
5442 return img_picto($titlealt, 'eye', $moreatt);
5443}
5444
5453function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5454{
5455 global $langs;
5456
5457 if ($titlealt == 'default') {
5458 $titlealt = $langs->trans('Delete');
5459 }
5460
5461 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5462}
5463
5471function img_printer($titlealt = "default", $other = '')
5472{
5473 global $langs;
5474 if ($titlealt == "default") {
5475 $titlealt = $langs->trans("Print");
5476 }
5477 return img_picto($titlealt, 'printer.png', $other);
5478}
5479
5487function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5488{
5489 global $langs;
5490
5491 if ($titlealt == 'default') {
5492 $titlealt = $langs->trans('Split');
5493 }
5494
5495 return img_picto($titlealt, 'split.png', $other);
5496}
5497
5505function img_help($usehelpcursor = 1, $usealttitle = 1)
5506{
5507 global $langs;
5508
5509 if ($usealttitle) {
5510 if (is_string($usealttitle)) {
5511 $usealttitle = dol_escape_htmltag($usealttitle);
5512 } else {
5513 $usealttitle = $langs->trans('Info');
5514 }
5515 }
5516
5517 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5518}
5519
5526function img_info($titlealt = 'default')
5527{
5528 global $langs;
5529
5530 if ($titlealt == 'default') {
5531 $titlealt = $langs->trans('Informations');
5532 }
5533
5534 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5535}
5536
5545function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5546{
5547 global $langs;
5548
5549 if ($titlealt == 'default') {
5550 $titlealt = $langs->trans('Warning');
5551 }
5552
5553 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5554 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5555}
5556
5563function img_error($titlealt = 'default')
5564{
5565 global $langs;
5566
5567 if ($titlealt == 'default') {
5568 $titlealt = $langs->trans('Error');
5569 }
5570
5571 return img_picto($titlealt, 'error.png');
5572}
5573
5581function img_next($titlealt = 'default', $moreatt = '')
5582{
5583 global $langs;
5584
5585 if ($titlealt == 'default') {
5586 $titlealt = $langs->trans('Next');
5587 }
5588
5589 //return img_picto($titlealt, 'next.png', $moreatt);
5590 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5591}
5592
5600function img_previous($titlealt = 'default', $moreatt = '')
5601{
5602 global $langs;
5603
5604 if ($titlealt == 'default') {
5605 $titlealt = $langs->trans('Previous');
5606 }
5607
5608 //return img_picto($titlealt, 'previous.png', $moreatt);
5609 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5610}
5611
5620function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5621{
5622 global $langs;
5623
5624 if ($titlealt == 'default') {
5625 $titlealt = $langs->trans('Down');
5626 }
5627
5628 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5629}
5630
5639function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5640{
5641 global $langs;
5642
5643 if ($titlealt == 'default') {
5644 $titlealt = $langs->trans('Up');
5645 }
5646
5647 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5648}
5649
5658function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5659{
5660 global $langs;
5661
5662 if ($titlealt == 'default') {
5663 $titlealt = $langs->trans('Left');
5664 }
5665
5666 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5667}
5668
5677function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5678{
5679 global $langs;
5680
5681 if ($titlealt == 'default') {
5682 $titlealt = $langs->trans('Right');
5683 }
5684
5685 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5686}
5687
5695function img_allow($allow, $titlealt = 'default')
5696{
5697 global $langs;
5698
5699 if ($titlealt == 'default') {
5700 $titlealt = $langs->trans('Active');
5701 }
5702
5703 if ($allow == 1) {
5704 return img_picto($titlealt, 'tick.png');
5705 }
5706
5707 return '-';
5708}
5709
5717function img_credit_card($brand, $morecss = null)
5718{
5719 if (is_null($morecss)) {
5720 $morecss = 'fa-2x';
5721 }
5722
5723 if ($brand == 'visa' || $brand == 'Visa') {
5724 $brand = 'cc-visa';
5725 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5726 $brand = 'cc-mastercard';
5727 } elseif ($brand == 'amex' || $brand == 'American Express') {
5728 $brand = 'cc-amex';
5729 } elseif ($brand == 'discover' || $brand == 'Discover') {
5730 $brand = 'cc-discover';
5731 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5732 $brand = 'cc-jcb';
5733 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5734 $brand = 'cc-diners-club';
5735 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5736 $brand = 'credit-card';
5737 }
5738
5739 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5740}
5741
5750function img_mime($file, $titlealt = '', $morecss = '')
5751{
5752 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5753
5754 $mimetype = dol_mimetype($file, '', 1);
5755 $mimeimg = dol_mimetype($file, '', 2);
5756 $mimefa = dol_mimetype($file, '', 4);
5757
5758 if (empty($titlealt)) {
5759 $titlealt = 'Mime type: '.$mimetype;
5760 }
5761
5762 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5763 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5764}
5765
5766
5774function img_search($titlealt = 'default', $other = '')
5775{
5776 global $langs;
5777
5778 if ($titlealt == 'default') {
5779 $titlealt = $langs->trans('Search');
5780 }
5781
5782 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5783
5784 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5785 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5786
5787 return $input;
5788}
5789
5797function img_searchclear($titlealt = 'default', $other = '')
5798{
5799 global $langs;
5800
5801 if ($titlealt == 'default') {
5802 $titlealt = $langs->trans('Search');
5803 }
5804
5805 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5806
5807 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5808 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5809
5810 return $input;
5811}
5812
5825function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5826{
5827 global $conf, $langs;
5828
5829 if ($infoonimgalt) {
5830 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5831 } else {
5832 if (empty($conf->use_javascript_ajax)) {
5833 $textfordropdown = '';
5834 }
5835
5836 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5837 $fa = 'info-circle';
5838 if ($picto == 'warning') {
5839 $fa = 'exclamation-triangle';
5840 }
5841 $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> ';
5842 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5843 $result .= ($nodiv ? '' : '</div>');
5844
5845 if ($textfordropdown) {
5846 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5847 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5848 jQuery(document).ready(function() {
5849 jQuery(".'.$class.'text").click(function() {
5850 console.log("toggle text");
5851 jQuery(".'.$class.'").toggle();
5852 });
5853 });
5854 </script>';
5855
5856 $result = $tmpresult.$result;
5857 }
5858 }
5859
5860 return $result;
5861}
5862
5863
5875function dol_print_error($db = null, $error = '', $errors = null)
5876{
5877 global $conf, $langs, $user, $argv;
5878 global $dolibarr_main_prod;
5879
5880 $out = '';
5881 $syslog = '';
5882
5883 // If error occurs before the $lang object was loaded
5884 if (!$langs) {
5885 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5886 $langs = new Translate('', $conf);
5887 $langs->load("main");
5888 }
5889
5890 // Load translation files required by the error messages
5891 $langs->loadLangs(array('main', 'errors'));
5892
5893 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5894 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5895 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5896 $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";
5897 }
5898 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5899
5900 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5901 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5902 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5903 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5904 }
5905 if ($user instanceof User) {
5906 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
5907 }
5908 if (function_exists("phpversion")) {
5909 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5910 }
5911 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5912 if (function_exists("php_uname")) {
5913 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5914 }
5915 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5916 $out .= "<br>\n";
5917 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5918 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5919 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5920 $out .= "<br>\n";
5921 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5922 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5923 } else { // Mode CLI
5924 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5925 $syslog .= "pid=".dol_getmypid();
5926 }
5927
5928 if (!empty($conf->modules)) {
5929 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
5930 }
5931
5932 if (is_object($db)) {
5933 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5934 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5935 $lastqueryerror = $db->lastqueryerror();
5936 if (!utf8_check($lastqueryerror)) {
5937 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5938 }
5939 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5940 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5941 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5942 $out .= "<br>\n";
5943 } else { // Mode CLI
5944 // No dol_escape_htmltag for output, we are in CLI mode
5945 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5946 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5947 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5948 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5949 }
5950 $syslog .= ", sql=".$db->lastquery();
5951 $syslog .= ", db_error=".$db->lasterror();
5952 }
5953
5954 if ($error || $errors) {
5955 // Merge all into $errors array
5956 if (is_array($error) && is_array($errors)) {
5957 $errors = array_merge($error, $errors);
5958 } elseif (is_array($error)) { // deprecated, use second parameters
5959 $errors = $error;
5960 } elseif (is_array($errors) && !empty($error)) {
5961 $errors = array_merge(array($error), $errors);
5962 } elseif (!empty($error)) {
5963 $errors = array_merge(array($error), array($errors));
5964 }
5965
5966 $langs->load("errors");
5967
5968 foreach ($errors as $msg) {
5969 if (empty($msg)) {
5970 continue;
5971 }
5972 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5973 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5974 } else { // Mode CLI
5975 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5976 }
5977 $syslog .= ", msg=".$msg;
5978 }
5979 }
5980 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5981 xdebug_print_function_stack();
5982 $out .= '<b>XDebug information:</b>'."<br>\n";
5983 $out .= 'File: '.xdebug_call_file()."<br>\n";
5984 $out .= 'Line: '.xdebug_call_line()."<br>\n";
5985 $out .= 'Function: '.xdebug_call_function()."<br>\n";
5986 $out .= "<br>\n";
5987 }
5988
5989 // Return a http header with error code if possible
5990 if (!headers_sent()) {
5991 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5992 top_httphead();
5993 }
5994 //http_response_code(500); // If we use 500, message is not output with some command line tools
5995 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
5996 }
5997
5998 if (empty($dolibarr_main_prod)) {
5999 print $out;
6000 } else {
6001 if (empty($langs->defaultlang)) {
6002 $langs->setDefaultLang();
6003 }
6004 $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.
6005 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6006 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";
6007 print $langs->trans("DolibarrHasDetectedError").'. ';
6008 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6009 if (!defined("MAIN_CORE_ERROR")) {
6010 define("MAIN_CORE_ERROR", 1);
6011 }
6012 }
6013
6014 dol_syslog("Error ".$syslog, LOG_ERR);
6015}
6016
6027function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6028{
6029 global $langs;
6030
6031 if (empty($email)) {
6032 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6033 }
6034
6035 $langs->load("errors");
6036 $now = dol_now();
6037
6038 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6039 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6040 if ($errormessage) {
6041 print '<br><br>'.$errormessage;
6042 }
6043 if (is_array($errormessages) && count($errormessages)) {
6044 foreach ($errormessages as $mesgtoshow) {
6045 print '<br><br>'.$mesgtoshow;
6046 }
6047 }
6048 print '</div></div>';
6049}
6050
6067function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6068{
6069 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6070}
6071
6090function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6091{
6092 global $langs, $form;
6093 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6094
6095 if ($moreattrib == 'class="right"') {
6096 $prefix .= 'right '; // For backward compatibility
6097 }
6098
6099 $sortorder = strtoupper($sortorder);
6100 $out = '';
6101 $sortimg = '';
6102
6103 $tag = 'th';
6104 if ($thead == 2) {
6105 $tag = 'div';
6106 }
6107
6108 $tmpsortfield = explode(',', $sortfield);
6109 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6110 $tmpfield = explode(',', $field);
6111 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6112
6113 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6114 $prefix = 'wrapcolumntitle '.$prefix;
6115 }
6116
6117 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6118 // If field is used as sort criteria we use a specific css class liste_titre_sel
6119 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6120 $liste_titre = 'liste_titre';
6121 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6122 $liste_titre = 'liste_titre_sel';
6123 }
6124
6125 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6126 //$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)).'"' : '');
6127 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6128 $tagstart .= '>';
6129
6130 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6131 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6132 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6133 $options = preg_replace('/&+/i', '&', $options);
6134 if (!preg_match('/^&/', $options)) {
6135 $options = '&'.$options;
6136 }
6137
6138 $sortordertouseinlink = '';
6139 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6140 if (preg_match('/^DESC/i', $sortorder)) {
6141 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6142 } else { // We reverse the var $sortordertouseinlink
6143 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6144 }
6145 } else { // We are on field that is the first current sorting criteria
6146 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6147 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6148 } else {
6149 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6150 }
6151 }
6152 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6153 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6154 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6155 $out .= '>';
6156 }
6157 if ($tooltip) {
6158 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6159 if (preg_match('/:\w+$/', $tooltip)) {
6160 $tmptooltip = explode(':', $tooltip);
6161 } else {
6162 $tmptooltip = array($tooltip);
6163 }
6164 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6165 } else {
6166 $out .= $langs->trans($name);
6167 }
6168
6169 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6170 $out .= '</a>';
6171 }
6172
6173 if (empty($thead) && $field) { // If this is a sort field
6174 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6175 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6176 $options = preg_replace('/&+/i', '&', $options);
6177 if (!preg_match('/^&/', $options)) {
6178 $options = '&'.$options;
6179 }
6180
6181 if (!$sortorder || ($field1 != $sortfield1)) {
6182 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6183 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6184 } else {
6185 if (preg_match('/^DESC/', $sortorder)) {
6186 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6187 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6188 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6189 }
6190 if (preg_match('/^ASC/', $sortorder)) {
6191 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6192 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6193 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6194 }
6195 }
6196 }
6197
6198 $tagend = '</'.$tag.'>';
6199
6200 $out = $tagstart.$sortimg.$out.$tagend;
6201
6202 return $out;
6203}
6204
6213function print_titre($title)
6214{
6215 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6216
6217 print '<div class="titre">'.$title.'</div>';
6218}
6219
6231function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6232{
6233 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6234}
6235
6249function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6250{
6251 $return = '';
6252
6253 if ($picto == 'setup') {
6254 $picto = 'generic';
6255 }
6256
6257 $return .= "\n";
6258 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
6259 $return .= '<tr class="titre">';
6260 if ($picto) {
6261 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6262 }
6263 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6264 $return .= '<div class="titre inline-block">';
6265 $return .= $title; // $title is already HTML sanitized content
6266 $return .= '</div>';
6267 $return .= '</td>';
6268 if (dol_strlen($morehtmlcenter)) {
6269 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6270 }
6271 if (dol_strlen($morehtmlright)) {
6272 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6273 }
6274 $return .= '</tr></table>'."\n";
6275
6276 return $return;
6277}
6278
6302function 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 = '')
6303{
6304 global $conf;
6305
6306 $savlimit = $limit;
6307 $savtotalnboflines = $totalnboflines;
6308 if (is_numeric($totalnboflines)) {
6309 $totalnboflines = abs($totalnboflines);
6310 }
6311
6312 $page = (int) $page;
6313
6314 if ($picto == 'setup') {
6315 $picto = 'title_setup.png';
6316 }
6317 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6318 $picto = 'title.gif';
6319 }
6320 if ($limit < 0) {
6321 $limit = $conf->liste_limit;
6322 }
6323
6324 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6325 $nextpage = 1;
6326 } else {
6327 $nextpage = 0;
6328 }
6329 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
6330
6331 print "\n";
6332 print "<!-- Begin title -->\n";
6333 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
6334
6335 // Left
6336
6337 if ($picto && $title) {
6338 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
6339 }
6340
6341 print '<td class="nobordernopadding valignmiddle col-title">';
6342 print '<div class="titre inline-block">';
6343 print $title; // $title may contains HTML
6344 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
6345 print '<span class="opacitymedium colorblack paddingleft totalnboflines">('.$totalnboflines.')</span>';
6346 }
6347 print '</div></td>';
6348
6349 // Center
6350 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6351 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6352 }
6353
6354 // Right
6355 print '<td class="nobordernopadding valignmiddle right col-right">';
6356 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6357 if ($sortfield) {
6358 $options .= "&sortfield=".urlencode($sortfield);
6359 }
6360 if ($sortorder) {
6361 $options .= "&sortorder=".urlencode($sortorder);
6362 }
6363 // Show navigation bar
6364 $pagelist = '';
6365 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6366 if ($totalnboflines) { // If we know total nb of lines
6367 // Define nb of extra page links before and after selected page + ... + first or last
6368 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6369
6370 if ($limit > 0) {
6371 $nbpages = ceil($totalnboflines / $limit);
6372 } else {
6373 $nbpages = 1;
6374 }
6375 $cpt = ($page - $maxnbofpage);
6376 if ($cpt < 0) {
6377 $cpt = 0;
6378 }
6379
6380 if ($cpt >= 1) {
6381 if (empty($pagenavastextinput)) {
6382 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6383 if ($cpt > 2) {
6384 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6385 } elseif ($cpt == 2) {
6386 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6387 }
6388 }
6389 }
6390
6391 do {
6392 if ($pagenavastextinput) {
6393 if ($cpt == $page) {
6394 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6395 $pagelist .= '/';
6396 }
6397 } else {
6398 if ($cpt == $page) {
6399 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6400 } else {
6401 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6402 }
6403 }
6404 $cpt++;
6405 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6406
6407 if (empty($pagenavastextinput)) {
6408 if ($cpt < $nbpages) {
6409 if ($cpt < $nbpages - 2) {
6410 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6411 } elseif ($cpt == $nbpages - 2) {
6412 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6413 }
6414 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6415 }
6416 } else {
6417 //var_dump($page.' '.$cpt.' '.$nbpages);
6418 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6419 }
6420 } else {
6421 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6422 }
6423 }
6424
6425 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6426 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
6427 }
6428
6429 // js to autoselect page field on focus
6430 if ($pagenavastextinput) {
6431 print ajax_autoselect('.pageplusone');
6432 }
6433
6434 print '</td>';
6435 print '</tr>';
6436
6437 print '</table>'."\n";
6438
6439 // Center
6440 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6441 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6442 }
6443
6444 print "<!-- End title -->\n\n";
6445}
6446
6463function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
6464{
6465 global $conf, $langs;
6466
6467 print '<div class="pagination"><ul>';
6468 if ($beforearrows) {
6469 print '<li class="paginationbeforearrows">';
6470 print $beforearrows;
6471 print '</li>';
6472 }
6473
6474 if (empty($hidenavigation)) {
6475 if ((int) $limit > 0 && empty($hideselectlimit)) {
6476 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6477 $pagesizechoices .= ',5000:5000,10000:10000';
6478 //$pagesizechoices .= ',20000:20000'; // Memory trouble on browsers
6479 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6480 //$pagesizechoices .= ',2:2';
6481 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6482 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6483 }
6484
6485 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6486 print '<li class="pagination">';
6487 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.'">';
6488 print '<datalist id="limitlist">';
6489 } else {
6490 print '<li class="paginationcombolimit valignmiddle">';
6491 print '<select id="limit" class="flat selectlimit nopadding maxwidth75 center" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6492 }
6493 $tmpchoice = explode(',', $pagesizechoices);
6494 $tmpkey = $limit.':'.$limit;
6495 if (!in_array($tmpkey, $tmpchoice)) {
6496 $tmpchoice[] = $tmpkey;
6497 }
6498 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6499 if (!in_array($tmpkey, $tmpchoice)) {
6500 $tmpchoice[] = $tmpkey;
6501 }
6502 asort($tmpchoice, SORT_NUMERIC);
6503 foreach ($tmpchoice as $val) {
6504 $selected = '';
6505 $tmp = explode(':', $val);
6506 $key = $tmp[0];
6507 $val = $tmp[1];
6508 if ($key != '' && $val != '') {
6509 if ((int) $key == (int) $limit) {
6510 $selected = ' selected="selected"';
6511 }
6512 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6513 }
6514 }
6515 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6516 print '</datalist>';
6517 } else {
6518 print '</select>';
6519 print ajax_combobox("limit", array(), 0, 0, 'resolve', -1, 'limit');
6520 //print ajax_combobox("limit");
6521 }
6522
6523 if ($conf->use_javascript_ajax) {
6524 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6525 <script>
6526 jQuery(document).ready(function () {
6527 jQuery(".selectlimit").change(function() {
6528 console.log("Change limit. Send submit");
6529 $(this).parents(\'form:first\').submit();
6530 });
6531 });
6532 </script>
6533 ';
6534 }
6535 print '</li>';
6536 }
6537 if ($page > 0) {
6538 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>';
6539 }
6540 if ($betweenarrows) {
6541 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6542 print $betweenarrows;
6543 print '<!--</div>-->';
6544 }
6545 if ($nextpage > 0) {
6546 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>';
6547 }
6548 if ($afterarrows) {
6549 print '<li class="paginationafterarrows">';
6550 print $afterarrows;
6551 print '</li>';
6552 }
6553 }
6554 print '</ul></div>'."\n";
6555}
6556
6557
6569function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6570{
6571 $morelabel = '';
6572
6573 if (preg_match('/%/', $rate)) {
6574 $rate = str_replace('%', '', $rate);
6575 $addpercent = true;
6576 }
6577 $reg = array();
6578 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6579 $morelabel = ' ('.$reg[1].')';
6580 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6581 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6582 }
6583 if (preg_match('/\*/', $rate)) {
6584 $rate = str_replace('*', '', $rate);
6585 $info_bits |= 1;
6586 }
6587
6588 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6589 if (!preg_match('/\//', $rate)) {
6590 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6591 } else {
6592 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6593 $ret = $rate.($addpercent ? '%' : '');
6594 }
6595 if (($info_bits & 1) && $usestarfornpr >= 0) {
6596 $ret .= ' *';
6597 }
6598 $ret .= $morelabel;
6599 return $ret;
6600}
6601
6602
6618function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6619{
6620 global $langs, $conf;
6621
6622 // Clean parameters
6623 if (empty($amount)) {
6624 $amount = 0; // To have a numeric value if amount not defined or = ''
6625 }
6626 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6627 if ($rounding == -1) {
6628 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6629 }
6630 $nbdecimal = $rounding;
6631
6632 if ($outlangs === 'none') {
6633 // Use international separators
6634 $dec = '.';
6635 $thousand = '';
6636 } else {
6637 // Output separators by default (french)
6638 $dec = ',';
6639 $thousand = ' ';
6640
6641 // If $outlangs not forced, we use use language
6642 if (!($outlangs instanceof Translate)) {
6643 $outlangs = $langs;
6644 }
6645
6646 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6647 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6648 }
6649 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6650 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6651 }
6652 if ($thousand == 'None') {
6653 $thousand = '';
6654 } elseif ($thousand == 'Space') {
6655 $thousand = ' ';
6656 }
6657 }
6658 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6659
6660 //print "amount=".$amount."-";
6661 $amount = str_replace(',', '.', $amount); // should be useless
6662 //print $amount."-";
6663 $data = explode('.', $amount);
6664 $decpart = isset($data[1]) ? $data[1] : '';
6665 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6666 //print "decpart=".$decpart."<br>";
6667 $end = '';
6668
6669 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6670 if (dol_strlen($decpart) > $nbdecimal) {
6671 $nbdecimal = dol_strlen($decpart);
6672 }
6673
6674 // If nbdecimal is higher than max to show
6675 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6676 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6677 $nbdecimal = $nbdecimalmaxshown;
6678 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6679 // If output is truncated, we show ...
6680 $end = '...';
6681 }
6682 }
6683
6684 // If force rounding
6685 if ((string) $forcerounding != '-1') {
6686 if ($forcerounding === 'MU') {
6687 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6688 } elseif ($forcerounding === 'MT') {
6689 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
6690 } elseif ($forcerounding >= 0) {
6691 $nbdecimal = $forcerounding;
6692 }
6693 }
6694
6695 // Format number
6696 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6697 if ($form) {
6698 $output = preg_replace('/\s/', '&nbsp;', $output);
6699 $output = preg_replace('/\'/', '&#039;', $output);
6700 }
6701 // Add symbol of currency if requested
6702 $cursymbolbefore = $cursymbolafter = '';
6703 if ($currency_code && is_object($outlangs)) {
6704 if ($currency_code == 'auto') {
6705 $currency_code = $conf->currency;
6706 }
6707
6708 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6709 $listoflanguagesbefore = array('nl_NL');
6710 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6711 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6712 } else {
6713 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6714 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6715 }
6716 }
6717 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6718
6719 return $output;
6720}
6721
6746function price2num($amount, $rounding = '', $option = 0)
6747{
6748 global $langs, $conf;
6749
6750 // Clean parameters
6751 if (is_null($amount)) {
6752 $amount = '';
6753 }
6754
6755 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6756 // Numbers must be '1234.56'
6757 // Decimal delimiter for PHP and database SQL requests must be '.'
6758 $dec = ',';
6759 $thousand = ' ';
6760 if (is_null($langs)) { // $langs is not defined, we use english values.
6761 $dec = '.';
6762 $thousand = ',';
6763 } else {
6764 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6765 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6766 }
6767 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6768 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6769 }
6770 }
6771 if ($thousand == 'None') {
6772 $thousand = '';
6773 } elseif ($thousand == 'Space') {
6774 $thousand = ' ';
6775 }
6776 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6777
6778 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6779 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6780 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6781 if (!is_numeric($amount)) {
6782 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6783 }
6784
6785 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
6786 $amount = str_replace($thousand, '', $amount);
6787 }
6788
6789 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6790 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6791 // So if number was already a good number, it is converted into local Dolibarr setup.
6792 if (is_numeric($amount)) {
6793 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6794 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6795 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6796 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6797 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6798 }
6799 //print "QQ".$amount."<br>\n";
6800
6801 // Now make replace (the main goal of function)
6802 if ($thousand != ',' && $thousand != '.') {
6803 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6804 }
6805
6806 $amount = str_replace(' ', '', $amount); // To avoid spaces
6807 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6808 $amount = str_replace($dec, '.', $amount);
6809
6810 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6811 }
6812 //print ' XX'.$amount.' '.$rounding;
6813
6814 // Now, $amount is a real PHP float number. We make a rounding if required.
6815 if ($rounding) {
6816 $nbofdectoround = '';
6817 if ($rounding == 'MU') {
6818 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6819 } elseif ($rounding == 'MT') {
6820 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6821 } elseif ($rounding == 'MS') {
6822 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6823 } elseif ($rounding == 'CU') {
6824 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6825 } elseif ($rounding == 'CT') {
6826 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6827 } elseif (is_numeric($rounding)) {
6828 $nbofdectoround = (int) $rounding;
6829 }
6830
6831 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6832 if (dol_strlen($nbofdectoround)) {
6833 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6834 } else {
6835 return 'ErrorBadParameterProvidedToFunction';
6836 }
6837 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6838
6839 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6840 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6841 if (is_numeric($amount)) {
6842 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6843 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6844 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6845 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6846 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6847 }
6848 //print "TT".$amount.'<br>';
6849
6850 // Always make replace because each math function (like round) replace
6851 // with local values and we want a number that has a SQL string format x.y
6852 if ($thousand != ',' && $thousand != '.') {
6853 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6854 }
6855
6856 $amount = str_replace(' ', '', $amount); // To avoid spaces
6857 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6858 $amount = str_replace($dec, '.', $amount);
6859
6860 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6861 }
6862
6863 return $amount;
6864}
6865
6878function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6879{
6880 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6881
6882 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6883 $dimension *= 1000000;
6884 $unit -= 6;
6885 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6886 $dimension *= 1000;
6887 $unit -= 3;
6888 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6889 $dimension /= 1000000;
6890 $unit += 6;
6891 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6892 $dimension /= 1000;
6893 $unit += 3;
6894 }
6895 // Special case when we want output unit into pound or ounce
6896 /* TODO
6897 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6898 {
6899 $dimension = // convert dimension from standard unit into ounce or pound
6900 $unit = $forceunitoutput;
6901 }
6902 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6903 {
6904 $dimension = // convert dimension from standard unit into ounce or pound
6905 $unit = $forceunitoutput;
6906 }*/
6907
6908 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6909 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
6910 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6911
6912 return $ret;
6913}
6914
6915
6928function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
6929{
6930 global $db, $conf, $mysoc;
6931
6932 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6933 $thirdparty_seller = $mysoc;
6934 }
6935
6936 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);
6937
6938 $vatratecleaned = $vatrate;
6939 $reg = array();
6940 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
6941 $vatratecleaned = trim($reg[1]);
6942 $vatratecode = $reg[2];
6943 }
6944
6945 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6946 {
6947 return 0;
6948 }*/
6949
6950 // Some test to guess with no need to make database access
6951 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6952 if ($local == 1) {
6953 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6954 return 0;
6955 }
6956 if ($thirdparty_seller->id == $mysoc->id) {
6957 if (!$thirdparty_buyer->localtax1_assuj) {
6958 return 0;
6959 }
6960 } else {
6961 if (!$thirdparty_seller->localtax1_assuj) {
6962 return 0;
6963 }
6964 }
6965 }
6966
6967 if ($local == 2) {
6968 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6969 if (!$mysoc->localtax2_assuj) {
6970 return 0; // If main vat is 0, IRPF may be different than 0.
6971 }
6972 if ($thirdparty_seller->id == $mysoc->id) {
6973 if (!$thirdparty_buyer->localtax2_assuj) {
6974 return 0;
6975 }
6976 } else {
6977 if (!$thirdparty_seller->localtax2_assuj) {
6978 return 0;
6979 }
6980 }
6981 }
6982 } else {
6983 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6984 return 0;
6985 }
6986 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6987 return 0;
6988 }
6989 }
6990
6991 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6992 if (in_array($mysoc->country_code, array('ES'))) {
6993 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6994 }
6995
6996 // Search local taxes
6997 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
6998 if ($local == 1) {
6999 if ($thirdparty_seller != $mysoc) {
7000 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7001 return $thirdparty_seller->localtax1_value;
7002 }
7003 } else { // i am the seller
7004 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7005 return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
7006 }
7007 }
7008 }
7009 if ($local == 2) {
7010 if ($thirdparty_seller != $mysoc) {
7011 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7012 // TODO We should also return value defined on thirdparty only if defined
7013 return $thirdparty_seller->localtax2_value;
7014 }
7015 } else { // i am the seller
7016 if (in_array($mysoc->country_code, array('ES'))) {
7017 return $thirdparty_buyer->localtax2_value;
7018 } else {
7019 return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
7020 }
7021 }
7022 }
7023 }
7024
7025 // By default, search value of local tax on line of common tax
7026 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7027 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7028 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7029 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7030 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7031 if (!empty($vatratecode)) {
7032 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7033 } else {
7034 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7035 }
7036
7037 $resql = $db->query($sql);
7038
7039 if ($resql) {
7040 $obj = $db->fetch_object($resql);
7041 if ($obj) {
7042 if ($local == 1) {
7043 return $obj->localtax1;
7044 } elseif ($local == 2) {
7045 return $obj->localtax2;
7046 }
7047 }
7048 }
7049
7050 return 0;
7051}
7052
7053
7062function isOnlyOneLocalTax($local)
7063{
7064 $tax = get_localtax_by_third($local);
7065
7066 $valors = explode(":", $tax);
7067
7068 if (count($valors) > 1) {
7069 return false;
7070 } else {
7071 return true;
7072 }
7073}
7074
7081function get_localtax_by_third($local)
7082{
7083 global $db, $mysoc;
7084
7085 $sql = " SELECT t.localtax".$local." as localtax";
7086 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7087 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7088 $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";
7089 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7090 $sql .= " AND t.localtax".$local."_type <> '0'";
7091 $sql .= " ORDER BY t.rowid DESC";
7092
7093 $resql = $db->query($sql);
7094 if ($resql) {
7095 $obj = $db->fetch_object($resql);
7096 if ($obj) {
7097 return $obj->localtax;
7098 } else {
7099 return '0';
7100 }
7101 }
7102
7103 return 'Error';
7104}
7105
7106
7118function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7119{
7120 global $db;
7121
7122 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7123
7124 // Search local taxes
7125 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7126 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7127 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7128 if ($firstparamisid) {
7129 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7130 } else {
7131 $vatratecleaned = $vatrate;
7132 $vatratecode = '';
7133 $reg = array();
7134 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7135 $vatratecleaned = $reg[1];
7136 $vatratecode = $reg[2];
7137 }
7138
7139 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7140 /*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 ??
7141 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7142 $sql .= " WHERE t.fk_pays = c.rowid";
7143 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7144 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7145 } else {
7146 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7147 }
7148 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7149 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7150 if ($vatratecode) {
7151 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7152 }
7153 }
7154
7155 $resql = $db->query($sql);
7156 if ($resql) {
7157 $obj = $db->fetch_object($resql);
7158 if ($obj) {
7159 return array(
7160 'rowid' => $obj->rowid,
7161 'code' => $obj->code,
7162 'rate' => $obj->rate,
7163 'localtax1' => $obj->localtax1,
7164 'localtax1_type' => $obj->localtax1_type,
7165 'localtax2' => $obj->localtax2,
7166 'localtax2_type' => $obj->localtax2_type,
7167 'npr' => $obj->npr,
7168 'accountancy_code_sell' => $obj->accountancy_code_sell,
7169 'accountancy_code_buy' => $obj->accountancy_code_buy
7170 );
7171 } else {
7172 return array();
7173 }
7174 } else {
7175 dol_print_error($db);
7176 }
7177
7178 return array();
7179}
7180
7197function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7198{
7199 global $db, $mysoc;
7200
7201 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7202
7203 // Search local taxes
7204 $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";
7205 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7206 if ($firstparamisid) {
7207 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7208 } else {
7209 $vatratecleaned = $vatrate;
7210 $vatratecode = '';
7211 $reg = array();
7212 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7213 $vatratecleaned = $reg[1];
7214 $vatratecode = $reg[2];
7215 }
7216
7217 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7218 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7219 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7220 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7221 } else {
7222 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7223 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7224 }
7225 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7226 if ($vatratecode) {
7227 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7228 }
7229 }
7230
7231 $resql = $db->query($sql);
7232 if ($resql) {
7233 $obj = $db->fetch_object($resql);
7234
7235 if ($obj) {
7236 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7237
7238 if ($local == 1) {
7239 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7240 } elseif ($local == 2) {
7241 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7242 } else {
7243 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);
7244 }
7245 }
7246 }
7247
7248 return array();
7249}
7250
7261function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7262{
7263 global $db, $mysoc;
7264
7265 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7266
7267 $ret = 0;
7268 $found = 0;
7269
7270 if ($idprod > 0) {
7271 // Load product
7272 $product = new Product($db);
7273 $product->fetch($idprod);
7274
7275 if (($mysoc->country_code == $thirdpartytouse->country_code)
7276 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7277 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7278 ) {
7279 // If country of thirdparty to consider is ours
7280 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7281 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7282 if ($result > 0) {
7283 $ret = $product->vatrate_supplier;
7284 if ($product->default_vat_code_supplier) {
7285 $ret .= ' ('.$product->default_vat_code_supplier.')';
7286 }
7287 $found = 1;
7288 }
7289 }
7290 if (!$found) {
7291 $ret = $product->tva_tx; // Default sales vat of product
7292 if ($product->default_vat_code) {
7293 $ret .= ' ('.$product->default_vat_code.')';
7294 }
7295 $found = 1;
7296 }
7297 } else {
7298 // TODO Read default product vat according to product and an other countrycode.
7299 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7300 }
7301 }
7302
7303 if (!$found) {
7304 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7305 // 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).
7306 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7307 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7308 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7309 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7310 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7311 $sql .= $db->plimit(1);
7312
7313 $resql = $db->query($sql);
7314 if ($resql) {
7315 $obj = $db->fetch_object($resql);
7316 if ($obj) {
7317 $ret = $obj->vat_rate;
7318 if ($obj->default_vat_code) {
7319 $ret .= ' ('.$obj->default_vat_code.')';
7320 }
7321 }
7322 $db->free($resql);
7323 } else {
7324 dol_print_error($db);
7325 }
7326 } else {
7327 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7328 // '1.23'
7329 // or '1.23 (CODE)'
7330 $defaulttx = '';
7331 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7332 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7333 }
7334 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7335 $defaultcode = $reg[1];
7336 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7337 }*/
7338
7339 $ret = $defaulttx;
7340 }
7341 }
7342
7343 dol_syslog("get_product_vat_for_country: ret=".$ret);
7344 return $ret;
7345}
7346
7356function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7357{
7358 global $db, $mysoc;
7359
7360 if (!class_exists('Product')) {
7361 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7362 }
7363
7364 $ret = 0;
7365 $found = 0;
7366
7367 if ($idprod > 0) {
7368 // Load product
7369 $product = new Product($db);
7370 $result = $product->fetch($idprod);
7371
7372 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7373 /* Not defined yet, so we don't use this
7374 if ($local==1) $ret=$product->localtax1_tx;
7375 elseif ($local==2) $ret=$product->localtax2_tx;
7376 $found=1;
7377 */
7378 } else {
7379 // TODO Read default product vat according to product and another countrycode.
7380 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7381 }
7382 }
7383
7384 if (!$found) {
7385 // If vat of product for the country not found or not defined, we return higher vat of country.
7386 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7387 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7388 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7389 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7390 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7391 $sql .= $db->plimit(1);
7392
7393 $resql = $db->query($sql);
7394 if ($resql) {
7395 $obj = $db->fetch_object($resql);
7396 if ($obj) {
7397 if ($local == 1) {
7398 $ret = $obj->localtax1;
7399 } elseif ($local == 2) {
7400 $ret = $obj->localtax2;
7401 }
7402 }
7403 } else {
7404 dol_print_error($db);
7405 }
7406 }
7407
7408 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7409 return $ret;
7410}
7411
7428function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7429{
7430 global $conf;
7431
7432 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7433
7434 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7435 $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;
7436
7437 $seller_country_code = $thirdparty_seller->country_code;
7438 $seller_in_cee = isInEEC($thirdparty_seller);
7439
7440 $buyer_country_code = $thirdparty_buyer->country_code;
7441 $buyer_in_cee = isInEEC($thirdparty_buyer);
7442
7443 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 : ''));
7444
7445 // 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)
7446 // we use the buyer VAT.
7447 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7448 if ($seller_in_cee && $buyer_in_cee) {
7449 $isacompany = $thirdparty_buyer->isACompany();
7450 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7451 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7452 if (!isValidVATID($thirdparty_buyer)) {
7453 $isacompany = 0;
7454 }
7455 }
7456
7457 if (!$isacompany) {
7458 //print 'VATRULE 0';
7459 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7460 }
7461 }
7462 }
7463
7464 // If seller does not use VAT, default VAT is 0. End of rule.
7465 if (!$seller_use_vat) {
7466 //print 'VATRULE 1';
7467 return 0;
7468 }
7469
7470 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7471 if (($seller_country_code == $buyer_country_code)
7472 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7473 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7474 ) { // Warning ->country_code not always defined
7475 //print 'VATRULE 2';
7476 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7477
7478 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7479 // Special case for india.
7480 //print 'VATRULE 2b';
7481 $reg = array();
7482 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7483 // we must revert the C+S into I
7484 $tmpvat = str_replace("C+S", "I", $tmpvat);
7485 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7486 // we must revert the I into C+S
7487 $tmpvat = str_replace("I", "C+S", $tmpvat);
7488 }
7489 }
7490
7491 return $tmpvat;
7492 }
7493
7494 // 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.
7495 // 'VATRULE 3' - Not supported
7496
7497 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7498 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7499 if (($seller_in_cee && $buyer_in_cee)) {
7500 $isacompany = $thirdparty_buyer->isACompany();
7501 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7502 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7503 if (!isValidVATID($thirdparty_buyer)) {
7504 $isacompany = 0;
7505 }
7506 }
7507
7508 if (!$isacompany) {
7509 //print 'VATRULE 4';
7510 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7511 } else {
7512 //print 'VATRULE 5';
7513 return 0;
7514 }
7515 }
7516
7517 // 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
7518 // I don't see any use case that need this rule.
7519 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7520 $isacompany = $thirdparty_buyer->isACompany();
7521 if (!$isacompany) {
7522 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7523 //print 'VATRULE extra';
7524 }
7525 }
7526
7527 // Otherwise the VAT proposed by default=0. End of rule.
7528 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7529 //print 'VATRULE 6';
7530 return 0;
7531}
7532
7533
7544function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7545{
7546 global $db;
7547
7548 if ($idprodfournprice > 0) {
7549 if (!class_exists('ProductFournisseur')) {
7550 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7551 }
7552 $prodprice = new ProductFournisseur($db);
7553 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7554 return $prodprice->fourn_tva_npr;
7555 } elseif ($idprod > 0) {
7556 if (!class_exists('Product')) {
7557 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7558 }
7559 $prod = new Product($db);
7560 $prod->fetch($idprod);
7561 return $prod->tva_npr;
7562 }
7563
7564 return 0;
7565}
7566
7580function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7581{
7582 global $mysoc;
7583
7584 if (!is_object($thirdparty_seller)) {
7585 return -1;
7586 }
7587 if (!is_object($thirdparty_buyer)) {
7588 return -1;
7589 }
7590
7591 if ($local == 1) { // Localtax 1
7592 if ($mysoc->country_code == 'ES') {
7593 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7594 return 0;
7595 }
7596 } else {
7597 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7598 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7599 return 0;
7600 }
7601 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7602 return 0;
7603 }
7604 }
7605 } elseif ($local == 2) { //I Localtax 2
7606 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7607 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7608 return 0;
7609 }
7610 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7611 return 0;
7612 }
7613 }
7614
7615 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7616 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7617 }
7618
7619 return 0;
7620}
7621
7630function yn($yesno, $case = 1, $color = 0)
7631{
7632 global $langs;
7633
7634 $result = 'unknown';
7635 $classname = '';
7636 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7637 $result = $langs->trans('yes');
7638 if ($case == 1 || $case == 3) {
7639 $result = $langs->trans("Yes");
7640 }
7641 if ($case == 2) {
7642 $result = '<input type="checkbox" value="1" checked disabled>';
7643 }
7644 if ($case == 3) {
7645 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7646 }
7647 if ($case == 4) {
7648 $result = img_picto('check', 'check');
7649 }
7650
7651 $classname = 'ok';
7652 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7653 $result = $langs->trans("no");
7654 if ($case == 1 || $case == 3) {
7655 $result = $langs->trans("No");
7656 }
7657 if ($case == 2) {
7658 $result = '<input type="checkbox" value="0" disabled>';
7659 }
7660 if ($case == 3) {
7661 $result = '<input type="checkbox" value="0" disabled> '.$result;
7662 }
7663 if ($case == 4) {
7664 $result = img_picto('uncheck', 'uncheck');
7665 }
7666
7667 if ($color == 2) {
7668 $classname = 'ok';
7669 } else {
7670 $classname = 'error';
7671 }
7672 }
7673 if ($color) {
7674 return '<span class="'.$classname.'">'.$result.'</span>';
7675 }
7676 return $result;
7677}
7678
7697function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7698{
7699 if (empty($modulepart) && is_object($object)) {
7700 if (!empty($object->module)) {
7701 $modulepart = $object->module;
7702 } elseif (!empty($object->element)) {
7703 $modulepart = $object->element;
7704 }
7705 }
7706
7707 $path = '';
7708
7709 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7710 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7711 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7712 $arrayforoldpath['product'] = 2;
7713 }
7714
7715 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7716 $level = $arrayforoldpath[$modulepart];
7717 }
7718 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7719 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7720 if (empty($num) && is_object($object)) {
7721 $num = $object->id;
7722 }
7723 if (empty($alpha)) {
7724 $num = preg_replace('/([^0-9])/i', '', $num);
7725 } else {
7726 $num = preg_replace('/^.*\-/i', '', $num);
7727 }
7728 $num = substr("000".$num, -$level);
7729 if ($level == 1) {
7730 $path = substr($num, 0, 1);
7731 }
7732 if ($level == 2) {
7733 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7734 }
7735 if ($level == 3) {
7736 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7737 }
7738 } else {
7739 // We will enhance here a common way of forging path for document storage.
7740 // In a future, we may distribute directories on several levels depending on setup and object.
7741 // Here, $object->id, $object->ref and $modulepart are required.
7742 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
7743 // Special case for thirdparty, where the ref is a company name that is not unique so path on disk is using the ID instead of the ref
7744 $path = dol_sanitizeFileName($object->id);
7745 } else {
7746 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7747 }
7748 }
7749
7750 if (empty($withoutslash) && !empty($path)) {
7751 $path .= '/';
7752 }
7753
7754 return $path;
7755}
7756
7765function dol_mkdir($dir, $dataroot = '', $newmask = '')
7766{
7767 global $conf;
7768
7769 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7770
7771 $dir_osencoded = dol_osencode($dir);
7772 if (@is_dir($dir_osencoded)) {
7773 return 0;
7774 }
7775
7776 $nberr = 0;
7777 $nbcreated = 0;
7778
7779 $ccdir = '';
7780 if (!empty($dataroot)) {
7781 // Remove data root from loop
7782 $dir = str_replace($dataroot.'/', '', $dir);
7783 $ccdir = $dataroot.'/';
7784 }
7785
7786 $cdir = explode("/", $dir);
7787 $num = count($cdir);
7788 for ($i = 0; $i < $num; $i++) {
7789 if ($i > 0) {
7790 $ccdir .= '/'.$cdir[$i];
7791 } else {
7792 $ccdir .= $cdir[$i];
7793 }
7794 $regs = array();
7795 if (preg_match("/^.:$/", $ccdir, $regs)) {
7796 continue; // If the Windows path is incomplete, continue with next directory
7797 }
7798
7799 // Attention, is_dir() can fail event if the directory exists
7800 // (i.e. according the open_basedir configuration)
7801 if ($ccdir) {
7802 $ccdir_osencoded = dol_osencode($ccdir);
7803 if (!@is_dir($ccdir_osencoded)) {
7804 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7805
7806 umask(0);
7807 $dirmaskdec = octdec((string) $newmask);
7808 if (empty($newmask)) {
7809 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7810 }
7811 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7812 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7813 // If the is_dir has returned a false information, we arrive here
7814 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7815 $nberr++;
7816 } else {
7817 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7818 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7819 $nbcreated++;
7820 }
7821 } else {
7822 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7823 }
7824 }
7825 }
7826 return ($nberr ? -$nberr : $nbcreated);
7827}
7828
7829
7837function dolChmod($filepath, $newmask = '')
7838{
7839 global $conf;
7840
7841 if (!empty($newmask)) {
7842 @chmod($filepath, octdec($newmask));
7843 } elseif (getDolGlobalString('MAIN_UMASK')) {
7844 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7845 }
7846}
7847
7848
7854function picto_required()
7855{
7856 return '<span class="fieldrequired">*</span>';
7857}
7858
7859
7876function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
7877{
7878 if (is_null($stringtoclean)) {
7879 return '';
7880 }
7881
7882 if ($removelinefeed == 2) {
7883 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
7884 }
7885 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
7886
7887 // 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)
7888 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7889
7890 $temp = str_replace('< ', '__ltspace__', $temp);
7891 $temp = str_replace('<:', '__lttwopoints__', $temp);
7892
7893 if ($strip_tags) {
7894 $temp = strip_tags($temp);
7895 } else {
7896 // 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).
7897 $pattern = "/<[^<>]+>/";
7898 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
7899 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
7900 // pass 2 - $temp after pass 2: 0000-021
7901 $tempbis = $temp;
7902 do {
7903 $temp = $tempbis;
7904 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
7905 $tempbis = preg_replace($pattern, '', $tempbis);
7906 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
7907 } while ($tempbis != $temp);
7908
7909 $temp = $tempbis;
7910
7911 // 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).
7912 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
7913 }
7914
7915 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
7916
7917 // Remove also carriage returns
7918 if ($removelinefeed == 1) {
7919 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
7920 }
7921
7922 // And double quotes
7923 if ($removedoublespaces) {
7924 while (strpos($temp, " ")) {
7925 $temp = str_replace(" ", " ", $temp);
7926 }
7927 }
7928
7929 $temp = str_replace('__ltspace__', '< ', $temp);
7930 $temp = str_replace('__lttwopoints__', '<:', $temp);
7931
7932 return trim($temp);
7933}
7934
7950function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0)
7951{
7952 if (empty($allowed_tags)) {
7953 $allowed_tags = array(
7954 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
7955 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
7956 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
7957 );
7958 }
7959 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
7960 if ($allowiframe) {
7961 if (!in_array('iframe', $allowed_tags)) {
7962 $allowed_tags[] = "iframe";
7963 }
7964 }
7965 if ($allowlink) {
7966 if (!in_array('link', $allowed_tags)) {
7967 $allowed_tags[] = "link";
7968 }
7969 }
7970
7971 $allowed_tags_string = implode("><", $allowed_tags);
7972 $allowed_tags_string = '<'.$allowed_tags_string.'>';
7973
7974 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
7975
7976 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
7977
7978 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
7979 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
7980
7981 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
7982 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
7983
7984 // Remove all HTML tags
7985 $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
7986
7987 if ($cleanalsosomestyles) { // Clean for remaining html tags
7988 $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
7989 }
7990 if ($removeclassattribute) { // Clean for remaining html tags
7991 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
7992 }
7993
7994 // Remove 'javascript:' that we should not find into a text with
7995 // 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)).
7996 if ($cleanalsojavascript) {
7997 $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);
7998 }
7999
8000 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8001
8002 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8003
8004
8005 return $temp;
8006}
8007
8008
8021function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8022{
8023 if (is_null($allowed_attributes)) {
8024 $allowed_attributes = array(
8025 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8026 // HTML5
8027 "header", "footer", "nav", "section", "menu", "menuitem"
8028 );
8029 }
8030 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8031 if (!in_array("content", $allowed_attributes)) {
8032 $allowed_attributes[] = "content";
8033 }
8034 if (!in_array("http-equiv", $allowed_attributes)) {
8035 $allowed_attributes[] = "http-equiv";
8036 }
8037
8038 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8039 $stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8040
8041 // Warning: loadHTML does not support HTML5 on old libxml versions.
8042 $dom = new DOMDocument('', 'UTF-8');
8043 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8044 $savwarning = error_reporting();
8045 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8046 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8047 error_reporting($savwarning);
8048
8049 if ($dom instanceof DOMDocument) {
8050 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8051 $el = $els->item($i);
8052 if (!$el instanceof DOMElement) {
8053 continue;
8054 }
8055 $attrs = $el->attributes;
8056 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8057 //var_dump($attrs->item($ii));
8058 if (!empty($attrs->item($ii)->name)) {
8059 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8060 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8061 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8062 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8063 // If attribute is 'style'
8064 $valuetoclean = $attrs->item($ii)->value;
8065
8066 if (isset($valuetoclean)) {
8067 do {
8068 $oldvaluetoclean = $valuetoclean;
8069 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8070 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8071 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8072 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8073 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8074 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8075 }
8076
8077 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8078 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8079 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8080 } while ($oldvaluetoclean != $valuetoclean);
8081 }
8082
8083 $attrs->item($ii)->value = $valuetoclean;
8084 }
8085 }
8086 }
8087 }
8088 }
8089
8090 $dom->encoding = 'UTF-8';
8091
8092 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8093 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8094
8095 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8096 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8097 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8098
8099 return trim($return);
8100 } else {
8101 return $stringtoclean;
8102 }
8103}
8104
8116function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8117{
8118 $temp = $stringtoclean;
8119 foreach ($disallowed_tags as $tagtoremove) {
8120 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8121 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8122 }
8123
8124 if ($cleanalsosomestyles) {
8125 $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
8126 }
8127
8128 return $temp;
8129}
8130
8131
8141function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8142{
8143 if ($nboflines == 1) {
8144 if (dol_textishtml($text)) {
8145 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8146 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8147 } else {
8148 if (isset($text)) {
8149 $firstline = preg_replace('/[\n\r].*/', '', $text);
8150 } else {
8151 $firstline = '';
8152 }
8153 }
8154 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8155 } else {
8156 $ishtml = 0;
8157 if (dol_textishtml($text)) {
8158 $text = preg_replace('/\n/', '', $text);
8159 $ishtml = 1;
8160 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8161 } else {
8162 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8163 }
8164
8165 $text = strtr($text, $repTable);
8166 if ($charset == 'UTF-8') {
8167 $pattern = '/(<br[^>]*>)/Uu';
8168 } else {
8169 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8170 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8171 }
8172 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8173
8174 $firstline = '';
8175 $i = 0;
8176 $countline = 0;
8177 $lastaddediscontent = 1;
8178 while ($countline < $nboflines && isset($a[$i])) {
8179 if (preg_match('/<br[^>]*>/', $a[$i])) {
8180 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8181 $firstline .= ($ishtml ? "<br>\n" : "\n");
8182 // Is it a br for a new line of after a printed line ?
8183 if (!$lastaddediscontent) {
8184 $countline++;
8185 }
8186 $lastaddediscontent = 0;
8187 }
8188 } else {
8189 $firstline .= $a[$i];
8190 $lastaddediscontent = 1;
8191 $countline++;
8192 }
8193 $i++;
8194 }
8195
8196 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8197 //unset($a);
8198 $ret = $firstline.($adddots ? '...' : '');
8199 //exit;
8200 return $ret;
8201 }
8202}
8203
8204
8216function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8217{
8218 if (is_null($stringtoencode)) {
8219 return '';
8220 }
8221
8222 if (!$nl2brmode) {
8223 return nl2br($stringtoencode, $forxml);
8224 } else {
8225 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8226 return $ret;
8227 }
8228}
8229
8239function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8240{
8241 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8242 // TODO using sandbox on inline html content is not possible yet with current browsers
8243 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8244 //$s .= $stringtoencode;
8245 //$s .= '</body></html></iframe>';
8246 return $stringtoencode;
8247 } else {
8248 $out = $stringtoencode;
8249
8250 // First clean HTML content
8251 do {
8252 $oldstringtoclean = $out;
8253
8254 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8255 try {
8256 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8257 if (LIBXML_VERSION < 20900) {
8258 // Avoid load of external entities (security problem).
8259 // Required only if LIBXML_VERSION < 20900
8260 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8261 libxml_disable_entity_loader(true);
8262 }
8263
8264 $dom = new DOMDocument();
8265 // Add a trick to solve pb with text without parent tag
8266 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8267 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8268
8269 if (dol_textishtml($out)) {
8270 $out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8271 } else {
8272 $out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
8273 }
8274
8275 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8276
8277 $dom->encoding = 'UTF-8';
8278
8279 $out = trim($dom->saveHTML());
8280
8281 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8282 $out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8283 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8284 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8285 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8286 // $out = preg_replace('/<\/div>$/', '', $out);
8287 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8288 } catch (Exception $e) {
8289 // If error, invalid HTML string with no way to clean it
8290 //print $e->getMessage();
8291 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8292 }
8293 }
8294
8295 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && $check != 'restricthtmlallowunvalid') {
8296 try {
8297 // Try cleaning using tidy
8298 if (extension_loaded('tidy') && class_exists("tidy")) {
8299 //print "aaa".$out."\n";
8300
8301 // See options at https://tidy.sourceforge.net/docs/quickref.html
8302 $config = array(
8303 'clean' => false,
8304 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8305 'doctype' => 'strict',
8306 'show-body-only' => true,
8307 "indent-attributes" => false,
8308 "vertical-space" => false,
8309 //'ident' => false, // Not always supported
8310 "wrap" => 0
8311 // HTML5 tags
8312 //'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',
8313 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8314 //'new-empty-tags' => 'command embed keygen source track wbr',
8315 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8316 );
8317
8318 // Tidy
8319 $tidy = new tidy();
8320 $out = $tidy->repairString($out, $config, 'utf8');
8321
8322 //print "xxx".$out;exit;
8323 }
8324 } catch (Exception $e) {
8325 // If error, invalid HTML string with no way to clean it
8326 //print $e->getMessage();
8327 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8328 }
8329 }
8330
8331 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8332 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8333
8334 // Clean some html entities that are useless so text is cleaner
8335 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8336
8337 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8338 // encoded using text entities) so we can then exclude all numeric entities.
8339 $out = preg_replace('/&#39;/i', '&apos;', $out);
8340
8341 // 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).
8342 // 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
8343 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8344 $out = preg_replace_callback(
8345 '/&#(x?[0-9][0-9a-f]+;?)/i',
8350 static function ($m) {
8351 return realCharForNumericEntities($m);
8352 },
8353 $out
8354 );
8355
8356 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8357 $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'.
8358
8359 // Keep only some html tags and remove also some 'javascript:' strings
8360 if ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8361 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8362 } else {
8363 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8364 }
8365
8366 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8367 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8369 }
8370
8371 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8372 $out = preg_replace('/&apos;/i', "&#39;", $out);
8373
8374 // Now remove js
8375 // 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
8376 $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)>
8377 $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);
8378 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8379 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8380 $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);
8381 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8382 // More not into the previous list
8383 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8384 } while ($oldstringtoclean != $out);
8385
8386 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8387 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8388 // 'url(' to avoid inline style like background: url(http...
8389 // '<link' to avoid <link href="http...">
8390 $reg = array();
8391 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8392 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8393 $nblinks = count($reg[0]);
8394 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8395 $out = 'ErrorTooManyLinksIntoHTMLString';
8396 }
8397
8398 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8399 if ($nblinks > 0) {
8400 $out = 'ErrorHTMLLinksNotAllowed';
8401 }
8402 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8403 $nblinks = 0;
8404 // Loop on each url in src= and url(
8405 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8406
8407 $matches = array();
8408 if (preg_match_all($pattern, $out, $matches)) {
8409 // URLs are into $matches[1]
8410 $urls = $matches[1];
8411
8412 // Affiche les URLs
8413 foreach ($urls as $url) {
8414 $nblinks++;
8415 echo "Found url = ".$url . "\n";
8416 }
8417 if ($nblinks > 0) {
8418 $out = 'ErrorHTMLExternalLinksNotAllowed';
8419 }
8420 }
8421 }
8422
8423 return $out;
8424 }
8425}
8426
8447function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8448{
8449 if (is_null($stringtoencode)) {
8450 return '';
8451 }
8452
8453 $newstring = $stringtoencode;
8454 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8455 $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.
8456 if ($removelasteolbr) {
8457 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8458 }
8459 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8460 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8461 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8462 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8463 } else {
8464 if ($removelasteolbr) {
8465 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8466 }
8467 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8468 }
8469 // Other substitutions that htmlentities does not do
8470 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8471 return $newstring;
8472}
8473
8481function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8482{
8483 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8484 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8485 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8486 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8487 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8488 return $ret;
8489}
8490
8497function dol_htmlcleanlastbr($stringtodecode)
8498{
8499 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8500 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8501 return $ret;
8502}
8503
8513function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8514{
8515 $newstring = $a;
8516 if ($keepsomeentities) {
8517 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8518 }
8519 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8520 if ($keepsomeentities) {
8521 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8522 }
8523 return $newstring;
8524}
8525
8537function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8538{
8539 return htmlentities($string, $flags, $encoding, $double_encode);
8540}
8541
8553function dol_string_is_good_iso($s, $clean = 0)
8554{
8555 $len = dol_strlen($s);
8556 $out = '';
8557 $ok = 1;
8558 for ($scursor = 0; $scursor < $len; $scursor++) {
8559 $ordchar = ord($s[$scursor]);
8560 //print $scursor.'-'.$ordchar.'<br>';
8561 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8562 $ok = 0;
8563 break;
8564 } elseif ($ordchar > 126 && $ordchar < 160) {
8565 $ok = 0;
8566 break;
8567 } elseif ($clean) {
8568 $out .= $s[$scursor];
8569 }
8570 }
8571 if ($clean) {
8572 return $out;
8573 }
8574 return $ok;
8575}
8576
8585function dol_nboflines($s, $maxchar = 0)
8586{
8587 if ($s == '') {
8588 return 0;
8589 }
8590 $arraystring = explode("\n", $s);
8591 $nb = count($arraystring);
8592
8593 return $nb;
8594}
8595
8596
8606function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8607{
8608 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8609 if (dol_textishtml($text)) {
8610 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8611 }
8612
8613 $text = strtr($text, $repTable);
8614 if ($charset == 'UTF-8') {
8615 $pattern = '/(<br[^>]*>)/Uu';
8616 } else {
8617 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8618 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8619 }
8620 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8621
8622 $nblines = (int) floor((count($a) + 1) / 2);
8623 // count possible auto line breaks
8624 if ($maxlinesize) {
8625 foreach ($a as $line) {
8626 if (dol_strlen($line) > $maxlinesize) {
8627 //$line_dec = html_entity_decode(strip_tags($line));
8628 $line_dec = html_entity_decode($line);
8629 if (dol_strlen($line_dec) > $maxlinesize) {
8630 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8631 $nblines += substr_count($line_dec, '\n');
8632 }
8633 }
8634 }
8635 }
8636
8637 unset($a);
8638 return $nblines;
8639}
8640
8649function dol_textishtml($msg, $option = 0)
8650{
8651 if (is_null($msg)) {
8652 return false;
8653 }
8654
8655 if ($option == 1) {
8656 if (preg_match('/<html/i', $msg)) {
8657 return true;
8658 } elseif (preg_match('/<body/i', $msg)) {
8659 return true;
8660 } elseif (preg_match('/<\/textarea/i', $msg)) {
8661 return true;
8662 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8663 return true;
8664 } elseif (preg_match('/<br/i', $msg)) {
8665 return true;
8666 }
8667 return false;
8668 } else {
8669 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8670 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8671 if (preg_match('/<html/i', $msg)) {
8672 return true;
8673 } elseif (preg_match('/<body/i', $msg)) {
8674 return true;
8675 } elseif (preg_match('/<\/textarea/i', $msg)) {
8676 return true;
8677 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8678 return true;
8679 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8680 return true;
8681 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8682 return true;
8683 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8684 return true;
8685 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8686 return true; // must accept <img src="http://example.com/aaa.png" />
8687 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8688 return true; // must accept <a href="http://example.com/aaa.png" />
8689 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8690 return true;
8691 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8692 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8693 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8694 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8695 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8696 }
8697
8698 return false;
8699 }
8700}
8701
8716function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8717{
8718 if (!empty($invert)) {
8719 $tmp = $text1;
8720 $text1 = $text2;
8721 $text2 = $tmp;
8722 }
8723
8724 $ret = '';
8725 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8726 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8727 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8728 return $ret;
8729}
8730
8731
8732
8746function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8747{
8748 global $db, $conf, $mysoc, $user, $extrafields;
8749
8750 $substitutionarray = array();
8751
8752 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
8753 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8754 // this will include signature content first and then replace var found into content of signature
8755 //var_dump($onlykey);
8756 $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()
8757 $usersignature = $user->signature;
8758 $substitutionarray = array_merge($substitutionarray, array(
8759 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8760 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8761 ));
8762
8763 if (is_object($user) && ($user instanceof User)) {
8764 $substitutionarray = array_merge($substitutionarray, array(
8765 '__USER_ID__' => (string) $user->id,
8766 '__USER_LOGIN__' => (string) $user->login,
8767 '__USER_EMAIL__' => (string) $user->email,
8768 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8769 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8770 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8771 '__USER_FAX__' => (string) $user->office_fax,
8772 '__USER_LASTNAME__' => (string) $user->lastname,
8773 '__USER_FIRSTNAME__' => (string) $user->firstname,
8774 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8775 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8776 '__USER_JOB__' => (string) $user->job,
8777 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8778 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8779 ));
8780 }
8781 }
8782 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8783 $substitutionarray = array_merge($substitutionarray, array(
8784 '__MYCOMPANY_NAME__' => $mysoc->name,
8785 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8786 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8787 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8788 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8789 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8790 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8791 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8792 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8793 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8794 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8795 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8796 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8797 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8798 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8799 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8800 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8801 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8802 '__MYCOMPANY_TOWN__' => $mysoc->town,
8803 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
8804 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
8805 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
8806 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
8807 ));
8808 }
8809
8810 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
8811 if ($onlykey) {
8812 $substitutionarray['__ID__'] = '__ID__';
8813 $substitutionarray['__REF__'] = '__REF__';
8814 $substitutionarray['__NEWREF__'] = '__NEWREF__';
8815 $substitutionarray['__LABEL__'] = '__LABEL__';
8816 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
8817 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
8818 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
8819 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
8820 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
8821
8822 if (isModEnabled("societe")) { // Most objects are concerned
8823 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
8824 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
8825 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
8826 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
8827 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
8828 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
8829 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
8830 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
8831 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
8832 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
8833 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
8834 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
8835 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
8836 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
8837 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
8838 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
8839 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
8840 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
8841 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
8842 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
8843 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
8844 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
8845 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
8846 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
8847 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
8848 }
8849 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
8850 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
8851 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
8852 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
8853 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
8854 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
8855 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
8856 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
8857 }
8858 // add substitution variables for ticket
8859 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
8860 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
8861 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
8862 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
8863 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
8864 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
8865 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
8866 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
8867 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
8868 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
8869 }
8870
8871 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
8872 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
8873 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
8874 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
8875 }
8876 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
8877 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
8878 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
8879 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
8880 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
8881 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
8882 }
8883 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
8884 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
8885 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
8886 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
8887 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
8888 }
8889 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
8890 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
8891 }
8892 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
8893 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
8894 }
8895 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
8896 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
8897 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
8898 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
8899 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
8900 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
8901 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
8902
8903 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
8904 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
8905 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
8906 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
8907 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
8908
8909 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
8910 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
8911 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
8912 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
8913 }
8914 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
8915 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
8916 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
8917 }
8918 } else {
8919 '@phan-var-force Adherent|Delivery $object';
8920 $substitutionarray['__ID__'] = $object->id;
8921 $substitutionarray['__REF__'] = $object->ref;
8922 $substitutionarray['__NEWREF__'] = $object->newref;
8923 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
8924 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8925 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8926 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
8927 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
8928
8929 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', 0, $outputlangs) : '');
8930 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', 0, $outputlangs) : '');
8931 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', 0, $outputlangs) : '');
8932
8933 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
8934 $date_delivery = null;
8935 if (property_exists($object, 'date_delivery')) {
8936 $date_delivery = $object->date_delivery;
8937 } elseif (property_exists($object, 'delivery_date')) {
8938 $date_delivery = $object->delivery_date;
8939 }
8940 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', 0, $outputlangs) : '');
8941 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
8942 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
8943 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
8944 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
8945 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
8946 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
8947 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
8948 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
8949
8950 // For backward compatibility (deprecated)
8951 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8952 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8953 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', 0, $outputlangs) : '');
8954 $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 : '')) : '');
8955 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
8956
8957 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
8958 '@phan-var-force Adherent $object';
8959 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
8960
8961 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
8962 if (method_exists($object, 'getCivilityLabel')) {
8963 $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
8964 }
8965 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
8966 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
8967 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
8968 if (method_exists($object, 'getFullName')) {
8969 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
8970 }
8971 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
8972 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
8973 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
8974 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
8975 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
8976 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
8977 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
8978 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
8979 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
8980 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
8981 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
8982 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
8983 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
8984 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
8985 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
8986
8987 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
8988 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
8989 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
8990 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
8991 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
8992 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
8993 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
8994 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
8995 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
8996 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
8997 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
8998 }
8999
9000 if (is_object($object) && $object->element == 'societe') {
9001 '@phan-var-force Societe $object';
9002 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
9003 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
9004 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
9005 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
9006 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
9007 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
9008 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
9009 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
9010 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
9011 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
9012 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
9013 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
9014 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
9015 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
9016 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
9017 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
9018 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
9019 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
9020 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
9021 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
9022 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
9023 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
9024 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
9025 } elseif (is_object($object->thirdparty)) {
9026 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
9027 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9028 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9029 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9030 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9031 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9032 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9033 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9034 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9035 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9036 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9037 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9038 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9039 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9040 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9041 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9042 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9043 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9044 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9045 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9046 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9047 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9048 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9049 }
9050
9051 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9052 '@phan-var-force RecruitmentCandidature $object';
9053 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9054 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9055 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9056 }
9057 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9058 '@phan-var-force ConferenceOrBoothAttendee $object';
9059 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9060 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9061 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9062 }
9063
9064 if (is_object($object) && $object->element == 'project') {
9065 '@phan-var-force Project $object';
9066 $substitutionarray['__PROJECT_ID__'] = $object->id;
9067 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9068 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9069 } elseif (is_object($object)) {
9070 $project = null;
9071 if (!empty($object->project)) {
9072 $project = $object->project;
9073 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9074 $project = $object->projet;
9075 }
9076 if (!is_null($project) && is_object($project)) {
9077 $substitutionarray['__PROJECT_ID__'] = $project->id;
9078 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9079 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9080 } else {
9081 // can substitute variables for project : uses lazy load in "make_substitutions" method
9082 $project_id = 0;
9083 if (!empty($object->fk_project) && $object->fk_project > 0) {
9084 $project_id = $object->fk_project;
9085 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9086 $project_id = $object->fk_project;
9087 }
9088 if ($project_id > 0) {
9089 // path:class:method:id
9090 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9091 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9092 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9093 }
9094 }
9095 }
9096
9097 if (is_object($object) && $object->element == 'facture') {
9098 '@phan-var-force Facture $object';
9099 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9100 }
9101 if (is_object($object) && $object->element == 'shipping') {
9102 '@phan-var-force Expedition $object';
9103 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9104 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9105 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9106 }
9107 if (is_object($object) && $object->element == 'reception') {
9108 '@phan-var-force Reception $object';
9109 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9110 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9111 }
9112
9113 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9114 '@phan-var-force Contrat $object';
9115 $dateplannedstart = '';
9116 $datenextexpiration = '';
9117 foreach ($object->lines as $line) {
9118 if ($line->date_start > $dateplannedstart) {
9119 $dateplannedstart = $line->date_start;
9120 }
9121 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9122 $datenextexpiration = $line->date_end;
9123 }
9124 }
9125 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9126 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9127 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9128
9129 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9130 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9131 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9132 }
9133 // add substitution variables for ticket
9134 if (is_object($object) && $object->element == 'ticket') {
9135 '@phan-var-force Ticket $object';
9136 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9137 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9138 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9139 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9140 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9141 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9142 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9143 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9144 $userstat = new User($db);
9145 if ($object->fk_user_assign > 0) {
9146 $userstat->fetch($object->fk_user_assign);
9147 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9148 }
9149
9150 if ($object->fk_user_create > 0) {
9151 $userstat->fetch($object->fk_user_create);
9152 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9153 }
9154 }
9155
9156 // Create dynamic tags for __EXTRAFIELD_FIELD__
9157 if ($object->table_element && $object->id > 0) {
9158 if (!is_object($extrafields)) {
9159 $extrafields = new ExtraFields($db);
9160 }
9161 $extrafields->fetch_name_optionals_label($object->table_element, true);
9162
9163 if ($object->fetch_optionals() > 0) {
9164 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9165 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9166 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9167 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9168 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9169 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9170 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9171 $datetime = $object->array_options['options_'.$key];
9172 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9173 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9174 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9175 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9176 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9177 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9178 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9179 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9180 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9181 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9182 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9183 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9184 }
9185 }
9186 }
9187 }
9188 }
9189
9190 // Complete substitution array with the url to make online payment
9191 if (empty($substitutionarray['__REF__'])) {
9192 $paymenturl = '';
9193 } else {
9194 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9195 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9196 $outputlangs->loadLangs(array('paypal', 'other'));
9197
9198 $amounttouse = 0;
9199 $typeforonlinepayment = 'free';
9200 if (is_object($object) && $object->element == 'commande') {
9201 $typeforonlinepayment = 'order';
9202 }
9203 if (is_object($object) && $object->element == 'facture') {
9204 $typeforonlinepayment = 'invoice';
9205 }
9206 if (is_object($object) && $object->element == 'member') {
9207 $typeforonlinepayment = 'member';
9208 if (!empty($object->last_subscription_amount)) {
9209 $amounttouse = $object->last_subscription_amount;
9210 }
9211 }
9212 if (is_object($object) && $object->element == 'contrat') {
9213 $typeforonlinepayment = 'contract';
9214 }
9215 if (is_object($object) && $object->element == 'fichinter') {
9216 $typeforonlinepayment = 'ficheinter';
9217 }
9218
9219 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9220 $paymenturl = $url;
9221 }
9222
9223 if ($object->id > 0) {
9224 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9225 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9226
9227 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9228 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9229 } else {
9230 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9231 }
9232 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9233 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9234 } else {
9235 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9236 }
9237 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9238 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9239 } else {
9240 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9241 }
9242 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9243 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9244 } else {
9245 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9246 }
9247 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9248 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9249 } else {
9250 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9251 }
9252 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9253 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9254 } else {
9255 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9256 }
9257
9258 if (is_object($object) && $object->element == 'propal') {
9259 '@phan-var-force Propal $object';
9260 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9261 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9262 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9263 }
9264 if (is_object($object) && $object->element == 'commande') {
9265 '@phan-var-force Commande $object';
9266 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9267 }
9268 if (is_object($object) && $object->element == 'facture') {
9269 '@phan-var-force Facture $object';
9270 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9271 }
9272 if (is_object($object) && $object->element == 'contrat') {
9273 '@phan-var-force Contrat $object';
9274 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9275 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9276 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9277 }
9278 if (is_object($object) && $object->element == 'fichinter') {
9279 '@phan-var-force Fichinter $object';
9280 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9281 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9282 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9283 }
9284 if (is_object($object) && $object->element == 'supplier_proposal') {
9285 '@phan-var-force SupplierProposal $object';
9286 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9287 }
9288 if (is_object($object) && $object->element == 'invoice_supplier') {
9289 '@phan-var-force FactureFournisseur $object';
9290 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9291 }
9292 if (is_object($object) && $object->element == 'shipping') {
9293 '@phan-var-force Expedition $object';
9294 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9295 }
9296 }
9297
9298 if (is_object($object) && $object->element == 'action') {
9299 '@phan-var-force ActionComm $object';
9300 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9301 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9302 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9303 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9304 }
9305 }
9306 }
9307 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9308 '@phan-var-force Facture|FactureRec $object';
9309 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9310
9311 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
9312 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
9313 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', 0, $outputlangs) : null) : '';
9314 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', 0, $outputlangs) : null) : '';
9315
9316 $already_payed_all = 0;
9317 if (is_object($object) && ($object instanceof Facture)) {
9318 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9319 }
9320
9321 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9322 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9323 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9324
9325 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9326 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9327 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9328
9329 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9330
9331 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9332 $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)) : '';
9333 $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)) : '';
9334
9335 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9336 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9337 }
9338 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9339 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9340 }
9341
9342 // Amount keys formatted in a currency
9343 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9344 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9345 $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) : '';
9346 $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)) : '';
9347 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9348 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9349 }
9350 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9351 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9352 }
9353 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9354 if ($onlykey != 2) {
9355 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9356 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9357 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9358 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9359 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9360 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9361 }
9362 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9363 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9364 }
9365 }
9366
9367 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9368 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9369 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9370 // TODO Add other keys for foreign multicurrency
9371
9372 // For backward compatibility
9373 if ($onlykey != 2) {
9374 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9375 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9376 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9377 }
9378 }
9379
9380
9381 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9382 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9383
9384 $now = dol_now();
9385
9386 $tmp = dol_getdate($now, true);
9387 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9388 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9389 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9390 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9391
9392 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9393
9394 $substitutionarray = array_merge($substitutionarray, array(
9395 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9396 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9397 '__DAY__' => (string) $tmp['mday'],
9398 '__DAY_TEXT__' => $daytext, // Monday
9399 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9400 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9401 '__MONTH__' => (string) $tmp['mon'],
9402 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9403 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9404 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9405 '__YEAR__' => (string) $tmp['year'],
9406 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9407 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9408 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9409 '__NEXT_DAY__' => (string) $tmp4['day'],
9410 '__NEXT_MONTH__' => (string) $tmp5['month'],
9411 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9412 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9413 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9414 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9415 ));
9416 }
9417
9418 if (isModEnabled('multicompany')) {
9419 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9420 }
9421 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9422 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9423 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9424 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9425 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9426 }
9427
9428 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9429
9430 return $substitutionarray;
9431}
9432
9449function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9450{
9451 global $conf, $db, $langs;
9452
9453 if (!is_array($substitutionarray)) {
9454 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9455 }
9456
9457 if (empty($outputlangs)) {
9458 $outputlangs = $langs;
9459 }
9460
9461 // Is initial text HTML or simple text ?
9462 $msgishtml = 0;
9463 if (dol_textishtml($text, 1)) {
9464 $msgishtml = 1;
9465 }
9466
9467 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9468 if (is_object($outputlangs)) {
9469 $reg = array();
9470 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9471 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9472 $tmp = explode('|', $reg[1]);
9473 if (!empty($tmp[1])) {
9474 $outputlangs->load($tmp[1]);
9475 }
9476
9477 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9478
9479 if (empty($converttextinhtmlifnecessary)) {
9480 // convert $newval into HTML is necessary
9481 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9482 } else {
9483 if (! $msgishtml) {
9484 $valueishtml = dol_textishtml($value, 1);
9485 //var_dump("valueishtml=".$valueishtml);
9486
9487 if ($valueishtml) {
9488 $text = dol_htmlentitiesbr($text);
9489 $msgishtml = 1;
9490 }
9491 } else {
9492 $value = dol_nl2br("$value");
9493 }
9494
9495 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9496 }
9497 }
9498 }
9499
9500 // Make substitution for constant keys.
9501 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9502 $reg = array();
9503 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9504 $keyfound = $reg[1];
9505 if (isASecretKey($keyfound)) {
9506 $value = '*****forbidden*****';
9507 } else {
9508 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9509 }
9510
9511 if (empty($converttextinhtmlifnecessary)) {
9512 // convert $newval into HTML is necessary
9513 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9514 } else {
9515 if (! $msgishtml) {
9516 $valueishtml = dol_textishtml($value, 1);
9517
9518 if ($valueishtml) {
9519 $text = dol_htmlentitiesbr($text);
9520 $msgishtml = 1;
9521 }
9522 } else {
9523 $value = dol_nl2br("$value");
9524 }
9525
9526 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9527 }
9528 }
9529
9530 // Make substitution for array $substitutionarray
9531 foreach ($substitutionarray as $key => $value) {
9532 if (!isset($value)) {
9533 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9534 }
9535
9536 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9537 $value = ''; // Protection
9538 }
9539
9540 if (empty($converttextinhtmlifnecessary)) {
9541 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9542 } else {
9543 if (! $msgishtml) {
9544 $valueishtml = dol_textishtml($value, 1);
9545
9546 if ($valueishtml) {
9547 $text = dol_htmlentitiesbr($text);
9548 $msgishtml = 1;
9549 }
9550 } else {
9551 $value = dol_nl2br("$value");
9552 }
9553 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9554 }
9555 }
9556
9557 // TODO Implement the lazyload substitution
9558 /*
9559 add a loop to scan $substitutionarray:
9560 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.
9561 If no, we don't need to make replacement, so we do nothing.
9562 If yes, we can make the substitution:
9563
9564 include_once $path;
9565 $tmpobj = new $class($db);
9566 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9567 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9568 */
9569 $memory_object_list = array();
9570 foreach ($substitutionarray as $key => $value) {
9571 $lazy_load_arr = array();
9572 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9573 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9574 $key_to_substitute = $lazy_load_arr[1];
9575 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9576 $param_arr = explode(':', $value);
9577 // path:class:method:id
9578 if (count($param_arr) == 4) {
9579 $path = $param_arr[0];
9580 $class = $param_arr[1];
9581 $method = $param_arr[2];
9582 $id = (int) $param_arr[3];
9583
9584 // load class file and init object list in memory
9585 if (!isset($memory_object_list[$class])) {
9586 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9587 require_once DOL_DOCUMENT_ROOT . $path;
9588 if (class_exists($class)) {
9589 $memory_object_list[$class] = array(
9590 'list' => array(),
9591 );
9592 }
9593 }
9594 }
9595
9596 // fetch object and set substitution
9597 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9598 if (method_exists($class, $method)) {
9599 if (!isset($memory_object_list[$class]['list'][$id])) {
9600 $tmpobj = new $class($db);
9601 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9602 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9603 $memory_object_list[$class]['list'][$id] = $tmpobj;
9604 } else {
9605 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9606 $tmpobj = $memory_object_list[$class]['list'][$id];
9607 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9608 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9609 }
9610
9611 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9612 }
9613 }
9614 }
9615 }
9616 }
9617 }
9618 }
9619
9620 return $text;
9621}
9622
9635function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9636{
9637 global $conf, $user;
9638
9639 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9640
9641 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9642
9643 // Check if there is external substitution to do, requested by plugins
9644 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9645
9646 foreach ($dirsubstitutions as $reldir) {
9647 $dir = dol_buildpath($reldir, 0);
9648
9649 // Check if directory exists
9650 if (!dol_is_dir($dir)) {
9651 continue;
9652 }
9653
9654 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9655 foreach ($substitfiles as $substitfile) {
9656 $reg = array();
9657 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9658 $module = $reg[1];
9659
9660 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9661 // Include the user's functions file
9662 require_once $dir.$substitfile['name'];
9663 // Call the user's function, and only if it is defined
9664 $function_name = $module."_".$callfunc;
9665 if (function_exists($function_name)) {
9666 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9667 }
9668 }
9669 }
9670 }
9671 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9672 // to list all tags in odt template
9673 $tags = '';
9674 foreach ($substitutionarray as $key => $value) {
9675 $tags .= '{'.$key.'} => '.$value."\n";
9676 }
9677 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9678 }
9679}
9680
9690function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9691{
9692 print get_date_range($date_start, $date_end, $format, $outputlangs);
9693}
9694
9705function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9706{
9707 global $langs;
9708
9709 $out = '';
9710
9711 if (!is_object($outputlangs)) {
9712 $outputlangs = $langs;
9713 }
9714
9715 if ($date_start && $date_end) {
9716 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9717 }
9718 if ($date_start && !$date_end) {
9719 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9720 }
9721 if (!$date_start && $date_end) {
9722 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9723 }
9724
9725 return $out;
9726}
9727
9736function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9737{
9738 global $conf;
9739
9740 $ret = '';
9741 // If order not defined, we use the setup
9742 if ($nameorder < 0) {
9743 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9744 }
9745 if ($nameorder == 1) {
9746 $ret .= $firstname;
9747 if ($firstname && $lastname) {
9748 $ret .= ' ';
9749 }
9750 $ret .= $lastname;
9751 } elseif ($nameorder == 2 || $nameorder == 3) {
9752 $ret .= $firstname;
9753 if (empty($ret) && $nameorder == 3) {
9754 $ret .= $lastname;
9755 }
9756 } else { // 0, 4 or 5
9757 $ret .= $lastname;
9758 if (empty($ret) && $nameorder == 5) {
9759 $ret .= $firstname;
9760 }
9761 if ($nameorder == 0) {
9762 if ($firstname && $lastname) {
9763 $ret .= ' ';
9764 }
9765 $ret .= $firstname;
9766 }
9767 }
9768 return $ret;
9769}
9770
9771
9783function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0)
9784{
9785 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9786 if (!is_array($mesgs)) {
9787 $mesgs = trim((string) $mesgs);
9788 // If mesgs is a not an empty string
9789 if ($mesgs) {
9790 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9791 return;
9792 }
9793 $_SESSION['dol_events'][$style][] = $mesgs;
9794 }
9795 } else {
9796 // If mesgs is an array
9797 foreach ($mesgs as $mesg) {
9798 $mesg = trim((string) $mesg);
9799 if ($mesg) {
9800 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
9801 return;
9802 }
9803 $_SESSION['dol_events'][$style][] = $mesg;
9804 }
9805 }
9806 }
9807}
9808
9821function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0)
9822{
9823 if (empty($mesg) && empty($mesgs)) {
9824 dol_syslog("Try to add a message in stack, but value to add is empty message", LOG_WARNING);
9825 } else {
9826 if ($messagekey) {
9827 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
9828 // TODO
9829 $mesg .= '';
9830 }
9831 if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
9832 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
9833 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
9834 }
9835 if (empty($mesgs)) {
9836 setEventMessage($mesg, $style, $noduplicate);
9837 } else {
9838 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
9839 setEventMessage($mesg, $style, $noduplicate); // Add message string if not already into array
9840 }
9841 setEventMessage($mesgs, $style, $noduplicate);
9842 }
9843 }
9844 }
9845}
9846
9856function dol_htmloutput_events($disabledoutputofmessages = 0)
9857{
9858 // Show mesgs
9859 if (isset($_SESSION['dol_events']['mesgs'])) {
9860 if (empty($disabledoutputofmessages)) {
9861 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
9862 }
9863 unset($_SESSION['dol_events']['mesgs']);
9864 }
9865 // Show errors
9866 if (isset($_SESSION['dol_events']['errors'])) {
9867 if (empty($disabledoutputofmessages)) {
9868 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
9869 }
9870 unset($_SESSION['dol_events']['errors']);
9871 }
9872
9873 // Show warnings
9874 if (isset($_SESSION['dol_events']['warnings'])) {
9875 if (empty($disabledoutputofmessages)) {
9876 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
9877 }
9878 unset($_SESSION['dol_events']['warnings']);
9879 }
9880}
9881
9896function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
9897{
9898 global $conf, $langs;
9899
9900 $ret = 0;
9901 $return = '';
9902 $out = '';
9903 $divstart = $divend = '';
9904
9905 // If inline message with no format, we add it.
9906 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
9907 $divstart = '<div class="'.$style.' clearboth">';
9908 $divend = '</div>';
9909 }
9910
9911 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
9912 $langs->load("errors");
9913 $out .= $divstart;
9914 if (is_array($mesgarray) && count($mesgarray)) {
9915 foreach ($mesgarray as $message) {
9916 $ret++;
9917 $out .= $langs->trans($message);
9918 if ($ret < count($mesgarray)) {
9919 $out .= "<br>\n";
9920 }
9921 }
9922 }
9923 if ($mesgstring) {
9924 $ret++;
9925 $out .= $langs->trans($mesgstring);
9926 }
9927 $out .= $divend;
9928 }
9929
9930 if ($out) {
9931 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
9932 $return = '<script nonce="'.getNonce().'">
9933 $(document).ready(function() {
9934 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
9935 if (block) {
9936 $.dolEventValid("","'.dol_escape_js($out).'");
9937 } else {
9938 /* jnotify(message, preset of message type, keepmessage) */
9939 $.jnotify("'.dol_escape_js($out).'",
9940 "'.($style == "ok" ? 3000 : $style).'",
9941 '.($style == "ok" ? "false" : "true").',
9942 { remove: function (){} } );
9943 }
9944 });
9945 </script>';
9946 } else {
9947 $return = $out;
9948 }
9949 }
9950
9951 return $return;
9952}
9953
9965function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
9966{
9967 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
9968}
9969
9983function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
9984{
9985 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
9986 return;
9987 }
9988
9989 $iserror = 0;
9990 $iswarning = 0;
9991 if (is_array($mesgarray)) {
9992 foreach ($mesgarray as $val) {
9993 if ($val && preg_match('/class="error"/i', $val)) {
9994 $iserror++;
9995 break;
9996 }
9997 if ($val && preg_match('/class="warning"/i', $val)) {
9998 $iswarning++;
9999 break;
10000 }
10001 }
10002 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10003 $iserror++;
10004 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10005 $iswarning++;
10006 }
10007 if ($style == 'error') {
10008 $iserror++;
10009 }
10010 if ($style == 'warning') {
10011 $iswarning++;
10012 }
10013
10014 if ($iserror || $iswarning) {
10015 // Remove div from texts
10016 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10017 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10018 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10019 // Remove div from texts array
10020 if (is_array($mesgarray)) {
10021 $newmesgarray = array();
10022 foreach ($mesgarray as $val) {
10023 if (is_string($val)) {
10024 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10025 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10026 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10027 $newmesgarray[] = $tmpmesgstring;
10028 } else {
10029 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10030 }
10031 }
10032 $mesgarray = $newmesgarray;
10033 }
10034 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10035 } else {
10036 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10037 }
10038}
10039
10051function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10052{
10053 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10054}
10055
10069function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10070{
10071 // Clean parameters
10072 $order = strtolower($order);
10073
10074 if (is_array($array)) {
10075 $sizearray = count($array);
10076 if ($sizearray > 0) {
10077 $temp = array();
10078 foreach (array_keys($array) as $key) {
10079 if (is_object($array[$key])) {
10080 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10081 } else {
10082 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10083 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10084 }
10085 if ($natsort == -1) {
10086 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10087 }
10088 }
10089
10090 if (empty($natsort) || $natsort == -1) {
10091 if ($order == 'asc') {
10092 asort($temp);
10093 } else {
10094 arsort($temp);
10095 }
10096 } else {
10097 if ($case_sensitive) {
10098 natsort($temp);
10099 } else {
10100 natcasesort($temp); // natecasesort is not sensible to case
10101 }
10102 if ($order != 'asc') {
10103 $temp = array_reverse($temp, true);
10104 }
10105 }
10106
10107 $sorted = array();
10108
10109 foreach (array_keys($temp) as $key) {
10110 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10111 }
10112
10113 return $sorted;
10114 }
10115 }
10116 return $array;
10117}
10118
10119
10127function utf8_check($str)
10128{
10129 $str = (string) $str; // Sometimes string is an int.
10130
10131 // We must use here a binary strlen function (so not dol_strlen)
10132 $strLength = strlen($str);
10133 for ($i = 0; $i < $strLength; $i++) {
10134 if (ord($str[$i]) < 0x80) {
10135 continue; // 0bbbbbbb
10136 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10137 $n = 1; // 110bbbbb
10138 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10139 $n = 2; // 1110bbbb
10140 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10141 $n = 3; // 11110bbb
10142 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10143 $n = 4; // 111110bb
10144 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10145 $n = 5; // 1111110b
10146 } else {
10147 return false; // Does not match any model
10148 }
10149 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10150 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10151 return false;
10152 }
10153 }
10154 }
10155 return true;
10156}
10157
10165function utf8_valid($str)
10166{
10167 /* 2 other methods to test if string is utf8
10168 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10169 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10170 */
10171 return preg_match('//u', $str) ? true : false;
10172}
10173
10174
10181function ascii_check($str)
10182{
10183 if (function_exists('mb_check_encoding')) {
10184 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10185 if (!mb_check_encoding($str, 'ASCII')) {
10186 return false;
10187 }
10188 } else {
10189 if (preg_match('/[^\x00-\x7f]/', $str)) {
10190 return false; // Contains a byte > 7f
10191 }
10192 }
10193
10194 return true;
10195}
10196
10197
10205function dol_osencode($str)
10206{
10207 $tmp = ini_get("unicode.filesystem_encoding");
10208 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10209 $tmp = 'iso-8859-1'; // By default for windows
10210 }
10211 if (empty($tmp)) {
10212 $tmp = 'utf-8'; // By default for other
10213 }
10214 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10215 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10216 }
10217
10218 if ($tmp == 'iso-8859-1') {
10219 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10220 }
10221 return $str;
10222}
10223
10224
10240function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10241{
10242 global $conf;
10243
10244 // If key empty
10245 if ($key == '') {
10246 return 0;
10247 }
10248
10249 // Check in cache
10250 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10251 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10252 }
10253
10254 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10255
10256 $sql = "SELECT ".$fieldid." as valuetoget";
10257 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10258 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10259 if (!empty($entityfilter)) {
10260 $sql .= " AND entity IN (".getEntity($tablename).")";
10261 }
10262 if ($filters) {
10263 $sql .= $filters;
10264 }
10265
10266 $resql = $db->query($sql);
10267 if ($resql) {
10268 $obj = $db->fetch_object($resql);
10269 $valuetoget = '';
10270 if ($obj) {
10271 $valuetoget = $obj->valuetoget;
10272 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10273 } else {
10274 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10275 }
10276 $db->free($resql);
10277
10278 return $valuetoget;
10279 } else {
10280 return -1;
10281 }
10282}
10283
10293function isStringVarMatching($var, $regextext, $matchrule = 1)
10294{
10295 if ($matchrule == 1) {
10296 if ($var == 'mainmenu') {
10297 global $mainmenu;
10298 return (preg_match('/^'.$regextext.'/', $mainmenu));
10299 } elseif ($var == 'leftmenu') {
10300 global $leftmenu;
10301 return (preg_match('/^'.$regextext.'/', $leftmenu));
10302 } else {
10303 return 'This variable is not accessible with dol_eval';
10304 }
10305 } else {
10306 return 'This value for matchrule is not implemented';
10307 }
10308}
10309
10310
10320function verifCond($strToEvaluate, $onlysimplestring = '1')
10321{
10322 //print $strToEvaluate."<br>\n";
10323 $rights = true;
10324 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10325 //var_dump($strToEvaluate);
10326 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10327 $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
10328 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10329 //var_dump($rights);
10330 }
10331 return $rights;
10332}
10333
10348function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10349{
10350 // Only this global variables can be read by eval function and returned to caller
10351 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10352 global $db, $langs, $user, $website, $websitepage;
10353 global $action, $mainmenu, $leftmenu;
10354 global $mysoc;
10355 global $objectoffield; // To allow the use of $objectoffield in computed fields
10356
10357 // Old variables used
10358 global $object;
10359 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10360
10361 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10362 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10363 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10364 }
10365
10366 try {
10367 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10368 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10369 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10370 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10371 // 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"
10372
10373 // Check if there is dynamic call (first we check chars are all into use a whitelist chars)
10374 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10375 if ($onlysimplestring == '2') {
10376 $specialcharsallowed .= '[]';
10377 }
10378 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10379 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10380 }
10381 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10382 if ($returnvalue) {
10383 return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s;
10384 } else {
10385 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s, LOG_WARNING);
10386 return '';
10387 }
10388 }
10389
10390 // Check if there is dynamic call (first we use black list patterns)
10391 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10392 if ($returnvalue) {
10393 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;
10394 } else {
10395 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);
10396 return '';
10397 }
10398 }
10399
10400 // Now we check if we try dynamic call (by removing white list pattern of using parenthesis then testing if a parenthesis exists)
10401 $savescheck = '';
10402 $scheck = $s;
10403 while ($scheck && $savescheck != $scheck) {
10404 $savescheck = $scheck;
10405 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10406 $scheck = preg_replace('/^\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10407 $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
10408 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10409 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10410 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10411 }
10412 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10413 if (strpos($scheck, '(') !== false) {
10414 if ($returnvalue) {
10415 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10416 } else {
10417 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);
10418 return '';
10419 }
10420 }
10421
10422 // TODO
10423 // We can exclude $ char that are not:
10424 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10425 }
10426 if (is_array($s) || $s === 'Array') {
10427 if ($returnvalue) {
10428 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10429 } else {
10430 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10431 return '';
10432 }
10433 }
10434
10435 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
10436 if ($returnvalue) {
10437 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
10438 } else {
10439 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
10440 return '';
10441 }
10442 }
10443
10444 if (strpos($s, '`') !== false) {
10445 if ($returnvalue) {
10446 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10447 } else {
10448 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10449 return '';
10450 }
10451 }
10452
10453 // Disallow also concat
10454 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
10455 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10456 if ($returnvalue) {
10457 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10458 } else {
10459 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10460 return '';
10461 }
10462 }
10463 }
10464
10465 // We block use of php exec or php file functions
10466 $forbiddenphpstrings = array('$$', '$_', '}[');
10467 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10468
10469 // We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all)
10470 // We must exclude all functions that allow to execute another function. This includes all function that has a parameter with type "callable" to avoid things
10471 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
10472 $forbiddenphpfunctions = array();
10473 // @phpcs:ignore
10474 $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
10475
10476 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10477 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10478 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
10479
10480 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("array_all", "array_any", "array_diff_ukey", "array_filter", "array_find", "array_find_key", "array_map", "array_reduce", "array_intersect_uassoc", "array_intersect_ukey", "array_walk", "array_walk_recursive"));
10481 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
10482 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("set_error_handler", "set_exception_handler", "libxml_set_external_entity_loader", "register_shutdown_function", "register_tick_function", "unregister_tick_function"));
10483 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
10484 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
10485
10486 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
10487
10488 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10489 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10490 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10491 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace", "mb_ereg_replace_callback")); // function with eval capabilities
10492 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
10493 $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
10494 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10495 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
10496
10497 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10498
10499 $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
10500
10501 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10502
10503 do {
10504 $oldstringtoclean = $s;
10505 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10506 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10507 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10508 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10509 } while ($oldstringtoclean != $s);
10510
10511
10512 if (strpos($s, '__forbiddenstring__') !== false) {
10513 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10514 if ($returnvalue) {
10515 return 'Bad string syntax to evaluate: '.$s;
10516 } else {
10517 dol_syslog('Bad string syntax to evaluate: '.$s);
10518 return '';
10519 }
10520 }
10521
10522 //print $s."<br>\n";
10523 if ($returnvalue) {
10524 if ($hideerrors) {
10525 ob_start(); // An evaluation has no reason to output data
10526 $isObBufferActive = true;
10527 $tmps = @eval('return '.$s.';');
10528 $tmpo = ob_get_clean();
10529 $isObBufferActive = false;
10530 if ($tmpo) {
10531 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10532 }
10533 return $tmps;
10534 } else {
10535 ob_start(); // An evaluation has no reason to output data
10536 $isObBufferActive = true;
10537 $tmps = eval('return '.$s.';');
10538 $tmpo = ob_get_clean();
10539 $isObBufferActive = false;
10540 if ($tmpo) {
10541 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10542 }
10543 return $tmps;
10544 }
10545 } else {
10546 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10547 if ($hideerrors) {
10548 @eval($s);
10549 } else {
10550 eval($s);
10551 }
10552 return '';
10553 }
10554 } catch (Error $e) {
10555 if ($isObBufferActive) {
10556 // Clean up buffer which was left behind due to exception.
10557 $tmpo = ob_get_clean();
10558 $isObBufferActive = false;
10559 }
10560 $error = 'dol_eval try/catch error : ';
10561 $error .= $e->getMessage();
10562 dol_syslog($error, LOG_WARNING);
10563 if ($returnvalue) {
10564 return 'Exception during evaluation: '.$s;
10565 } else {
10566 return '';
10567 }
10568 }
10569}
10570
10578function dol_validElement($element)
10579{
10580 return (trim($element) != '');
10581}
10582
10591function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10592{
10593 if (empty($codelang)) {
10594 return '';
10595 }
10596
10597 if ($codelang == 'auto') {
10598 return '<span class="fa fa-language"></span>';
10599 }
10600
10601 $langtocountryflag = array(
10602 'ar_AR' => '',
10603 'ca_ES' => 'catalonia',
10604 'da_DA' => 'dk',
10605 'fr_CA' => 'mq',
10606 'sv_SV' => 'se',
10607 'sw_SW' => 'unknown',
10608 'AQ' => 'unknown',
10609 'CW' => 'unknown',
10610 'IM' => 'unknown',
10611 'JE' => 'unknown',
10612 'MF' => 'unknown',
10613 'BL' => 'unknown',
10614 'SX' => 'unknown'
10615 );
10616
10617 if (isset($langtocountryflag[$codelang])) {
10618 $flagImage = $langtocountryflag[$codelang];
10619 } else {
10620 $tmparray = explode('_', $codelang);
10621 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10622 }
10623
10624 $morecss = '';
10625 $reg = array();
10626 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10627 $morecss = $reg[1];
10628 $moreatt = "";
10629 }
10630
10631 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10632 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10633}
10634
10642function getLanguageCodeFromCountryCode($countrycode)
10643{
10644 global $mysoc;
10645
10646 if (empty($countrycode)) {
10647 return null;
10648 }
10649
10650 if (strtoupper($countrycode) == 'MQ') {
10651 return 'fr_CA';
10652 }
10653 if (strtoupper($countrycode) == 'SE') {
10654 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10655 }
10656 if (strtoupper($countrycode) == 'CH') {
10657 if ($mysoc->country_code == 'FR') {
10658 return 'fr_CH';
10659 }
10660 if ($mysoc->country_code == 'DE') {
10661 return 'de_CH';
10662 }
10663 if ($mysoc->country_code == 'IT') {
10664 return 'it_CH';
10665 }
10666 }
10667
10668 // Locale list taken from:
10669 // http://stackoverflow.com/questions/3191664/
10670 // list-of-all-locales-and-their-short-codes
10671 $locales = array(
10672 'af-ZA',
10673 'am-ET',
10674 'ar-AE',
10675 'ar-BH',
10676 'ar-DZ',
10677 'ar-EG',
10678 'ar-IQ',
10679 'ar-JO',
10680 'ar-KW',
10681 'ar-LB',
10682 'ar-LY',
10683 'ar-MA',
10684 'ar-OM',
10685 'ar-QA',
10686 'ar-SA',
10687 'ar-SY',
10688 'ar-TN',
10689 'ar-YE',
10690 //'as-IN', // Moved after en-IN
10691 'ba-RU',
10692 'be-BY',
10693 'bg-BG',
10694 'bn-BD',
10695 //'bn-IN', // Moved after en-IN
10696 'bo-CN',
10697 'br-FR',
10698 'ca-ES',
10699 'co-FR',
10700 'cs-CZ',
10701 'cy-GB',
10702 'da-DK',
10703 'de-AT',
10704 'de-CH',
10705 'de-DE',
10706 'de-LI',
10707 'de-LU',
10708 'dv-MV',
10709 'el-GR',
10710 'en-AU',
10711 'en-BZ',
10712 'en-CA',
10713 'en-GB',
10714 'en-IE',
10715 'en-IN',
10716 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10717 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10718 'en-JM',
10719 'en-MY',
10720 'en-NZ',
10721 'en-PH',
10722 'en-SG',
10723 'en-TT',
10724 'en-US',
10725 'en-ZA',
10726 'en-ZW',
10727 'es-AR',
10728 'es-BO',
10729 'es-CL',
10730 'es-CO',
10731 'es-CR',
10732 'es-DO',
10733 'es-EC',
10734 'es-ES',
10735 'es-GT',
10736 'es-HN',
10737 'es-MX',
10738 'es-NI',
10739 'es-PA',
10740 'es-PE',
10741 'es-PR',
10742 'es-PY',
10743 'es-SV',
10744 'es-US',
10745 'es-UY',
10746 'es-VE',
10747 'et-EE',
10748 'eu-ES',
10749 'fa-IR',
10750 'fi-FI',
10751 'fo-FO',
10752 'fr-BE',
10753 'fr-CA',
10754 'fr-CH',
10755 'fr-FR',
10756 'fr-LU',
10757 'fr-MC',
10758 'fy-NL',
10759 'ga-IE',
10760 'gd-GB',
10761 'gl-ES',
10762 'gu-IN',
10763 'he-IL',
10764 'hi-IN',
10765 'hr-BA',
10766 'hr-HR',
10767 'hu-HU',
10768 'hy-AM',
10769 'id-ID',
10770 'ig-NG',
10771 'ii-CN',
10772 'is-IS',
10773 'it-CH',
10774 'it-IT',
10775 'ja-JP',
10776 'ka-GE',
10777 'kk-KZ',
10778 'kl-GL',
10779 'km-KH',
10780 'kn-IN',
10781 'ko-KR',
10782 'ky-KG',
10783 'lb-LU',
10784 'lo-LA',
10785 'lt-LT',
10786 'lv-LV',
10787 'mi-NZ',
10788 'mk-MK',
10789 'ml-IN',
10790 'mn-MN',
10791 'mr-IN',
10792 'ms-BN',
10793 'ms-MY',
10794 'mt-MT',
10795 'nb-NO',
10796 'ne-NP',
10797 'nl-BE',
10798 'nl-NL',
10799 'nn-NO',
10800 'oc-FR',
10801 'or-IN',
10802 'pa-IN',
10803 'pl-PL',
10804 'ps-AF',
10805 'pt-BR',
10806 'pt-PT',
10807 'rm-CH',
10808 'ro-MD',
10809 'ro-RO',
10810 'ru-RU',
10811 'rw-RW',
10812 'sa-IN',
10813 'se-FI',
10814 'se-NO',
10815 'se-SE',
10816 'si-LK',
10817 'sk-SK',
10818 'sl-SI',
10819 'sq-AL',
10820 'sv-FI',
10821 'sv-SE',
10822 'sw-KE',
10823 'ta-IN',
10824 'te-IN',
10825 'th-TH',
10826 'tk-TM',
10827 'tn-ZA',
10828 'tr-TR',
10829 'tt-RU',
10830 'ug-CN',
10831 'uk-UA',
10832 'ur-PK',
10833 'vi-VN',
10834 'wo-SN',
10835 'xh-ZA',
10836 'yo-NG',
10837 'zh-CN',
10838 'zh-HK',
10839 'zh-MO',
10840 'zh-SG',
10841 'zh-TW',
10842 'zu-ZA',
10843 );
10844
10845 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
10846 if (in_array($buildprimarykeytotest, $locales)) {
10847 return strtolower($countrycode).'_'.strtoupper($countrycode);
10848 }
10849
10850 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
10851 foreach ($locales as $locale) {
10852 $locale_language = locale_get_primary_language($locale);
10853 $locale_region = locale_get_region($locale);
10854 if (strtoupper($countrycode) == $locale_region) {
10855 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
10856 return strtolower($locale_language).'_'.strtoupper($locale_region);
10857 }
10858 }
10859 } else {
10860 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
10861 }
10862
10863 return null;
10864}
10865
10896function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
10897{
10898 global $hookmanager, $db;
10899
10900 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
10901 foreach ($conf->modules_parts['tabs'][$type] as $value) {
10902 $values = explode(':', $value);
10903
10904 $reg = array();
10905 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
10906 $newtab = array();
10907 $postab = $h;
10908 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
10909 $str = $values[1];
10910 $posstart = strpos($str, '(');
10911 if ($posstart > 0) {
10912 $posend = strpos($str, ')');
10913 if ($posstart > 0) {
10914 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
10915 if (is_numeric($res1)) {
10916 $postab = (int) $res1;
10917 $values[1] = '+' . substr($str, $posend + 1);
10918 }
10919 }
10920 }
10921 if (count($values) == 6) {
10922 // new declaration with permissions:
10923 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10924 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10925 if ($values[0] != $type) {
10926 continue;
10927 }
10928
10929 if (verifCond($values[4], '2')) {
10930 if ($values[3]) {
10931 if ($filterorigmodule) { // If a filter of module origin has been requested
10932 if (strpos($values[3], '@')) { // This is an external module
10933 if ($filterorigmodule != 'external') {
10934 continue;
10935 }
10936 } else { // This looks a core module
10937 if ($filterorigmodule != 'core') {
10938 continue;
10939 }
10940 }
10941 }
10942 $langs->load($values[3]);
10943 }
10944 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10945 // If label is "SUBSTITUION_..."
10946 $substitutionarray = array();
10947 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10948 $label = make_substitutions($reg[1], $substitutionarray);
10949 } else {
10950 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
10951 $labeltemp = explode(',', $values[2]);
10952 $label = $langs->trans($labeltemp[0]);
10953
10954 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
10955 dol_include_once($labeltemp[2]);
10956 $classtoload = $labeltemp[1];
10957 if (class_exists($classtoload)) {
10958 $obj = new $classtoload($db);
10959 $function = $labeltemp[3];
10960 if ($obj && $function && method_exists($obj, $function)) {
10961 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10962 $nbrec = $obj->$function($object->id, $obj);
10963 if (!empty($nbrec)) {
10964 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
10965 }
10966 }
10967 }
10968 }
10969 }
10970
10971 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
10972 $newtab[1] = $label;
10973 $newtab[2] = str_replace('+', '', $values[1]);
10974 $h++;
10975 } else {
10976 continue;
10977 }
10978 } elseif (count($values) == 5) { // case deprecated
10979 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
10980
10981 if ($values[0] != $type) {
10982 continue;
10983 }
10984 if ($values[3]) {
10985 if ($filterorigmodule) { // If a filter of module origin has been requested
10986 if (strpos($values[3], '@')) { // This is an external module
10987 if ($filterorigmodule != 'external') {
10988 continue;
10989 }
10990 } else { // This looks a core module
10991 if ($filterorigmodule != 'core') {
10992 continue;
10993 }
10994 }
10995 }
10996 $langs->load($values[3]);
10997 }
10998 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10999 $substitutionarray = array();
11000 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11001 $label = make_substitutions($reg[1], $substitutionarray);
11002 } else {
11003 $label = $langs->trans($values[2]);
11004 }
11005
11006 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11007 $newtab[1] = $label;
11008 $newtab[2] = str_replace('+', '', $values[1]);
11009 $h++;
11010 }
11011 // set tab at its position
11012 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11013 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11014 if ($values[0] != $type) {
11015 continue;
11016 }
11017 $tabname = str_replace('-', '', $values[1]);
11018 foreach ($head as $key => $val) {
11019 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11020 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11021 if ($head[$key][2] == $tabname && $condition) {
11022 unset($head[$key]);
11023 break;
11024 }
11025 }
11026 }
11027 }
11028 }
11029
11030 // No need to make a return $head. Var is modified as a reference
11031 if (!empty($hookmanager)) {
11032 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11033 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11034 if ($reshook > 0) { // Hook ask to replace completely the array
11035 $head = $hookmanager->resArray;
11036 } else { // Hook
11037 $head = array_merge($head, $hookmanager->resArray);
11038 }
11039 $h = count($head);
11040 }
11041}
11042
11054function printCommonFooter($zone = 'private')
11055{
11056 global $conf, $hookmanager, $user, $langs;
11057 global $debugbar;
11058 global $action;
11059 global $micro_start_time;
11060
11061 if ($zone == 'private') {
11062 print "\n".'<!-- Common footer for private page -->'."\n";
11063 } else {
11064 print "\n".'<!-- Common footer for public page -->'."\n";
11065 }
11066
11067 // A div to store page_y POST parameter so we can read it using javascript
11068 print "\n<!-- A div to store page_y POST parameter -->\n";
11069 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11070
11071 $parameters = array();
11072 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11073 if (empty($reshook)) {
11074 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11075 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11076 }
11077
11078 print "\n";
11079 if (!empty($conf->use_javascript_ajax)) {
11080 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11081 print '<script>'."\n";
11082 print 'jQuery(document).ready(function() {'."\n";
11083
11084 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11085 print "\n";
11086 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11087 print 'jQuery("li.menuhider").click(function(event) {';
11088 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11089 print ' console.log("We click on .menuhider");'."\n";
11090 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11091 print '});'."\n";
11092 }
11093
11094 // Management of focus and mandatory for fields
11095 if ($action == 'create' || $action == 'add' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))) || ((empty($action) || $action == 'addline') && (preg_match('/card\.php/', $_SERVER["PHP_SELF"])))) {
11096 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11097 $relativepathstring = $_SERVER["PHP_SELF"];
11098 // Clean $relativepathstring
11099 if (constant('DOL_URL_ROOT')) {
11100 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11101 }
11102 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11103 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11104 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11105
11106 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11107 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11108 $qualified = 0;
11109 if ($defkey != '_noquery_') {
11110 $tmpqueryarraytohave = explode('&', $defkey);
11111 $foundintru = 0;
11112 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11113 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11114 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11115 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11116 $foundintru = 1;
11117 }
11118 }
11119 if (!$foundintru) {
11120 $qualified = 1;
11121 }
11122 //var_dump($defkey.'-'.$qualified);
11123 } else {
11124 $qualified = 1;
11125 }
11126
11127 if ($qualified) {
11128 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11129 foreach ($defval as $paramkey => $paramval) {
11130 // Set focus on field
11131 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11132 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11133 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11134 }
11135 }
11136 }
11137 }
11138 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11139 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11140 $qualified = 0;
11141 if ($defkey != '_noquery_') {
11142 $tmpqueryarraytohave = explode('&', $defkey);
11143 $foundintru = 0;
11144 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11145 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11146 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11147 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11148 $foundintru = 1;
11149 }
11150 }
11151 if (!$foundintru) {
11152 $qualified = 1;
11153 }
11154 //var_dump($defkey.'-'.$qualified);
11155 } else {
11156 $qualified = 1;
11157 }
11158
11159 if ($qualified) {
11160 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11161
11162 foreach ($defval as $paramkey => $paramval) {
11163 // Solution 1: Add handler on submit to check if mandatory fields are empty
11164 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11165 print "form.on('submit', function(event) {
11166 var submitter = $(this).find(':submit:focus').get(0);
11167 if (submitter) {
11168 var buttonName = $(submitter).attr('name');
11169 if (buttonName == 'cancel') {
11170 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11171 return true;
11172 }
11173 }
11174
11175 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11176
11177 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11178 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11179
11180 if (tmptypefield == 'textarea') {
11181 // We must instead check the content of ckeditor
11182 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11183 if (tmpeditor) {
11184 tmpvalue = tmpeditor.getData();
11185 console.log('For textarea tmpvalue is '+tmpvalue);
11186 }
11187 }
11188
11189 let tmpvalueisempty = false;
11190 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '') {
11191 tmpvalueisempty = true;
11192 }
11193 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
11194 tmpvalueisempty = true;
11195 }
11196 if (tmpvalueisempty && (buttonName == 'save')) {
11197 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11198 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11199 event.stopPropagation(); // Stop other handlers.
11200 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11201 return false;
11202 }
11203 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11204 return true;
11205 });
11206 \n";
11207
11208 // Solution 2: Add property 'required' on input
11209 // so browser will check value and try to focus on it when submitting the form.
11210 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11211 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11212 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11213 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11214 //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";
11215 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11216 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11217 // Add 'field required' class on closest td for all input elements : input, textarea and select
11218 //print '}, 500);'; // 500 milliseconds delay
11219
11220 // Now set the class "fieldrequired"
11221 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11222 }
11223
11224
11225 // If we submit using the cancel button, we remove the required attributes
11226 print 'jQuery("input[name=\'cancel\']").click(function() {
11227 console.log("We click on cancel button so removed all required attribute");
11228 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11229 });'."\n";
11230 }
11231 }
11232 }
11233 }
11234
11235 print '});'."\n";
11236
11237 // End of tuning
11238 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11239 print "\n";
11240 print "/* JS CODE TO ENABLE to add memory info */\n";
11241 print 'window.console && console.log("';
11242 if (getDolGlobalString('MEMCACHED_SERVER')) {
11243 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11244 }
11245 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11246 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11247 $micro_end_time = microtime(true);
11248 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11249 }
11250
11251 if (function_exists("memory_get_usage")) {
11252 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11253 }
11254 if (function_exists("memory_get_peak_usage")) {
11255 print ' - Real mem peak: '.memory_get_peak_usage(true);
11256 }
11257 if (function_exists("zend_loader_file_encoded")) {
11258 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11259 }
11260 print '");'."\n";
11261 }
11262
11263 print "\n".'</script>'."\n";
11264
11265 // Google Analytics
11266 // TODO Add a hook here
11267 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11268 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11269 foreach ($tmptagarray as $tmptag) {
11270 print "\n";
11271 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11272 print '
11273 <!-- Global site tag (gtag.js) - Google Analytics -->
11274 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11275 <script>
11276 window.dataLayer = window.dataLayer || [];
11277 function gtag(){dataLayer.push(arguments);}
11278 gtag(\'js\', new Date());
11279
11280 gtag(\'config\', \''.trim($tmptag).'\');
11281 </script>';
11282 print "\n";
11283 }
11284 }
11285 }
11286
11287 // Add Xdebug coverage of code
11288 if (defined('XDEBUGCOVERAGE')) {
11289 print_r(xdebug_get_code_coverage());
11290 }
11291
11292 // Add DebugBar data
11293 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11294 if (isset($debugbar['time'])) {
11295 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11296 $debugbar['time']->stopMeasure('pageaftermaster');
11297 }
11298 print '<!-- Output debugbar data -->'."\n";
11299 $renderer = $debugbar->getJavascriptRenderer();
11300 print $renderer->render();
11301 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11302 print "\n";
11303 print "<!-- Start of log output\n";
11304 //print '<div class="hidden">'."\n";
11305 foreach ($conf->logbuffer as $logline) {
11306 print $logline."<br>\n";
11307 }
11308 //print '</div>'."\n";
11309 print "End of log output -->\n";
11310 }
11311 }
11312}
11313
11323function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11324{
11325 if (is_null($string)) {
11326 return array();
11327 }
11328
11329 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11330 // This is a regex string
11331 $newdelimiter = $delimiter;
11332 } else {
11333 // This is a simple string
11334 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11335 $newdelimiter = preg_quote($delimiter, '/');
11336 }
11337
11338 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11339 $ka = array();
11340 foreach ($a as $s) { // each part
11341 if ($s) {
11342 if ($pos = strpos($s, $kv)) { // key/value delimiter
11343 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11344 } else { // key delimiter not found
11345 $ka[] = trim($s);
11346 }
11347 }
11348 }
11349 return $ka;
11350 }
11351
11352 return array();
11353}
11354
11355
11362function dol_set_focus($selector)
11363{
11364 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11365 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11366}
11367
11368
11376function dol_getmypid()
11377{
11378 if (!function_exists('getmypid')) {
11379 return mt_rand(99900000, 99965535);
11380 } else {
11381 return getmypid(); // May be a number on 64 bits (depending on OS)
11382 }
11383}
11384
11385
11407function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11408{
11409 global $db, $langs;
11410
11411 $value = trim($value);
11412
11413 if ($mode == 0) {
11414 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11415 }
11416 if ($mode == 1) {
11417 $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
11418 }
11419
11420 $value = preg_replace('/\s*\|\s*/', '|', $value);
11421
11422 //natural mode search type 3 allow spaces into search ...
11423 if ($mode == 3 || $mode == -3) {
11424 $crits = explode(',', $value);
11425 } else {
11426 $crits = explode(' ', $value);
11427 }
11428 $res = '';
11429 if (!is_array($fields)) {
11430 $fields = array($fields);
11431 }
11432
11433 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11434 foreach ($crits as $crit) { // Loop on each AND criteria
11435 $crit = trim($crit);
11436 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11437 $newres = '';
11438 foreach ($fields as $field) {
11439 if ($mode == 1) {
11440 $tmpcrits = explode('|', $crit);
11441 $i3 = 0; // count the nb of valid criteria added for this current field
11442 foreach ($tmpcrits as $tmpcrit) {
11443 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11444 continue;
11445 }
11446 $tmpcrit = trim($tmpcrit);
11447
11448 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11449
11450 $operator = '=';
11451 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11452
11453 $reg = array();
11454 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11455 if (!empty($reg[1])) {
11456 $operator = $reg[1];
11457 }
11458 if ($newcrit != '') {
11459 $numnewcrit = price2num($newcrit);
11460 if (is_numeric($numnewcrit)) {
11461 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11462 } else {
11463 $newres .= '1 = 2'; // force false, we received a corrupted data
11464 }
11465 $i3++; // a criteria was added to string
11466 }
11467 }
11468 $i2++; // a criteria for 1 more field was added to string
11469 } elseif ($mode == 2 || $mode == -2) {
11470 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11471 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11472 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11473 if ($mode == -2) {
11474 $newres .= ' OR '.$field.' IS NULL';
11475 }
11476 $i2++; // a criteria for 1 more field was added to string
11477 } elseif ($mode == 3 || $mode == -3) {
11478 $tmparray = explode(',', $crit);
11479 if (count($tmparray)) {
11480 $listofcodes = '';
11481 foreach ($tmparray as $val) {
11482 $val = trim($val);
11483 if ($val) {
11484 $listofcodes .= ($listofcodes ? ',' : '');
11485 $listofcodes .= "'".$db->escape($val)."'";
11486 }
11487 }
11488 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
11489 $i2++; // a criteria for 1 more field was added to string
11490 }
11491 if ($mode == -3) {
11492 $newres .= ' OR '.$field.' IS NULL';
11493 }
11494 } elseif ($mode == 4) {
11495 $tmparray = explode(',', $crit);
11496 if (count($tmparray)) {
11497 $listofcodes = '';
11498 foreach ($tmparray as $val) {
11499 $val = trim($val);
11500 if ($val) {
11501 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11502 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11503 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11504 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11505 $newres .= ')';
11506 $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)
11507 }
11508 }
11509 }
11510 } else { // $mode=0
11511 $tmpcrits = explode('|', $crit);
11512 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11513 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11514 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11515 continue;
11516 }
11517 $tmpcrit = trim($tmpcrit);
11518
11519 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11520 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11521 } else {
11522 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11523 }
11524
11525 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11526 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11527 } else {
11528 $tmpcrit2 = $tmpcrit;
11529 $tmpbefore = '%';
11530 $tmpafter = '%';
11531 $tmps = '';
11532
11533 if (preg_match('/^!/', $tmpcrit)) {
11534 $tmps .= $field." NOT LIKE "; // ! as exclude character
11535 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11536 } else {
11537 $tmps .= $field." LIKE ";
11538 }
11539 $tmps .= "'";
11540
11541 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11542 $tmpbefore = '';
11543 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11544 }
11545 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11546 $tmpafter = '';
11547 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11548 }
11549
11550 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11551 $tmps = "(".$tmps;
11552 }
11553 $newres .= $tmps;
11554 $newres .= $tmpbefore;
11555 $newres .= $db->escape($tmpcrit2);
11556 $newres .= $tmpafter;
11557 $newres .= "'";
11558 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11559 $newres .= " OR ".$field." IS NULL)";
11560 }
11561 }
11562
11563 $i3++;
11564 }
11565
11566 $i2++; // a criteria for 1 more field was added to string
11567 }
11568 }
11569
11570 if ($newres) {
11571 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11572 }
11573 $i1++;
11574 }
11575 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11576
11577 return $res;
11578}
11579
11586function showDirectDownloadLink($object)
11587{
11588 global $conf, $langs;
11589
11590 $out = '';
11591 $url = $object->getLastMainDocLink($object->element);
11592
11593 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11594 if ($url) {
11595 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11596 $out .= ajax_autoselect("directdownloadlink", 0);
11597 } else {
11598 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11599 }
11600
11601 return $out;
11602}
11603
11612function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11613{
11614 $dirName = dirname($file);
11615 if ($dirName == '.') {
11616 $dirName = '';
11617 }
11618
11619 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
11620 $fileName = basename($fileName);
11621
11622 if (empty($extImgTarget)) {
11623 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11624 }
11625 if (empty($extImgTarget)) {
11626 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11627 }
11628 if (empty($extImgTarget)) {
11629 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11630 }
11631 if (empty($extImgTarget)) {
11632 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11633 }
11634 if (empty($extImgTarget)) {
11635 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11636 }
11637 if (empty($extImgTarget)) {
11638 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11639 }
11640
11641 if (!$extImgTarget) {
11642 return $file;
11643 }
11644
11645 $subdir = '';
11646 if ($extName) {
11647 $subdir = 'thumbs/';
11648 }
11649
11650 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11651}
11652
11653
11663function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11664{
11665 global $conf, $langs;
11666
11667 if (empty($conf->use_javascript_ajax)) {
11668 return '';
11669 }
11670
11671 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11672
11673 if ($alldata == 1) {
11674 if ($isAllowedForPreview) {
11675 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));
11676 } else {
11677 return array();
11678 }
11679 }
11680
11681 // old behavior, return a string
11682 if ($isAllowedForPreview) {
11683 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11684 $title = $langs->transnoentities("Preview");
11685 //$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().
11686 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
11687
11688 // 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,
11689 // 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.
11690 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
11691 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
11692 } else {
11693 return '';
11694 }
11695}
11696
11697
11706function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11707{
11708 global $langs;
11709 $out = '<script nonce="'.getNonce().'">
11710 jQuery(document).ready(function () {
11711 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11712 });
11713 </script>';
11714 if ($addlink) {
11715 if ($textonlink === 'image') {
11716 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11717 } else {
11718 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11719 }
11720 }
11721 return $out;
11722}
11723
11731function dolIsAllowedForPreview($file)
11732{
11733 // Check .noexe extension in filename
11734 if (preg_match('/\.noexe$/i', $file)) {
11735 return 0;
11736 }
11737
11738 // Check mime types
11739 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11740 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11741 $mime_preview[] = 'svg+xml';
11742 }
11743 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11744 //$mime_preview[]='archive';
11745 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11746 if ($num_mime !== false) {
11747 return 1;
11748 }
11749
11750 // By default, not allowed for preview
11751 return 0;
11752}
11753
11754
11764function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11765{
11766 $mime = $default;
11767 $imgmime = 'other.png';
11768 $famime = 'file-o';
11769 $srclang = '';
11770
11771 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11772
11773 // Plain text files
11774 if (preg_match('/\.txt$/i', $tmpfile)) {
11775 $mime = 'text/plain';
11776 $imgmime = 'text.png';
11777 $famime = 'file-alt';
11778 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
11779 $mime = 'text/richtext';
11780 $imgmime = 'text.png';
11781 $famime = 'file-alt';
11782 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
11783 $mime = 'text/csv';
11784 $imgmime = 'text.png';
11785 $famime = 'file-csv';
11786 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
11787 $mime = 'text/tab-separated-values';
11788 $imgmime = 'text.png';
11789 $famime = 'file-alt';
11790 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
11791 $mime = 'text/plain';
11792 $imgmime = 'text.png';
11793 $famime = 'file-alt';
11794 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
11795 $mime = 'text/plain';
11796 $imgmime = 'text.png';
11797 $srclang = 'ini';
11798 $famime = 'file-alt';
11799 } elseif (preg_match('/\.md$/i', $tmpfile)) {
11800 $mime = 'text/plain';
11801 $imgmime = 'text.png';
11802 $srclang = 'md';
11803 $famime = 'file-alt';
11804 } elseif (preg_match('/\.css$/i', $tmpfile)) {
11805 $mime = 'text/css';
11806 $imgmime = 'css.png';
11807 $srclang = 'css';
11808 $famime = 'file-alt';
11809 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
11810 $mime = 'text/plain';
11811 $imgmime = 'text.png';
11812 $srclang = 'lang';
11813 $famime = 'file-alt';
11814 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
11815 $mime = 'text/plain';
11816 $imgmime = 'text.png';
11817 $famime = 'file-alt';
11818 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
11819 $mime = 'text/html';
11820 $imgmime = 'html.png';
11821 $srclang = 'html';
11822 $famime = 'file-alt';
11823 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
11824 $mime = 'text/xml';
11825 $imgmime = 'other.png';
11826 $srclang = 'xml';
11827 $famime = 'file-alt';
11828 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
11829 $mime = 'text/xml';
11830 $imgmime = 'other.png';
11831 $srclang = 'xaml';
11832 $famime = 'file-alt';
11833 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
11834 $mime = 'text/plain';
11835 $imgmime = 'text.png';
11836 $srclang = 'bas';
11837 $famime = 'file-code';
11838 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
11839 $mime = 'text/plain';
11840 $imgmime = 'text.png';
11841 $srclang = 'c';
11842 $famime = 'file-code';
11843 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
11844 $mime = 'text/plain';
11845 $imgmime = 'text.png';
11846 $srclang = 'cpp';
11847 $famime = 'file-code';
11848 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
11849 $mime = 'text/plain';
11850 $imgmime = 'text.png';
11851 $srclang = 'cs';
11852 $famime = 'file-code';
11853 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
11854 $mime = 'text/plain';
11855 $imgmime = 'text.png';
11856 $srclang = 'h';
11857 $famime = 'file-code';
11858 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
11859 $mime = 'text/plain';
11860 $imgmime = 'text.png';
11861 $srclang = 'java';
11862 $famime = 'file-code';
11863 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
11864 $mime = 'text/plain';
11865 $imgmime = 'php.png';
11866 $srclang = 'php';
11867 $famime = 'file-code';
11868 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
11869 $mime = 'text/plain';
11870 $imgmime = 'php.png';
11871 $srclang = 'php';
11872 $famime = 'file-code';
11873 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
11874 $mime = 'text/plain';
11875 $imgmime = 'pl.png';
11876 $srclang = 'perl';
11877 $famime = 'file-code';
11878 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
11879 $mime = 'text/plain';
11880 $imgmime = 'text.png';
11881 $srclang = 'sql';
11882 $famime = 'file-code';
11883 } elseif (preg_match('/\.js$/i', $tmpfile)) {
11884 $mime = 'text/x-javascript';
11885 $imgmime = 'jscript.png';
11886 $srclang = 'js';
11887 $famime = 'file-code';
11888 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
11889 $mime = 'application/vnd.oasis.opendocument.presentation';
11890 $imgmime = 'ooffice.png';
11891 $famime = 'file-powerpoint';
11892 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
11893 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
11894 $imgmime = 'ooffice.png';
11895 $famime = 'file-excel';
11896 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
11897 $mime = 'application/vnd.oasis.opendocument.text';
11898 $imgmime = 'ooffice.png';
11899 $famime = 'file-word';
11900 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
11901 $mime = 'application/msaccess';
11902 $imgmime = 'mdb.png';
11903 $famime = 'file';
11904 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
11905 $mime = 'application/msword';
11906 $imgmime = 'doc.png';
11907 $famime = 'file-word';
11908 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
11909 $mime = 'application/msword';
11910 $imgmime = 'doc.png';
11911 $famime = 'file-word';
11912 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
11913 $mime = 'application/vnd.ms-excel';
11914 $imgmime = 'xls.png';
11915 $famime = 'file-excel';
11916 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
11917 $mime = 'application/vnd.ms-excel';
11918 $imgmime = 'xls.png';
11919 $famime = 'file-excel';
11920 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
11921 $mime = 'application/vnd.ms-excel';
11922 $imgmime = 'xls.png';
11923 $famime = 'file-excel';
11924 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
11925 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
11926 $imgmime = 'xls.png';
11927 $famime = 'file-excel';
11928 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
11929 $mime = 'application/vnd.ms-powerpoint';
11930 $imgmime = 'ppt.png';
11931 $famime = 'file-powerpoint';
11932 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
11933 $mime = 'application/x-mspowerpoint';
11934 $imgmime = 'ppt.png';
11935 $famime = 'file-powerpoint';
11936 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
11937 $mime = 'application/pdf';
11938 $imgmime = 'pdf.png';
11939 $famime = 'file-pdf';
11940 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
11941 $mime = 'text/x-bat';
11942 $imgmime = 'script.png';
11943 $srclang = 'dos';
11944 $famime = 'file-code';
11945 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
11946 $mime = 'text/x-sh';
11947 $imgmime = 'script.png';
11948 $srclang = 'bash';
11949 $famime = 'file-code';
11950 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
11951 $mime = 'text/x-ksh';
11952 $imgmime = 'script.png';
11953 $srclang = 'bash';
11954 $famime = 'file-code';
11955 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
11956 $mime = 'text/x-bash';
11957 $imgmime = 'script.png';
11958 $srclang = 'bash';
11959 $famime = 'file-code';
11960 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
11961 $mime = 'image/x-icon';
11962 $imgmime = 'image.png';
11963 $famime = 'file-image';
11964 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
11965 $mime = 'image/jpeg';
11966 $imgmime = 'image.png';
11967 $famime = 'file-image';
11968 } elseif (preg_match('/\.png$/i', $tmpfile)) {
11969 $mime = 'image/png';
11970 $imgmime = 'image.png';
11971 $famime = 'file-image';
11972 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
11973 $mime = 'image/gif';
11974 $imgmime = 'image.png';
11975 $famime = 'file-image';
11976 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
11977 $mime = 'image/bmp';
11978 $imgmime = 'image.png';
11979 $famime = 'file-image';
11980 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
11981 $mime = 'image/tiff';
11982 $imgmime = 'image.png';
11983 $famime = 'file-image';
11984 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
11985 $mime = 'image/svg+xml';
11986 $imgmime = 'image.png';
11987 $famime = 'file-image';
11988 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
11989 $mime = 'image/webp';
11990 $imgmime = 'image.png';
11991 $famime = 'file-image';
11992 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
11993 $mime = 'text/calendar';
11994 $imgmime = 'other.png';
11995 $famime = 'file-alt';
11996 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
11997 $mime = 'text/calendar';
11998 $imgmime = 'other.png';
11999 $famime = 'file-alt';
12000 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12001 $mime = 'application/x-bittorrent';
12002 $imgmime = 'other.png';
12003 $famime = 'file-o';
12004 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12005 $mime = 'audio';
12006 $imgmime = 'audio.png';
12007 $famime = 'file-audio';
12008 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12009 $mime = 'video/mp4';
12010 $imgmime = 'video.png';
12011 $famime = 'file-video';
12012 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12013 $mime = 'video/ogg';
12014 $imgmime = 'video.png';
12015 $famime = 'file-video';
12016 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12017 $mime = 'video/webm';
12018 $imgmime = 'video.png';
12019 $famime = 'file-video';
12020 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12021 $mime = 'video/x-msvideo';
12022 $imgmime = 'video.png';
12023 $famime = 'file-video';
12024 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12025 $mime = 'video/divx';
12026 $imgmime = 'video.png';
12027 $famime = 'file-video';
12028 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12029 $mime = 'video/xvid';
12030 $imgmime = 'video.png';
12031 $famime = 'file-video';
12032 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12033 $mime = 'video';
12034 $imgmime = 'video.png';
12035 $famime = 'file-video';
12036 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12037 // application/xxx where zzz is zip, ...
12038 $mime = 'archive';
12039 $imgmime = 'archive.png';
12040 $famime = 'file-archive';
12041 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12042 $mime = 'application/octet-stream';
12043 $imgmime = 'other.png';
12044 $famime = 'file-o';
12045 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12046 $mime = 'library';
12047 $imgmime = 'library.png';
12048 $famime = 'file-o';
12049 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
12050 $mime = 'error';
12051 $imgmime = 'error.png';
12052 $famime = 'file-alt';
12053 }
12054
12055 // Return mimetype string
12056 switch ((int) $mode) {
12057 case 1:
12058 $tmp = explode('/', $mime);
12059 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12060 case 2:
12061 return $imgmime;
12062 case 3:
12063 return $srclang;
12064 case 4:
12065 return $famime;
12066 }
12067 return $mime;
12068}
12069
12081function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12082{
12083 global $conf, $db;
12084
12085 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12086
12087 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12088
12089 if (is_null($dictvalues)) {
12090 $dictvalues = array();
12091
12092 $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
12093 if ($checkentity) {
12094 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12095 }
12096
12097 $resql = $db->query($sql);
12098 if ($resql) {
12099 while ($obj = $db->fetch_object($resql)) {
12100 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12101 }
12102 } else {
12103 dol_print_error($db);
12104 }
12105
12106 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12107 }
12108
12109 if (!empty($dictvalues[$id])) {
12110 // Found
12111 $tmp = $dictvalues[$id];
12112 return (property_exists($tmp, $field) ? $tmp->$field : '');
12113 } else {
12114 // Not found
12115 return '';
12116 }
12117}
12118
12125function colorIsLight($stringcolor)
12126{
12127 $stringcolor = str_replace('#', '', $stringcolor);
12128 $res = -1;
12129 if (!empty($stringcolor)) {
12130 $res = 0;
12131 $tmp = explode(',', $stringcolor);
12132 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12133 $r = $tmp[0];
12134 $g = $tmp[1];
12135 $b = $tmp[2];
12136 } else {
12137 $hexr = $stringcolor[0].$stringcolor[1];
12138 $hexg = $stringcolor[2].$stringcolor[3];
12139 $hexb = $stringcolor[4].$stringcolor[5];
12140 $r = hexdec($hexr);
12141 $g = hexdec($hexg);
12142 $b = hexdec($hexb);
12143 }
12144 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12145 if ($bright > 0.6) {
12146 $res = 1;
12147 }
12148 }
12149 return $res;
12150}
12151
12160function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12161{
12162 global $conf;
12163
12164 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12165 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12166 if (empty($menuentry['enabled'])) {
12167 return 0; // Entry disabled by condition
12168 }
12169 if ($type_user && $menuentry['module']) {
12170 $tmploops = explode('|', $menuentry['module']);
12171 $found = 0;
12172 foreach ($tmploops as $tmploop) {
12173 if (in_array($tmploop, $listofmodulesforexternal)) {
12174 $found++;
12175 break;
12176 }
12177 }
12178 if (!$found) {
12179 return 0; // Entry is for menus all excluded to external users
12180 }
12181 }
12182 if (!$menuentry['perms'] && $type_user) {
12183 return 0; // No permissions and user is external
12184 }
12185 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12186 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12187 }
12188 if (!$menuentry['perms']) {
12189 return 2; // No permissions and user is external
12190 }
12191 return 1;
12192}
12193
12201function roundUpToNextMultiple($n, $x = 5)
12202{
12203 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12204 return (int) $result;
12205}
12206
12218function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12219{
12220 $csstouse = 'badge';
12221 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12222 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12223 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12224
12225 $attr = array(
12226 'class' => $csstouse
12227 );
12228
12229 if (empty($html)) {
12230 $html = $label;
12231 }
12232
12233 if (!empty($url)) {
12234 $attr['href'] = $url;
12235 }
12236
12237 if ($mode === 'dot') {
12238 $attr['class'] .= ' classfortooltip';
12239 $attr['title'] = $html;
12240 $attr['aria-label'] = $label;
12241 $html = '';
12242 }
12243
12244 // Override attr
12245 if (!empty($params['attr']) && is_array($params['attr'])) {
12246 foreach ($params['attr'] as $key => $value) {
12247 if ($key == 'class') {
12248 $attr['class'] .= ' '.$value;
12249 } elseif ($key == 'classOverride') {
12250 $attr['class'] = $value;
12251 } else {
12252 $attr[$key] = $value;
12253 }
12254 }
12255 }
12256
12257 // TODO: add hook
12258
12259 // escape all attribute
12260 $attr = array_map('dol_escape_htmltag', $attr);
12261
12262 $TCompiledAttr = array();
12263 foreach ($attr as $key => $value) {
12264 $TCompiledAttr[] = $key.'="'.$value.'"';
12265 }
12266
12267 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12268
12269 $tag = !empty($url) ? 'a' : 'span';
12270
12271 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12272}
12273
12274
12287function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12288{
12289 global $conf;
12290
12291 $return = '';
12292 $dolGetBadgeParams = array();
12293
12294 if (!empty($params['badgeParams'])) {
12295 $dolGetBadgeParams = $params['badgeParams'];
12296 }
12297
12298 // TODO : add a hook
12299 if ($displayMode == 0) {
12300 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12301 } elseif ($displayMode == 1) {
12302 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12303 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12304 // Use status with images (for backward compatibility)
12305 $return = '';
12306 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12307 $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>' : '');
12308
12309 // For small screen, we always use the short label instead of long label.
12310 if (!empty($conf->dol_optimize_smallscreen)) {
12311 if ($displayMode == 0) {
12312 $displayMode = 1;
12313 } elseif ($displayMode == 4) {
12314 $displayMode = 2;
12315 } elseif ($displayMode == 6) {
12316 $displayMode = 5;
12317 }
12318 }
12319
12320 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12321 $statusImg = array(
12322 'status0' => 'statut0',
12323 'status1' => 'statut1',
12324 'status2' => 'statut2',
12325 'status3' => 'statut3',
12326 'status4' => 'statut4',
12327 'status5' => 'statut5',
12328 'status6' => 'statut6',
12329 'status7' => 'statut7',
12330 'status8' => 'statut8',
12331 'status9' => 'statut9'
12332 );
12333
12334 if (!empty($statusImg[$statusType])) {
12335 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12336 } else {
12337 $htmlImg = img_picto($statusLabel, $statusType);
12338 }
12339
12340 if ($displayMode === 2) {
12341 $return = $htmlImg.' '.$htmlLabelShort;
12342 } elseif ($displayMode === 3) {
12343 $return = $htmlImg;
12344 } elseif ($displayMode === 4) {
12345 $return = $htmlImg.' '.$htmlLabel;
12346 } elseif ($displayMode === 5) {
12347 $return = $htmlLabelShort.' '.$htmlImg;
12348 } else { // $displayMode >= 6
12349 $return = $htmlLabel.' '.$htmlImg;
12350 }
12351 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12352 // Use new badge
12353 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12354
12355 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12356 if (empty($dolGetBadgeParams['attr']['title'])) {
12357 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12358 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12359 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12360 // And if we use tooltip, we can output title in HTML
12361 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12362 }
12363
12364 if ($displayMode == 3) {
12365 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12366 } elseif ($displayMode === 5) {
12367 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12368 } else {
12369 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12370 }
12371 }
12372
12373 return $return;
12374}
12375
12376
12415function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12416{
12417 global $hookmanager, $action, $object, $langs;
12418
12419 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12420 if (is_array($url)) {
12421 // Loop on $url array to remove entries of disabled modules
12422 foreach ($url as $key => $subbutton) {
12423 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12424 unset($url[$key]);
12425 }
12426 }
12427
12428 $out = '';
12429
12430 if (isset($params["areDropdownButtons"]) && $params["areDropdownButtons"] === false) {
12431 foreach ($url as $button) {
12432 if (!empty($button['lang'])) {
12433 $langs->load($button['lang']);
12434 }
12435 $label = $langs->trans($button['label']);
12436 $text = $button['text'] ?? '';
12437 $actionType = $button['actionType'] ?? '';
12438 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12439 $id = $button['id'] ?? '';
12440 $userRight = $button['perm'] ?? 1;
12441 $button['params'] = $button['params'] ?? [];
12442
12443 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12444 }
12445 return $out;
12446 }
12447
12448 if (count($url) > 1) {
12449 $out .= '<div class="dropdown inline-block dropdown-holder">';
12450 $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>';
12451 $out .= '<div class="dropdown-content">';
12452 foreach ($url as $subbutton) {
12453 if (!empty($subbutton['lang'])) {
12454 $langs->load($subbutton['lang']);
12455 }
12456
12457 if (!empty($subbutton['urlraw'])) {
12458 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12459 } else {
12460 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12461 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12462 }
12463
12464 $subbuttonparam = array();
12465 if (!empty($subbutton['attr'])) {
12466 $subbuttonparam['attr'] = $subbutton['attr'];
12467 }
12468 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown']??false) : $params['isDropDown']);
12469
12470 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12471 }
12472 $out .= "</div>";
12473 $out .= "</div>";
12474 } else {
12475 foreach ($url as $subbutton) { // Should loop on 1 record only
12476 if (!empty($subbutton['lang'])) {
12477 $langs->load($subbutton['lang']);
12478 }
12479
12480 if (!empty($subbutton['urlraw'])) {
12481 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12482 } else {
12483 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12484 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12485 }
12486
12487 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12488 }
12489 }
12490
12491 return $out;
12492 }
12493
12494 // Here, $url is a simple link
12495
12496 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12497 $class = "dropdown-item";
12498 } else {
12499 $class = 'butAction';
12500 if ($actionType == 'danger' || $actionType == 'delete') {
12501 $class = 'butActionDelete';
12502 if (!empty($url) && strpos($url, 'token=') === false) {
12503 $url .= '&token='.newToken();
12504 }
12505 }
12506 }
12507 $attr = array(
12508 'class' => $class,
12509 'href' => empty($url) ? '' : $url,
12510 'title' => $label
12511 );
12512
12513 if (empty($text)) {
12514 $text = $label;
12515 $attr['title'] = ''; // if html not set, leave label on title is redundant
12516 } else {
12517 $attr['title'] = $label;
12518 $attr['aria-label'] = $label;
12519 }
12520
12521 if (empty($userRight)) {
12522 $attr['class'] = 'butActionRefused';
12523 $attr['href'] = '';
12524 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12525 $attr['title'] = ($attr['title'] ? $attr['title'].'<br>' : '').$langs->trans('NotEnoughPermissions');
12526 }
12527
12528 if (!empty($id)) {
12529 $attr['id'] = $id;
12530 }
12531
12532 // Override attr
12533 if (!empty($params['attr']) && is_array($params['attr'])) {
12534 foreach ($params['attr'] as $key => $value) {
12535 if ($key == 'class') {
12536 $attr['class'] .= ' '.$value;
12537 } elseif ($key == 'classOverride') {
12538 $attr['class'] = $value;
12539 } else {
12540 $attr[$key] = $value;
12541 }
12542 }
12543 }
12544
12545 // automatic add tooltip when title is detected
12546 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12547 $attr['class'] .= ' classfortooltip';
12548 }
12549
12550 // Js Confirm button
12551 if ($userRight && !empty($params['confirm'])) {
12552 if (!is_array($params['confirm'])) {
12553 $params['confirm'] = array();
12554 }
12555
12556 if (empty($params['confirm']['url'])) {
12557 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12558 }
12559
12560 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12561 $attr['data-confirm-url'] = $params['confirm']['url'];
12562 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12563 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12564 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12565 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12566 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12567 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12568
12569 $attr['class'] .= ' butActionConfirm';
12570 }
12571
12572 if (isset($attr['href']) && empty($attr['href'])) {
12573 unset($attr['href']);
12574 }
12575
12576 // Escape all attributes
12577 if (!empty($params['use_unsecured_unescapedattr'])) { // Not recommended.
12578 if (is_array($params['use_unsecured_unescapedattr'])) {
12579 foreach ($attr as $attrK => $attrV) {
12580 if (in_array($attrK, $params['use_unsecured_unescapedattr'])) {
12581 $attr[$attrK] = dol_htmlentities($attrV, ENT_QUOTES | ENT_SUBSTITUTE);
12582 } else {
12583 $attr[$attrK] = dolPrintHTMLForAttribute($attrV);
12584 }
12585 }
12586 } else {
12587 $attr = array_map('dol_htmlentities', $attr);
12588 }
12589 } else {
12590 $attr = array_map('dolPrintHTMLForAttribute', $attr);
12591 }
12592
12593 $TCompiledAttr = array();
12594 foreach ($attr as $key => $value) {
12595 $TCompiledAttr[] = $key.'= "'.$value.'"';
12596 }
12597
12598 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12599
12600 $tag = !empty($attr['href']) ? 'a' : 'span';
12601
12602
12603 $parameters = array(
12604 'TCompiledAttr' => $TCompiledAttr, // array
12605 'compiledAttributes' => $compiledAttributes, // string
12606 'attr' => $attr,
12607 'tag' => $tag,
12608 'label' => $label,
12609 'html' => $text,
12610 'actionType' => $actionType,
12611 'url' => $url,
12612 'id' => $id,
12613 'userRight' => $userRight,
12614 'params' => $params
12615 );
12616
12617 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12618 if ($reshook < 0) {
12619 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12620 }
12621
12622 if (empty($reshook)) {
12623 if (dol_textishtml($text)) { // If content already HTML encoded
12624 return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
12625 } else {
12626 return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
12627 }
12628 } else {
12629 return $hookmanager->resPrint;
12630 }
12631}
12632
12633
12642function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
12643{
12644 if (empty($url)) {
12645 return '';
12646 }
12647
12648 $parsedUrl = parse_url($url);
12649 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
12650 return $url;
12651 }
12652
12653 if (!empty($parsedUrl['query'])) {
12654 // Use parse_str() function to parse the string passed via URL
12655 parse_str($parsedUrl['query'], $urlQuery);
12656 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
12657 $url.= '&amp;backtopage='.urlencode($params['backtopage']);
12658 }
12659 }
12660
12661 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
12662 $url = DOL_URL_ROOT.$url;
12663 }
12664
12665 return $url;
12666}
12667
12668
12675function dolGetButtonTitleSeparator($moreClass = "")
12676{
12677 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12678}
12679
12686function getFieldErrorIcon($fieldValidationErrorMsg)
12687{
12688 $out = '';
12689 if (!empty($fieldValidationErrorMsg)) {
12690 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12691 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12692 $out .= '</span>';
12693 }
12694
12695 return $out;
12696}
12697
12710function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12711{
12712 global $langs, $conf, $user;
12713
12714 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12715 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12716 return '';
12717 }
12718
12719 $class = 'btnTitle';
12720 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12721 $class .= ' btnTitlePlus';
12722 }
12723 $useclassfortooltip = 1;
12724
12725 if (!empty($params['morecss'])) {
12726 $class .= ' '.$params['morecss'];
12727 }
12728
12729 $attr = array(
12730 'class' => $class,
12731 'href' => empty($url) ? '' : $url
12732 );
12733
12734 if (!empty($helpText)) {
12735 $attr['title'] = dol_escape_htmltag($helpText);
12736 } elseif (empty($attr['title']) && $label) {
12737 $attr['title'] = $label;
12738 $useclassfortooltip = 0;
12739 }
12740
12741 if ($status == 2) {
12742 $attr['class'] .= ' btnTitleSelected';
12743 } elseif ($status <= 0) {
12744 $attr['class'] .= ' refused';
12745
12746 $attr['href'] = '';
12747
12748 if ($status == -1) { // disable
12749 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12750 } elseif ($status == 0) { // Not enough permissions
12751 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12752 }
12753 }
12754
12755 if (!empty($attr['title']) && $useclassfortooltip) {
12756 $attr['class'] .= ' classfortooltip';
12757 }
12758
12759 if (!empty($id)) {
12760 $attr['id'] = $id;
12761 }
12762
12763 // Override attr
12764 if (!empty($params['attr']) && is_array($params['attr'])) {
12765 foreach ($params['attr'] as $key => $value) {
12766 if ($key == 'class') {
12767 $attr['class'] .= ' '.$value;
12768 } elseif ($key == 'classOverride') {
12769 $attr['class'] = $value;
12770 } else {
12771 $attr[$key] = $value;
12772 }
12773 }
12774 }
12775
12776 if (isset($attr['href']) && empty($attr['href'])) {
12777 unset($attr['href']);
12778 }
12779
12780 // TODO : add a hook
12781
12782 // escape all attribute
12783 $attr = array_map('dol_escape_htmltag', $attr);
12784
12785 $TCompiledAttr = array();
12786 foreach ($attr as $key => $value) {
12787 $TCompiledAttr[] = $key.'="'.$value.'"';
12788 }
12789
12790 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12791
12792 $tag = (empty($attr['href']) ? 'span' : 'a');
12793
12794 $button = '<'.$tag.' '.$compiledAttributes.'>';
12795 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
12796 if (!empty($params['forcenohideoftext'])) {
12797 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
12798 }
12799 $button .= '</'.$tag.'>';
12800
12801 return $button;
12802}
12803
12813function getElementProperties($elementType)
12814{
12815 global $conf, $db, $hookmanager;
12816
12817 $regs = array();
12818
12819 //$element_type='facture';
12820
12821 $classfile = $classname = $classpath = $subdir = $dir_output = '';
12822
12823 // Parse element/subelement
12824 $module = $elementType;
12825 $element = $elementType;
12826 $subelement = $elementType;
12827 $table_element = $elementType;
12828
12829 // If we ask a resource form external module (instead of default path)
12830 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
12831 $element = $subelement = $regs[1];
12832 $module = $regs[2];
12833 }
12834
12835 // If we ask a resource for a string with an element and a subelement
12836 // Example 'project_task'
12837 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
12838 $module = $element = $regs[1];
12839 $subelement = $regs[2];
12840 }
12841
12842 // Object lines will use parent classpath and module ref
12843 if (substr($elementType, -3) == 'det') {
12844 $module = preg_replace('/det$/', '', $element);
12845 $subelement = preg_replace('/det$/', '', $subelement);
12846 $classpath = $module.'/class';
12847 $classfile = $module;
12848 $classname = preg_replace('/det$/', 'Line', $element);
12849 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
12850 $classname = preg_replace('/det$/', 'Ligne', $element);
12851 }
12852 }
12853 // For compatibility and to work with non standard path
12854 if ($elementType == "action" || $elementType == "actioncomm") {
12855 $classpath = 'comm/action/class';
12856 $subelement = 'Actioncomm';
12857 $module = 'agenda';
12858 $table_element = 'actioncomm';
12859 } elseif ($elementType == 'cronjob') {
12860 $classpath = 'cron/class';
12861 $module = 'cron';
12862 $table_element = 'cron';
12863 } elseif ($elementType == 'adherent_type') {
12864 $classpath = 'adherents/class';
12865 $classfile = 'adherent_type';
12866 $module = 'adherent';
12867 $subelement = 'adherent_type';
12868 $classname = 'AdherentType';
12869 $table_element = 'adherent_type';
12870 } elseif ($elementType == 'bank_account') {
12871 $classpath = 'compta/bank/class';
12872 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
12873 $classfile = 'account';
12874 $classname = 'Account';
12875 } elseif ($elementType == 'category') {
12876 $classpath = 'categories/class';
12877 $module = 'categorie';
12878 $subelement = 'categorie';
12879 $table_element = 'categorie';
12880 } elseif ($elementType == 'contact') {
12881 $classpath = 'contact/class';
12882 $classfile = 'contact';
12883 $module = 'societe';
12884 $subelement = 'contact';
12885 $table_element = 'socpeople';
12886 } elseif ($elementType == 'inventory') {
12887 $module = 'product';
12888 $classpath = 'product/inventory/class';
12889 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
12890 $module = 'stock';
12891 $classpath = 'product/stock/class';
12892 $classfile = 'entrepot';
12893 $classname = 'Entrepot';
12894 $table_element = 'entrepot';
12895 } elseif ($elementType == 'project') {
12896 $classpath = 'projet/class';
12897 $module = 'projet';
12898 $table_element = 'projet';
12899 } elseif ($elementType == 'project_task') {
12900 $classpath = 'projet/class';
12901 $module = 'projet';
12902 $subelement = 'task';
12903 $table_element = 'projet_task';
12904 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
12905 $classpath = 'compta/facture/class';
12906 $module = 'facture';
12907 $subelement = 'facture';
12908 $table_element = 'facture';
12909 } elseif ($elementType == 'facturerec') {
12910 $classpath = 'compta/facture/class';
12911 $module = 'facture';
12912 $classname = 'FactureRec';
12913 } elseif ($elementType == 'commande' || $elementType == 'order') {
12914 $classpath = 'commande/class';
12915 $module = 'commande';
12916 $subelement = 'commande';
12917 $table_element = 'commande';
12918 } elseif ($elementType == 'propal') {
12919 $classpath = 'comm/propal/class';
12920 $table_element = 'propal';
12921 } elseif ($elementType == 'shipping') {
12922 $classpath = 'expedition/class';
12923 $classfile = 'expedition';
12924 $classname = 'Expedition';
12925 $module = 'expedition';
12926 $table_element = 'expedition';
12927 } elseif ($elementType == 'delivery_note') {
12928 $classpath = 'delivery/class';
12929 $subelement = 'delivery';
12930 $module = 'expedition';
12931 } elseif ($elementType == 'delivery') {
12932 $classpath = 'delivery/class';
12933 $subelement = 'delivery';
12934 $module = 'expedition';
12935 } elseif ($elementType == 'supplier_proposal') {
12936 $classpath = 'supplier_proposal/class';
12937 $module = 'supplier_proposal';
12938 $element = 'supplierproposal';
12939 $classfile = 'supplier_proposal';
12940 $subelement = 'supplierproposal';
12941 } elseif ($elementType == 'contract') {
12942 $classpath = 'contrat/class';
12943 $module = 'contrat';
12944 $subelement = 'contrat';
12945 $table_element = 'contract';
12946 } elseif ($elementType == 'mailing') {
12947 $classpath = 'comm/mailing/class';
12948 $module = 'mailing';
12949 $classfile = 'mailing';
12950 $classname = 'Mailing';
12951 $subelement = '';
12952 } elseif ($elementType == 'member' || $elementType == 'adherent') {
12953 $classpath = 'adherents/class';
12954 $module = 'adherent';
12955 $subelement = 'adherent';
12956 $table_element = 'adherent';
12957 } elseif ($elementType == 'usergroup') {
12958 $classpath = 'user/class';
12959 $module = 'user';
12960 } elseif ($elementType == 'mo') {
12961 $classpath = 'mrp/class';
12962 $classfile = 'mo';
12963 $classname = 'Mo';
12964 $module = 'mrp';
12965 $subelement = '';
12966 $table_element = 'mrp_mo';
12967 } elseif ($elementType == 'cabinetmed_cons') {
12968 $classpath = 'cabinetmed/class';
12969 $module = 'cabinetmed';
12970 $subelement = 'cabinetmedcons';
12971 $table_element = 'cabinetmedcons';
12972 } elseif ($elementType == 'fichinter') {
12973 $classpath = 'fichinter/class';
12974 $module = 'ficheinter';
12975 $subelement = 'fichinter';
12976 $table_element = 'fichinter';
12977 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
12978 $classpath = 'resource/class';
12979 $module = 'resource';
12980 $subelement = 'dolresource';
12981 $table_element = 'resource';
12982 } elseif ($elementType == 'propaldet') {
12983 $classpath = 'comm/propal/class';
12984 $module = 'propal';
12985 $subelement = 'propaleligne';
12986 } elseif ($elementType == 'opensurvey_sondage') {
12987 $classpath = 'opensurvey/class';
12988 $module = 'opensurvey';
12989 $subelement = 'opensurveysondage';
12990 } elseif ($elementType == 'order_supplier') {
12991 $classpath = 'fourn/class';
12992 $module = 'fournisseur';
12993 $classfile = 'fournisseur.commande';
12994 $element = 'order_supplier';
12995 $subelement = '';
12996 $classname = 'CommandeFournisseur';
12997 $table_element = 'commande_fournisseur';
12998 } elseif ($elementType == 'commande_fournisseurdet') {
12999 $classpath = 'fourn/class';
13000 $module = 'fournisseur';
13001 $classfile = 'fournisseur.commande';
13002 $element = 'commande_fournisseurdet';
13003 $subelement = '';
13004 $classname = 'CommandeFournisseurLigne';
13005 $table_element = 'commande_fournisseurdet';
13006 } elseif ($elementType == 'invoice_supplier') {
13007 $classpath = 'fourn/class';
13008 $module = 'fournisseur';
13009 $classfile = 'fournisseur.facture';
13010 $element = 'invoice_supplier';
13011 $subelement = '';
13012 $classname = 'FactureFournisseur';
13013 $table_element = 'facture_fourn';
13014 } elseif ($elementType == "service") {
13015 $classpath = 'product/class';
13016 $subelement = 'product';
13017 $table_element = 'product';
13018 } elseif ($elementType == 'salary') {
13019 $classpath = 'salaries/class';
13020 $module = 'salaries';
13021 } elseif ($elementType == 'payment_salary') {
13022 $classpath = 'salaries/class';
13023 $classfile = 'paymentsalary';
13024 $classname = 'PaymentSalary';
13025 $module = 'salaries';
13026 } elseif ($elementType == 'productlot') {
13027 $module = 'productbatch';
13028 $classpath = 'product/stock/class';
13029 $classfile = 'productlot';
13030 $classname = 'Productlot';
13031 $element = 'productlot';
13032 $subelement = '';
13033 $table_element = 'product_lot';
13034 } elseif ($elementType == 'societeaccount') {
13035 $classpath = 'societe/class';
13036 $classfile = 'societeaccount';
13037 $classname = 'SocieteAccount';
13038 $module = 'societe';
13039 } elseif ($elementType == 'websitepage') {
13040 $classpath = 'website/class';
13041 $classfile = 'websitepage';
13042 $classname = 'Websitepage';
13043 $module = 'website';
13044 $subelement = 'websitepage';
13045 $table_element = 'website_page';
13046 } elseif ($elementType == 'fiscalyear') {
13047 $classpath = 'core/class';
13048 $module = 'accounting';
13049 $subelement = 'fiscalyear';
13050 } elseif ($elementType == 'chargesociales') {
13051 $classpath = 'compta/sociales/class';
13052 $module = 'tax';
13053 $table_element = 'chargesociales';
13054 } elseif ($elementType == 'tva') {
13055 $classpath = 'compta/tva/class';
13056 $module = 'tax';
13057 $subdir = '/vat';
13058 $table_element = 'tva';
13059 } elseif ($elementType == 'emailsenderprofile') {
13060 $module = '';
13061 $classpath = 'core/class';
13062 $classfile = 'emailsenderprofile';
13063 $classname = 'EmailSenderProfile';
13064 $table_element = 'c_email_senderprofile';
13065 $subelement = '';
13066 } elseif ($elementType == 'conferenceorboothattendee') {
13067 $classpath = 'eventorganization/class';
13068 $classfile = 'conferenceorboothattendee';
13069 $classname = 'ConferenceOrBoothAttendee';
13070 $module = 'eventorganization';
13071 } elseif ($elementType == 'conferenceorbooth') {
13072 $classpath = 'eventorganization/class';
13073 $classfile = 'conferenceorbooth';
13074 $classname = 'ConferenceOrBooth';
13075 $module = 'eventorganization';
13076 } elseif ($elementType == 'ccountry') {
13077 $module = '';
13078 $classpath = 'core/class';
13079 $classfile = 'ccountry';
13080 $classname = 'Ccountry';
13081 $table_element = 'c_country';
13082 $subelement = '';
13083 }
13084
13085 if (empty($classfile)) {
13086 $classfile = strtolower($subelement);
13087 }
13088 if (empty($classname)) {
13089 $classname = ucfirst($subelement);
13090 }
13091 if (empty($classpath)) {
13092 $classpath = $module.'/class';
13093 }
13094
13095 //print 'getElementProperties subdir='.$subdir;
13096
13097 // Set dir_output
13098 if ($module && isset($conf->$module)) { // The generic case
13099 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13100 $dir_output = $conf->$module->multidir_output[$conf->entity];
13101 } elseif (!empty($conf->$module->output[$conf->entity])) {
13102 $dir_output = $conf->$module->output[$conf->entity];
13103 } elseif (!empty($conf->$module->dir_output)) {
13104 $dir_output = $conf->$module->dir_output;
13105 }
13106 }
13107
13108 // Overwrite value for special cases
13109 if ($element == 'order_supplier') {
13110 $dir_output = $conf->fournisseur->commande->dir_output;
13111 } elseif ($element == 'invoice_supplier') {
13112 $dir_output = $conf->fournisseur->facture->dir_output;
13113 }
13114 $dir_output .= $subdir;
13115
13116 $elementProperties = array(
13117 'module' => $module,
13118 'element' => $element,
13119 'table_element' => $table_element,
13120 'subelement' => $subelement,
13121 'classpath' => $classpath,
13122 'classfile' => $classfile,
13123 'classname' => $classname,
13124 'dir_output' => $dir_output
13125 );
13126
13127
13128 // Add hook
13129 if (!is_object($hookmanager)) {
13130 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13131 $hookmanager = new HookManager($db);
13132 }
13133 $hookmanager->initHooks(array('elementproperties'));
13134
13135
13136 // Hook params
13137 $parameters = array(
13138 'elementType' => $elementType,
13139 'elementProperties' => $elementProperties
13140 );
13141
13142 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13143
13144 if ($reshook) {
13145 $elementProperties = $hookmanager->resArray;
13146 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13147 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13148 }
13149
13150 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13151 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13152 unset($hookmanager->contextarray[$key]);
13153 }
13154
13155 return $elementProperties;
13156}
13157
13170function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13171{
13172 global $db, $conf;
13173
13174 $ret = 0;
13175
13176 $element_prop = getElementProperties($element_type);
13177
13178 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13179 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13180 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13181 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13182 // of service and we will return properties of a product.
13183 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13184 } elseif ($element_prop['module'] == 'societeaccount') {
13185 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13186 } else {
13187 $ismodenabled = isModEnabled($element_prop['module']);
13188 }
13189 //var_dump('element_type='.$element_type);
13190 //var_dump($element_prop);
13191 //var_dump($element_prop['module'].' '.$ismodenabled);
13192 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13193 if ($useCache === 1
13194 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13195 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13196 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13197 ) {
13198 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13199 }
13200
13201 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13202
13203 if (class_exists($element_prop['classname'])) {
13204 $className = $element_prop['classname'];
13205 $objecttmp = new $className($db);
13206 '@phan-var-force CommonObject $objecttmp';
13207
13208 if ($element_id > 0 || !empty($element_ref)) {
13209 $ret = $objecttmp->fetch($element_id, $element_ref);
13210 if ($ret >= 0) {
13211 if (empty($objecttmp->module)) {
13212 $objecttmp->module = $element_prop['module'];
13213 }
13214
13215 if ($useCache > 0) {
13216 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13217 $conf->cache['fetchObjectByElement'][$element_type] = [];
13218 }
13219
13220 // Manage cache limit
13221 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13222 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13223 }
13224
13225 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13226 }
13227
13228 return $objecttmp;
13229 }
13230 } else {
13231 return $objecttmp; // returned an object without fetch
13232 }
13233 } else {
13234 return -1;
13235 }
13236 }
13237
13238 return $ret;
13239}
13240
13247function isAFileWithExecutableContent($filename)
13248{
13249 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)) {
13250 return true;
13251 }
13252
13253 return false;
13254}
13255
13263function newToken()
13264{
13265 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13266}
13267
13275function currentToken()
13276{
13277 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13278}
13279
13285function getNonce()
13286{
13287 global $conf;
13288
13289 if (empty($conf->cache['nonce'])) {
13290 $conf->cache['nonce'] = dolGetRandomBytes(8);
13291 }
13292
13293 return $conf->cache['nonce'];
13294}
13295
13296
13310function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13311{
13312 global $langs;
13313
13314 print '<div class="div-table-responsive-no-min">';
13315 print '<table class="noborder centpercent">';
13316 print '<tr class="liste_titre">';
13317
13318 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13319
13320 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13321
13322 if (!empty($link)) {
13323 if (!empty($arguments)) {
13324 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13325 } else {
13326 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13327 }
13328 }
13329
13330 if ($number > -1) {
13331 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13332 } elseif (!empty($link)) {
13333 print '<span class="badge marginleftonlyshort">...</span>';
13334 }
13335
13336 if (!empty($link)) {
13337 print '</a>';
13338 }
13339
13340 print '</th>';
13341
13342 if ($number < 0 && !empty($link)) {
13343 print '<th class="right">';
13344 print '</th>';
13345 }
13346
13347 print '</tr>';
13348}
13349
13358function finishSimpleTable($addLineBreak = false)
13359{
13360 print '</table>';
13361 print '</div>';
13362
13363 if ($addLineBreak) {
13364 print '<br>';
13365 }
13366}
13367
13379function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13380{
13381 global $langs;
13382
13383 if ($num === 0) {
13384 print '<tr class="oddeven">';
13385 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13386 print '</tr>';
13387 return;
13388 }
13389
13390 if ($nbofloop === 0) {
13391 // don't show a summary line
13392 return;
13393 }
13394
13395 if ($num === 0) {
13396 $colspan = $tableColumnCount;
13397 } elseif ($num > $nbofloop) {
13398 $colspan = $tableColumnCount;
13399 } else {
13400 $colspan = $tableColumnCount - 1;
13401 }
13402
13403 if ($extraRightColumn) {
13404 $colspan--;
13405 }
13406
13407 print '<tr class="liste_total">';
13408
13409 if ($nbofloop > 0 && $num > $nbofloop) {
13410 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13411 } else {
13412 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13413 print '<td class="right centpercent">'.price($total).'</td>';
13414 }
13415
13416 if ($extraRightColumn) {
13417 print '<td></td>';
13418 }
13419
13420 print '</tr>';
13421}
13422
13431function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13432{
13433 if ($method == -1) {
13434 $method = 0;
13435 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13436 $method = 1;
13437 }
13438 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13439 $method = 2;
13440 }
13441 }
13442
13443 // Be sure we don't have output buffering enabled to have readfile working correctly
13444 while (ob_get_level()) {
13445 ob_end_flush();
13446 }
13447
13448 // Solution 0
13449 if ($method == 0) {
13450 readfile($fullpath_original_file_osencoded);
13451 } elseif ($method == 1) {
13452 // Solution 1
13453 $handle = fopen($fullpath_original_file_osencoded, "rb");
13454 while (!feof($handle)) {
13455 print fread($handle, 8192);
13456 }
13457 fclose($handle);
13458 } elseif ($method == 2) {
13459 // Solution 2
13460 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13461 $handle2 = fopen("php://output", "wb");
13462 stream_copy_to_stream($handle1, $handle2);
13463 fclose($handle1);
13464 fclose($handle2);
13465 }
13466}
13467
13477function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13478{
13479 /*
13480 global $conf;
13481
13482 if (!empty($conf->dol_no_mouse_hover)) {
13483 $showonlyonhover = 0;
13484 }*/
13485
13486 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13487 if ($texttoshow === 'none') {
13488 $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>';
13489 } elseif ($texttoshow) {
13490 $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>';
13491 } else {
13492 $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>';
13493 }
13494
13495 return $result;
13496}
13497
13498
13505function jsonOrUnserialize($stringtodecode)
13506{
13507 $result = json_decode($stringtodecode);
13508 if ($result === null) {
13509 $result = unserialize($stringtodecode);
13510 }
13511
13512 return $result;
13513}
13514
13515
13532function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13533{
13534 global $db, $user;
13535
13536 if (is_null($filter) || !is_string($filter) || $filter === '') {
13537 return '';
13538 }
13539 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13540 $filter = '(' . $filter . ')';
13541 }
13542
13543 $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'
13544 $firstandlastparenthesis = 0;
13545
13546 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13547 if ($noerror) {
13548 return '1 = 2';
13549 } else {
13550 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13551 }
13552 }
13553
13554 // Test the filter syntax
13555 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13556 $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
13557 // If the string result contains something else than '()', the syntax was wrong
13558
13559 if (preg_match('/[^\‍(\‍)]/', $t)) {
13560 $tmperrorstr = 'Bad syntax of the search string';
13561 $errorstr = 'Bad syntax of the search string: '.$filter;
13562 if ($noerror) {
13563 return '1 = 2';
13564 } else {
13565 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
13566 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13567 }
13568 }
13569
13570 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
13571
13572 if (is_object($db)) {
13573 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
13574 }
13575 if (is_object($user)) {
13576 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13577 }
13578
13579 return $ret;
13580}
13581
13589function dolForgeExplodeAnd($sqlfilters)
13590{
13591 $arrayofandtags = array();
13592 $nbofchars = dol_strlen($sqlfilters);
13593
13594 $error = '';
13595 $parenthesislevel = 0;
13596 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13597 if (!$result) {
13598 return array();
13599 }
13600 if ($parenthesislevel >= 1) {
13601 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13602 }
13603
13604 $i = 0;
13605 $s = '';
13606 $countparenthesis = 0;
13607 while ($i < $nbofchars) {
13608 $char = dol_substr($sqlfilters, $i, 1);
13609
13610 if ($char == '(') {
13611 $countparenthesis++;
13612 } elseif ($char == ')') {
13613 $countparenthesis--;
13614 }
13615
13616 if ($countparenthesis == 0) {
13617 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13618 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13619 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13620 // We found a AND
13621 $s = trim($s);
13622 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13623 $s = '('.$s.')';
13624 }
13625 $arrayofandtags[] = $s;
13626 $s = '';
13627 $i += 2;
13628 } else {
13629 $s .= $char;
13630 }
13631 } else {
13632 $s .= $char;
13633 }
13634 $i++;
13635 }
13636 if ($s) {
13637 $s = trim($s);
13638 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13639 $s = '('.$s.')';
13640 }
13641 $arrayofandtags[] = $s;
13642 }
13643
13644 return $arrayofandtags;
13645}
13646
13656function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13657{
13658 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13659 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13660 $tmp = $sqlfilters;
13661
13662 $nb = dol_strlen($tmp);
13663 $counter = 0;
13664 $parenthesislevel = 0;
13665
13666 $error = '';
13667
13668 $i = 0;
13669 while ($i < $nb) {
13670 $char = dol_substr($tmp, $i, 1);
13671
13672 if ($char == '(') {
13673 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13674 // We open a parenthesis and it is the first char
13675 $parenthesislevel++;
13676 }
13677 $counter++;
13678 } elseif ($char == ')') {
13679 $nbcharremaining = ($nb - $i - 1);
13680 if ($nbcharremaining >= $counter) {
13681 $parenthesislevel = min($parenthesislevel, $counter - 1);
13682 }
13683 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13684 $parenthesislevel = $counter;
13685 }
13686 $counter--;
13687 }
13688
13689 if ($counter < 0) {
13690 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13691 $parenthesislevel = 0;
13692 dol_syslog($error, LOG_WARNING);
13693 return false;
13694 }
13695
13696 $i++;
13697 }
13698
13699 if ($counter > 0) {
13700 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13701 $parenthesislevel = 0;
13702 dol_syslog($error, LOG_WARNING);
13703 return false;
13704 }
13705
13706 return true;
13707}
13708
13716function dolForgeDummyCriteriaCallback($matches)
13717{
13718 //dol_syslog("Convert matches ".$matches[1]);
13719 if (empty($matches[1])) {
13720 return '';
13721 }
13722 $tmp = explode(':', $matches[1]);
13723 if (count($tmp) < 3) {
13724 return '';
13725 }
13726
13727 return '()'; // An empty criteria
13728}
13729
13738function dolForgeCriteriaCallback($matches)
13739{
13740 global $db;
13741
13742 //dol_syslog("Convert matches ".$matches[1]);
13743 if (empty($matches[1])) {
13744 return '';
13745 }
13746 $tmp = explode(':', $matches[1], 3);
13747 if (count($tmp) < 3) {
13748 return '';
13749 }
13750
13751 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
13752
13753 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
13754
13755 $realOperator = [
13756 'NOTLIKE' => 'NOT LIKE',
13757 'ISNOT' => 'IS NOT',
13758 'NOTIN' => 'NOT IN',
13759 '!=' => '<>',
13760 ];
13761
13762 if (array_key_exists($operator, $realOperator)) {
13763 $operator = $realOperator[$operator];
13764 }
13765
13766 $tmpescaped = $tmp[2];
13767
13768 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
13769
13770 $regbis = array();
13771
13772 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
13773 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
13774 $tmpescaped2 = '(';
13775 // Explode and sanitize each element in list
13776 $tmpelemarray = explode(',', $tmpescaped);
13777 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
13778 $reg = array();
13779 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
13780 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
13781 } else {
13782 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
13783 }
13784 }
13785 $tmpescaped2 .= implode(',', $tmpelemarray);
13786 $tmpescaped2 .= ')';
13787
13788 $tmpescaped = $tmpescaped2;
13789 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
13790 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
13791 $tmpescaped = $regbis[1];
13792 }
13793 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
13794 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
13795 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
13796 // TODO Retrieve type of field for $operand field name.
13797 // So we can complete format. For example we could complete a year with month and day.
13798 $tmpescaped = "'".$db->escape($regbis[1])."'";
13799 } else {
13800 if (strtoupper($tmpescaped) == 'NULL') {
13801 $tmpescaped = 'NULL';
13802 } elseif (is_int($tmpescaped)) {
13803 $tmpescaped = (int) $tmpescaped;
13804 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
13805 $tmpescaped = (float) $tmpescaped;
13806 } else {
13807 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
13808 }
13809 }
13810
13811 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
13812}
13813
13814
13824function getTimelineIcon($actionstatic, &$histo, $key)
13825{
13826 global $langs;
13827
13828 $out = '<!-- timeline icon -->'."\n";
13829 $iconClass = 'fa fa-comments';
13830 $img_picto = '';
13831 $colorClass = '';
13832 $pictoTitle = '';
13833
13834 if ($histo[$key]['percent'] == -1) {
13835 $colorClass = 'timeline-icon-not-applicble';
13836 $pictoTitle = $langs->trans('StatusNotApplicable');
13837 } elseif ($histo[$key]['percent'] == 0) {
13838 $colorClass = 'timeline-icon-todo';
13839 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
13840 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
13841 $colorClass = 'timeline-icon-in-progress';
13842 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
13843 } elseif ($histo[$key]['percent'] >= 100) {
13844 $colorClass = 'timeline-icon-done';
13845 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
13846 }
13847
13848 if ($actionstatic->code == 'AC_TICKET_CREATE') {
13849 $iconClass = 'fa fa-ticket';
13850 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
13851 $iconClass = 'fa fa-pencilxxx';
13852 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
13853 $iconClass = 'fa fa-comments';
13854 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
13855 $iconClass = 'fa fa-mask';
13856 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
13857 if ($actionstatic->type_picto) {
13858 $img_picto = img_picto('', $actionstatic->type_picto);
13859 } else {
13860 if ($actionstatic->type_code == 'AC_RDV') {
13861 $iconClass = 'fa fa-handshake';
13862 } elseif ($actionstatic->type_code == 'AC_TEL') {
13863 $iconClass = 'fa fa-phone';
13864 } elseif ($actionstatic->type_code == 'AC_FAX') {
13865 $iconClass = 'fa fa-fax';
13866 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
13867 $iconClass = 'fa fa-envelope';
13868 } elseif ($actionstatic->type_code == 'AC_INT') {
13869 $iconClass = 'fa fa-shipping-fast';
13870 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
13871 $iconClass = 'fa fa-robot';
13872 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
13873 $iconClass = 'fa fa-robot';
13874 }
13875 }
13876 }
13877
13878 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
13879 return $out;
13880}
13881
13889{
13890 global $conf, $db;
13891
13892 $documents = array();
13893
13894 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
13895 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
13896 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
13897 //$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
13898 $sql .= ' ORDER BY ecm.position ASC';
13899
13900 $resql = $db->query($sql);
13901 if ($resql) {
13902 if ($db->num_rows($resql)) {
13903 while ($obj = $db->fetch_object($resql)) {
13904 $documents[$obj->id] = $obj;
13905 }
13906 }
13907 }
13908
13909 return $documents;
13910}
13911
13912
13930function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
13931{
13932 global $user, $conf;
13933 global $form;
13934
13935 global $param, $massactionbutton;
13936
13937 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
13938
13939 // Check parameters
13940 if (!is_object($filterobj) && !is_object($objcon)) {
13941 dol_print_error(null, 'BadParameter');
13942 }
13943
13944 $histo = array();
13945 '@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';
13946
13947 $numaction = 0;
13948 $now = dol_now();
13949
13950 $sortfield_list = explode(',', $sortfield);
13951 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
13952 $sortfield_new_list = array();
13953 foreach ($sortfield_list as $sortfield_value) {
13954 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
13955 }
13956 $sortfield_new = implode(',', $sortfield_new_list);
13957
13958 $sql = null;
13959 $sql2 = null;
13960
13961 if (isModEnabled('agenda')) {
13962 // Search histo on actioncomm
13963 if (is_object($objcon) && $objcon->id > 0) {
13964 $sql = "SELECT DISTINCT a.id, a.label as label,";
13965 } else {
13966 $sql = "SELECT a.id, a.label as label,";
13967 }
13968 $sql .= " a.datep as dp,";
13969 $sql .= " a.note as message,";
13970 $sql .= " a.datep2 as dp2,";
13971 $sql .= " a.percent as percent, 'action' as type,";
13972 $sql .= " a.fk_element, a.elementtype,";
13973 $sql .= " a.fk_contact,";
13974 $sql .= " a.email_from as msg_from,";
13975 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
13976 $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";
13977 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13978 $sql .= ", sp.lastname, sp.firstname";
13979 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13980 $sql .= ", m.lastname, m.firstname";
13981 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13982 $sql .= ", o.ref";
13983 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13984 $sql .= ", o.ref";
13985 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13986 $sql .= ", o.ref";
13987 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13988 $sql .= ", o.ref";
13989 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13990 $sql .= ", o.ref";
13991 }
13992 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
13993 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
13994 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
13995
13996 $force_filter_contact = $filterobj instanceof User;
13997
13998 if (is_object($objcon) && $objcon->id > 0) {
13999 $force_filter_contact = true;
14000 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
14001 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
14002 }
14003
14004 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14005 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
14006 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
14007 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
14008 $sql .= " ON er.resource_type = 'dolresource'";
14009 $sql .= " AND er.element_id = a.id";
14010 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
14011 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14012 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
14013 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14014 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
14015 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14016 $sql .= ", ".MAIN_DB_PREFIX."product as o";
14017 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14018 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
14019 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14020 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
14021 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14022 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
14023 }
14024
14025 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
14026 if (!$force_filter_contact) {
14027 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
14028 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
14029 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
14030 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
14031 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14032 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
14033 if ($filterobj->id) {
14034 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14035 }
14036 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14037 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
14038 if ($filterobj->id) {
14039 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14040 }
14041 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14042 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
14043 if ($filterobj->id) {
14044 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14045 }
14046 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14047 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
14048 if ($filterobj->id) {
14049 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14050 }
14051 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14052 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
14053 if ($filterobj->id) {
14054 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14055 }
14056 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14057 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
14058 if ($filterobj->id) {
14059 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14060 }
14061 }
14062 } else {
14063 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
14064 }
14065
14066 // Condition on actioncode
14067 if (!empty($actioncode)) {
14068 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14069 if ($actioncode == 'AC_NON_AUTO') {
14070 $sql .= " AND c.type != 'systemauto'";
14071 } elseif ($actioncode == 'AC_ALL_AUTO') {
14072 $sql .= " AND c.type = 'systemauto'";
14073 } else {
14074 if ($actioncode == 'AC_OTH') {
14075 $sql .= " AND c.type != 'systemauto'";
14076 } elseif ($actioncode == 'AC_OTH_AUTO') {
14077 $sql .= " AND c.type = 'systemauto'";
14078 }
14079 }
14080 } else {
14081 if ($actioncode == 'AC_NON_AUTO') {
14082 $sql .= " AND c.type != 'systemauto'";
14083 } elseif ($actioncode == 'AC_ALL_AUTO') {
14084 $sql .= " AND c.type = 'systemauto'";
14085 } else {
14086 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14087 }
14088 }
14089 }
14090 if ($donetodo == 'todo') {
14091 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14092 } elseif ($donetodo == 'done') {
14093 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14094 }
14095 if (is_array($filters) && $filters['search_agenda_label']) {
14096 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14097 }
14098 }
14099
14100 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14101 if (isModEnabled('mailing') && !empty($objcon->email)
14102 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14103 $langs->load("mails");
14104
14105 $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";
14106 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14107 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14108 $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
14109 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14110 $sql2 .= ", '' as lastname, '' as firstname";
14111 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14112 $sql2 .= ", '' as lastname, '' as firstname";
14113 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14114 $sql2 .= ", '' as ref";
14115 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14116 $sql2 .= ", '' as ref";
14117 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14118 $sql2 .= ", '' as ref";
14119 }
14120 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14121 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14122 $sql2 .= " AND mc.statut = 1";
14123 $sql2 .= " AND u.rowid = m.fk_user_valid";
14124 $sql2 .= " AND mc.fk_mailing=m.rowid";
14125 }
14126
14127 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14128 if (!empty($sql) && !empty($sql2)) {
14129 $sql = $sql." UNION ".$sql2;
14130 } elseif (empty($sql) && !empty($sql2)) {
14131 $sql = $sql2;
14132 }
14133
14134 //TODO Add navigation with this limits...
14135 $offset = 0;
14136 $limit = 1000;
14137
14138 // Complete request and execute it with limit
14139 $sql .= $db->order($sortfield_new, $sortorder);
14140 if ($limit) {
14141 $sql .= $db->plimit($limit + 1, $offset);
14142 }
14143
14144 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14145 $resql = $db->query($sql);
14146 if ($resql) {
14147 $i = 0;
14148 $num = $db->num_rows($resql);
14149
14150 $imaxinloop = ($limit ? min($num, $limit) : $num);
14151 while ($i < $imaxinloop) {
14152 $obj = $db->fetch_object($resql);
14153
14154 if ($obj->type == 'action') {
14155 $contactaction = new ActionComm($db);
14156 $contactaction->id = $obj->id;
14157 $result = $contactaction->fetchResources();
14158 if ($result < 0) {
14159 dol_print_error($db);
14160 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14161 }
14162
14163 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14164 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14165 $tododone = '';
14166 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14167 $tododone = 'todo';
14168 }
14169
14170 $histo[$numaction] = array(
14171 'type' => $obj->type,
14172 'tododone' => $tododone,
14173 'id' => $obj->id,
14174 'datestart' => $db->jdate($obj->dp),
14175 'dateend' => $db->jdate($obj->dp2),
14176 'note' => $obj->label,
14177 'message' => dol_htmlentitiesbr($obj->message),
14178 'percent' => $obj->percent,
14179
14180 'userid' => $obj->user_id,
14181 'login' => $obj->user_login,
14182 'userfirstname' => $obj->user_firstname,
14183 'userlastname' => $obj->user_lastname,
14184 'userphoto' => $obj->user_photo,
14185 'msg_from' => $obj->msg_from,
14186
14187 'contact_id' => $obj->fk_contact,
14188 'socpeopleassigned' => $contactaction->socpeopleassigned,
14189 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14190 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14191 'fk_element' => $obj->fk_element,
14192 'elementtype' => $obj->elementtype,
14193 // Type of event
14194 'acode' => $obj->acode,
14195 'alabel' => $obj->alabel,
14196 'libelle' => $obj->alabel, // deprecated
14197 'apicto' => $obj->apicto
14198 );
14199 } else {
14200 $histo[$numaction] = array(
14201 'type' => $obj->type,
14202 'tododone' => 'done',
14203 'id' => $obj->id,
14204 'datestart' => $db->jdate($obj->dp),
14205 'dateend' => $db->jdate($obj->dp2),
14206 'note' => $obj->label,
14207 'message' => dol_htmlentitiesbr($obj->message),
14208 'percent' => $obj->percent,
14209 'acode' => $obj->acode,
14210
14211 'userid' => $obj->user_id,
14212 'login' => $obj->user_login,
14213 'userfirstname' => $obj->user_firstname,
14214 'userlastname' => $obj->user_lastname,
14215 'userphoto' => $obj->user_photo
14216 );
14217 }
14218
14219 $numaction++;
14220 $i++;
14221 }
14222 } else {
14223 dol_print_error($db);
14224 }
14225 }
14226
14227 // Set $out to show events
14228 $out = '';
14229
14230 if (!isModEnabled('agenda')) {
14231 $langs->loadLangs(array("admin", "errors"));
14232 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14233 }
14234
14235 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14236 $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
14237
14238 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14239 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14240 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14241 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14242
14243 $formactions = new FormActions($db);
14244
14245 $actionstatic = new ActionComm($db);
14246 $userstatic = new User($db);
14247 $contactstatic = new Contact($db);
14248 $userGetNomUrlCache = array();
14249 $contactGetNomUrlCache = array();
14250
14251 $out .= '<div class="filters-container" >';
14252 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14253 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14254
14255 if ($objcon && get_class($objcon) == 'Contact' &&
14256 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14257 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14258 } else {
14259 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14260 }
14261 if (($filterobj && get_class($filterobj) == 'Societe')) {
14262 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14263 } else {
14264 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14265 }
14266
14267 $out .= "\n";
14268
14269 $out .= '<div class="div-table-responsive-no-min">';
14270 $out .= '<table class="noborder borderbottom centpercent">';
14271
14272 $out .= '<tr class="liste_titre">';
14273
14274 // Action column
14275 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14276 $out .= '<th class="liste_titre width50 middle">';
14277 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14278 $out .= $searchpicto;
14279 $out .= '</th>';
14280 }
14281
14282 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
14283
14284 $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14285 if ($donetodo) {
14286 $out .= '<th class="liste_titre"></th>';
14287 }
14288 $out .= '<th class="liste_titre">';
14289 $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14290 //$out .= img_picto($langs->trans("Type"), 'type');
14291 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', !getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : -1, 0, 0, 1, 'minwidth200imp');
14292 $out .= '</th>';
14293 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14294 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14295 $out .= '</th>';
14296
14297 // Action column
14298 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14299 $out .= '<th class="liste_titre width50 middle">';
14300 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14301 $out .= $searchpicto;
14302 $out .= '</th>';
14303 }
14304
14305 $out .= '</tr>';
14306
14307
14308 $out .= '</table>';
14309
14310 $out .= '</form>';
14311 $out .= '</div>';
14312
14313 $out .= "\n";
14314
14315 $out .= '<ul class="timeline">';
14316
14317 if ($donetodo) {
14318 $tmp = '';
14319 if ($filterobj instanceof Societe) {
14320 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14321 }
14322 if ($filterobj instanceof User) {
14323 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14324 }
14325 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14326 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14327 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14328 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14329 if ($filterobj instanceof Societe) {
14330 $tmp .= '</a>';
14331 }
14332 if ($filterobj instanceof User) {
14333 $tmp .= '</a>';
14334 }
14335 $out .= getTitleFieldOfList($tmp);
14336 }
14337
14338 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14339 $caction = new CActionComm($db);
14340 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14341
14342 $actualCycleDate = false;
14343
14344 // Loop on each event to show it
14345 foreach ($histo as $key => $value) {
14346 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14347
14348 $actionstatic->type_picto = $histo[$key]['apicto'];
14349 $actionstatic->type_code = $histo[$key]['acode'];
14350
14351 $labeltype = $actionstatic->type_code;
14352 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14353 $labeltype = 'AC_OTH';
14354 }
14355 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14356 $labeltype = $langs->trans("Message");
14357 } else {
14358 if (!empty($arraylist[$labeltype])) {
14359 $labeltype = $arraylist[$labeltype];
14360 }
14361 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14362 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14363 }
14364 }
14365
14366 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14367
14368 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14369
14370 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14371 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14372 $out .= '<!-- timeline time label -->';
14373 $out .= '<li class="time-label">';
14374 $out .= '<span class="timeline-badge-date">';
14375 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14376 $out .= '</span>';
14377 $out .= '</li>';
14378 $out .= '<!-- /.timeline-label -->';
14379 }
14380
14381
14382 $out .= '<!-- timeline item -->'."\n";
14383 $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
14384
14385 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14386 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14387 //$out .= $timelineicon;
14388 //var_dump($timelineicon);
14389 $out .= $typeicon;
14390
14391 $out .= '<div class="timeline-item">'."\n";
14392
14393 $out .= '<span class="time timeline-header-action2">';
14394
14395 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14396 $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").' ';
14397 $out .= $histo[$key]['id'];
14398 $out .= '</a> ';
14399 } else {
14400 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14401 }
14402
14403 if ($user->hasRight('agenda', 'allactions', 'create') ||
14404 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14405 $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).'">';
14406 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14407 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14408 $out .= '</a>';
14409 }
14410
14411 $out .= '</span>';
14412
14413 // Date
14414 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14415 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14416 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14417 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14418 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14419 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14420 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14421 } else {
14422 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14423 }
14424 }
14425 $late = 0;
14426 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14427 $late = 1;
14428 }
14429 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14430 $late = 1;
14431 }
14432 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14433 $late = 1;
14434 }
14435 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14436 $late = 1;
14437 }
14438 if ($late) {
14439 $out .= img_warning($langs->trans("Late")).' ';
14440 }
14441 $out .= "</span></span>\n";
14442
14443 // Ref
14444 $out .= '<h3 class="timeline-header">';
14445
14446 // Author of event
14447 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14448 if ($histo[$key]['userid'] > 0) {
14449 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14450 $userstatic->fetch($histo[$key]['userid']);
14451 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14452 }
14453 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14454 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14455 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14456 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14457 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14458 } else {
14459 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14460 }
14461 }
14462 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14463 }
14464 $out .= '</div>';
14465
14466 // Title
14467 $out .= ' <div class="messaging-title inline-block">';
14468 //$out .= $actionstatic->getTypePicto();
14469 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14470 $out .= $labeltype.' - ';
14471 }
14472
14473 $libelle = '';
14474 if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14475 $out .= $langs->trans('TicketNewMessage');
14476 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14477 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14478 } elseif (isset($histo[$key]['type'])) {
14479 if ($histo[$key]['type'] == 'action') {
14480 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14481 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14482 $libelle = $histo[$key]['note'];
14483 $actionstatic->id = $histo[$key]['id'];
14484 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14485 } elseif ($histo[$key]['type'] == 'mailing') {
14486 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14487 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14488 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14489 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14490 } else {
14491 $libelle .= $histo[$key]['note'];
14492 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14493 }
14494 }
14495
14496 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14497 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14498 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14499 } else {
14500 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14501 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14502 }
14503 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14504 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14505 }
14506 if ($link) {
14507 $out .= ' - '.$link;
14508 }
14509 }
14510
14511 $out .= '</div>';
14512
14513 $out .= '</h3>';
14514
14515 // Message
14516 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14517 && $actionstatic->code != 'AC_TICKET_CREATE'
14518 && $actionstatic->code != 'AC_TICKET_MODIFY'
14519 ) {
14520 $out .= '<div class="timeline-body wordbreak small">';
14521 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14522 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14523 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14524 $out .= '<div class="readmore-block --closed" >';
14525 $out .= ' <div class="readmore-block__excerpt">';
14526 $out .= dolPrintHTML($truncatedText);
14527 $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>';
14528 $out .= ' </div>';
14529 $out .= ' <div class="readmore-block__full-text" >';
14530 $out .= dolPrintHTML($histo[$key]['message']);
14531 $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>';
14532 $out .= ' </div>';
14533 $out .= '</div>';
14534 } else {
14535 $out .= dolPrintHTML($histo[$key]['message']);
14536 }
14537
14538 $out .= '</div>';
14539 }
14540
14541 // Timeline footer
14542 $footer = '';
14543
14544 // Contact for this action
14545 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14546 $contactList = '';
14547 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14548 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14549 $contact = new Contact($db);
14550 $contact->fetch($cid);
14551 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14552 } else {
14553 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14554 }
14555
14556 if ($contact) {
14557 $contactList .= !empty($contactList) ? ', ' : '';
14558 $contactList .= $contact->getNomUrl(1);
14559 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14560 if (!empty($contact->phone_pro)) {
14561 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14562 }
14563 }
14564 }
14565 }
14566
14567 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14568 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14569 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14570 $contact = new Contact($db);
14571 $result = $contact->fetch($histo[$key]['contact_id']);
14572 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14573 } else {
14574 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14575 $result = ($contact instanceof Contact) ? $contact->id : 0;
14576 }
14577
14578 if ($result > 0) {
14579 $footer .= $contact->getNomUrl(1);
14580 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14581 if (!empty($contact->phone_pro)) {
14582 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14583 }
14584 }
14585 }
14586 }
14587
14588 $documents = getActionCommEcmList($actionstatic);
14589 if (!empty($documents)) {
14590 $footer .= '<div class="timeline-documents-container">';
14591 foreach ($documents as $doc) {
14592 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14593 $footer .= ' data-id="'.$doc->id.'" ';
14594 $footer .= ' data-path="'.$doc->filepath.'"';
14595 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14596 $footer .= '>';
14597
14598 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14599 $mime = dol_mimetype($filePath);
14600 $file = $actionstatic->id.'/'.$doc->filename;
14601 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14602 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14603 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14604
14605 $mimeAttr = ' mime="'.$mime.'" ';
14606 $class = '';
14607 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14608 $class .= ' documentpreview';
14609 }
14610
14611 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14612 $footer .= img_mime($filePath).' '.$doc->filename;
14613 $footer .= '</a>';
14614
14615 $footer .= '</span>';
14616 }
14617 $footer .= '</div>';
14618 }
14619
14620 if (!empty($footer)) {
14621 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14622 }
14623
14624 $out .= '</div>'."\n"; // end timeline-item
14625
14626 $out .= '</li>';
14627 $out .= '<!-- END timeline item -->';
14628 }
14629
14630 $out .= "</ul>\n";
14631
14632 $out .= '<script>
14633 jQuery(document).ready(function () {
14634 $(document).on("click", "[data-read-more-action]", function(e){
14635 let readMoreBloc = $(this).closest(".readmore-block");
14636 if(readMoreBloc.length > 0){
14637 e.preventDefault();
14638 if($(this).attr("data-read-more-action") == "close"){
14639 readMoreBloc.addClass("--closed").removeClass("--open");
14640 $("html, body").animate({
14641 scrollTop: readMoreBloc.offset().top - 200
14642 }, 100);
14643 }else{
14644 readMoreBloc.addClass("--open").removeClass("--closed");
14645 }
14646 }
14647 });
14648 });
14649 </script>';
14650
14651
14652 if (empty($histo)) {
14653 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14654 }
14655 }
14656
14657 if ($noprint) {
14658 return $out;
14659 } else {
14660 print $out;
14661 }
14662}
14663
14674function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
14675{
14676 $m = array();
14677 if ($hourTime === 'getpost') {
14678 $hour = GETPOSTINT($prefix . 'hour');
14679 $minute = GETPOSTINT($prefix . 'minute');
14680 $second = GETPOSTINT($prefix . 'second');
14681 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
14682 $hour = intval($m[1]);
14683 $minute = intval($m[2]);
14684 $second = intval($m[3]);
14685 } else {
14686 $hour = $minute = $second = 0;
14687 }
14688 // normalize out of range values
14689 $hour = min($hour, 23);
14690 $minute = min($minute, 59);
14691 $second = min($second, 59);
14692 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
14693}
14694
14706function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14707{
14708 if ($timestamp === null) {
14709 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14710 }
14711 $TParam = array(
14712 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14713 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14714 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14715 );
14716 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
14717 $TParam = array_merge($TParam, array(
14718 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
14719 $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
14720 $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
14721 ));
14722 }
14723
14724 return '&' . http_build_query($TParam);
14725}
14726
14746function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
14747{
14748 global $conf, $db, $langs, $hookmanager;
14749 global $action, $object;
14750
14751 if (!is_object($langs)) {
14752 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
14753 $langs = new Translate('', $conf);
14754 $langs->setDefaultLang();
14755 }
14756
14757 $langs->load("errors");
14758
14759 if ($printheader) {
14760 if (function_exists("llxHeader")) {
14761 llxHeader('');
14762 } elseif (function_exists("llxHeaderVierge")) {
14763 llxHeaderVierge('');
14764 }
14765 }
14766
14767 print '<div class="error">';
14768 if (empty($message)) {
14769 print $langs->trans("ErrorRecordNotFound");
14770 } else {
14771 print $langs->trans($message);
14772 }
14773 print '</div>';
14774 print '<br>';
14775
14776 if (empty($showonlymessage)) {
14777 if (empty($hookmanager)) {
14778 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14779 $hookmanager = new HookManager($db);
14780 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
14781 $hookmanager->initHooks(array('main'));
14782 }
14783
14784 $parameters = array('message' => $message, 'params' => $params);
14785 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14786 print $hookmanager->resPrint;
14787 }
14788
14789 if ($printfooter && function_exists("llxFooter")) {
14790 llxFooter();
14791 }
14792 exit(0);
14793}
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.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
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_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:2037